Azure DevOps YAML Pipeline CI/CD Setup

Goal

Build a single YAML pipeline that builds a Docker container, pushes it to Azure Container Registry, and deploys it sequentially to dev and int environments with approval gates.

Key Points

Pipeline Structure

Trigger on master branch push:

trigger:
- master

resources:
- repo: self

Variables - use BuildId as container tag:

variables:
  dockerRegistryServiceConnection: 'ra076bb1-83c4-4bff-9c7d-d2e1b7918498'
  imageRepository: 'bustroker.yamlpipelines.webapi'
  containerRegistry: 'bustrokeryamlpipelines.azurecr.io'
  dockerfilePath: '$(Build.SourcesDirectory)/Bustroker.YamlPipelines.WebApi/Dockerfile'
  tag: '$(Build.BuildId)'
  vmImageName: 'ubuntu-latest'

Stage 1: Build and Push

stages:
- stage: BuildStage
  displayName: Build
  jobs:  
  - job: Build
    displayName: Build and push
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: Docker@2
      displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerfile: $(dockerfilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: |
          $(tag)          

Stage 2: Deploy to Dev

- stage: DevDeployStage
  dependsOn: BuildStage
  displayName: Dev
  jobs:
  - deployment: DevDeploymentJob
    displayName: Deploy to dev
    environment: dev
    strategy: 
      runOnce:
        deploy:
          steps:
          - task: AzureWebAppContainer@1
            inputs:
              azureSubscription: 'AzDevOps.ServiceConnection'
              appName: 'bustroker-yamlpipelines-webapi-dev'
              containers: '$(containerRegistry)/$(imageRepository):$(tag)'
              appSettings: '-ASPNETCORE_ENVIRONMENT dev'

Stage 3: Deploy to Int

- stage: IntDeployStage
  dependsOn: DevDeployStage
  displayName: Int
  jobs:
  - deployment: IntDeploymentJob
    displayName: Deploy to int
    environment: int
    pool:
      vmImage: $(vmImageName)
    strategy: 
      runOnce:
        deploy:
          steps:
          - task: AzureWebAppContainer@1
            inputs:
              azureSubscription: 'AzDevOps.ServiceConnection'
              appName: 'bustroker-yamlpipelines-webapi-int'
              containers: '$(containerRegistry)/$(imageRepository):$(tag)'
              appSettings: '-ASPNETCORE_ENVIRONMENT int'

Complete Pipeline

## Docker
## Build and push an image to Azure Container Registry
## https://docs.microsoft.com/azure/devops/pipelines/languages/docker

trigger:
- master

resources:
- repo: self

variables:
  # Container registry service connection established during pipeline creation
  dockerRegistryServiceConnection: 'ra076bb1-83c4-4bff-9c7d-d2e1b7918498'
  imageRepository: 'bustroker.yamlpipelines.webapi'
  containerRegistry: 'bustrokeryamlpipelines.azurecr.io'
  dockerfilePath: '$(Build.SourcesDirectory)/Bustroker.YamlPipelines.WebApi/Dockerfile'
  tag: '$(Build.BuildId)'
  
  # Agent VM image name
  vmImageName: 'ubuntu-latest'

stages:
- stage: BuildStage
  displayName: Build
  jobs:  
  - job: Build
    displayName: Build and push
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: Docker@2
      displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerfile: $(dockerfilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: |
          $(tag)          

- stage: DevDeployStage
  dependsOn: BuildStage
  displayName: Dev
  jobs:
  - deployment: DevDeploymentJob
    displayName: Deploy to dev
    environment: dev
    strategy: 
      runOnce:
        deploy:
          steps:
          - task: AzureWebAppContainer@1
            inputs:
              azureSubscription: 'AzDevOps.ServiceConnection'
              appName: 'bustroker-yamlpipelines-webapi-dev'
              containers: '$(containerRegistry)/$(imageRepository):$(tag)'
              appSettings: '-ASPNETCORE_ENVIRONMENT dev'
              
- stage: IntDeployStage
  dependsOn: DevDeployStage
  displayName: Int
  jobs:
  - deployment: IntDeploymentJob
    displayName: Deploy to int
    environment: int
    pool:
      vmImage: $(vmImageName)
    strategy: 
      runOnce:
        deploy:
          steps:
          - task: AzureWebAppContainer@1
            inputs:
              azureSubscription: 'AzDevOps.ServiceConnection'
              appName: 'bustroker-yamlpipelines-webapi-int'
              containers: '$(containerRegistry)/$(imageRepository):$(tag)'
              appSettings: '-ASPNETCORE_ENVIRONMENT int'