Manual approval in an Azure DevOps YAML-pipeline

Last week I was creating a YAML-pipeline in Azure DevOps to deploy some ARM Templates. Before deploying those changes to my production environment, I wanted to have a manual approval in place. Here’s how I did that.

Let’s first explore the pipeline before I added the production environment. Below you find the YAML definition of that pipeline. It consists of two stages, one to publish the templates and a second one that deploys the templates to the test environment.

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: 'PublishTemplates'
  displayName: 'PublishTemplates'
  jobs: 
    - job: 'PublishTemplates'
      steps:
      - task: CopyFiles@2
        displayName: 'Copy composing to: $(build.artifactstagingdirectory)'
        inputs:
          SourceFolder: infra-templates
          TargetFolder: '$(build.artifactstagingdirectory)/templates'
      - task: PublishBuildArtifacts@1
        displayName: 'Publish Artifact: drop'
- stage: 'Test'
  jobs:
  - job: 'DeployToTest'
    steps:
    - task: DownloadBuildArtifacts@0
      inputs:
        buildType: 'current'
        downloadType: 'single'
        artifactName: 'drop'
        downloadPath: '$(System.DefaultWorkingDirectory)'
    - task: AzureResourceManagerTemplateDeployment@3
      inputs:
        deploymentScope: 'Resource Group'
        azureResourceManagerConnection: '<subscription-connection-name>'
        subscriptionId: '<subscriptiont-id>'
        action: 'Create Or Update Resource Group'
        resourceGroupName: 'erwin-test'
        location: 'West Europe'
        templateLocation: 'Linked artifact'
        csmFile: '$(System.DefaultWorkingDirectory)/drop/templates/api.json'
        csmParametersFile: '$(System.DefaultWorkingDirectory)/drop/templates/api.test.json'
        deploymentMode: 'Incremental'

Add the production environment

To add an approval in a YAML-pipeline, one needs to add an environment in Azure DevOps. Navigate to ‘Pipelines’ –> ‘Environments’. There you click on ‘New Environment’, you will see the following form. Add a name and leave the Resource section set to None. Click ‘Create’. SnelStart

Now it’s time to add the approval rule. You do that by clicking on the three stacked dots in the upper right corner and click ‘Approvals and Checks’.

SnelStart

There you click on ‘Approvals’ and will be directed to the following screen. There you enter the name of the person or group that needs to approve this stage. SnelStart

Repeat the above steps for the test environment. I choose not to add an approval to that one since I want to deploy to my test environment on each change.

Add the environment to the pipeline

The last step in the process is to add the environments to the pipeline’s definition. You need to replace the ‘job’ with a ‘deployment’ definition. ‘deployment’ is a special type of job. So, replace ‘job’ with:

- deployment: 'DeployToTest'
    environment: 'infrastructure-test'
    strategy:
        runOnce:
          deploy:

As you see in this example, below the deployment, you get to specify the environment. Below that, you define the deployment strategy. Here you will find more information on those strategies. Now copy the test stage and rename all appropriate parameters to reflect production. The result for your pipeline should look like the template below.

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: 'PublishTemplates'
  displayName: 'PublishTemplates'
  jobs: 
    - job: 'PublishTemplates'
      steps:
      - task: CopyFiles@2
        displayName: 'Copy composing to: $(build.artifactstagingdirectory)'
        inputs:
          SourceFolder: infra-templates
          TargetFolder: '$(build.artifactstagingdirectory)/templates'
      - task: PublishBuildArtifacts@1
        displayName: 'Publish Artifact: drop'
- stage: 'Test'
  jobs:
  - deployment: 'DeployToTest'
    environment: 'infrastructure-test'
    strategy:
        runOnce:
          deploy:
            steps:
            - task: DownloadBuildArtifacts@0
              inputs:
                buildType: 'current'
                downloadType: 'single'
                artifactName: 'drop'
                downloadPath: '$(System.DefaultWorkingDirectory)'
            - task: AzureResourceManagerTemplateDeployment@3
              inputs:
                deploymentScope: 'Resource Group'
                azureResourceManagerConnection: '<subscription-connection-name>'
                subscriptionId: '<subscriptiont-id>'
                action: 'Create Or Update Resource Group'
                resourceGroupName: 'erwin-test'
                location: 'West Europe'
                templateLocation: 'Linked artifact'
                csmFile: '$(System.DefaultWorkingDirectory)/drop/templates/api.json'
                csmParametersFile: '$(System.DefaultWorkingDirectory)/drop/templates/api.test.json'
                deploymentMode: 'Incremental'
- stage: 'Production'
  jobs:
  - deployment: 'DeployToProduction'
    environment: 'infrastructure-production'
    strategy:
        runOnce:
          deploy:
            steps:
            - task: DownloadBuildArtifacts@0
              inputs:
                buildType: 'current'
                downloadType: 'single'
                artifactName: 'drop'
                downloadPath: '$(System.DefaultWorkingDirectory)'
            - task: AzureResourceManagerTemplateDeployment@3
              inputs:
                deploymentScope: 'Resource Group'
                azureResourceManagerConnection: '<subscription-connection-name>'
                subscriptionId: '<subscriptiont-id>'
                action: 'Create Or Update Resource Group'
                resourceGroupName: 'erwin-prod'
                location: 'West Europe'
                templateLocation: 'Linked artifact'
                csmFile: '$(System.DefaultWorkingDirectory)/drop/templates/api.json'
                csmParametersFile: '$(System.DefaultWorkingDirectory)/drop/templates/api.prod.json'
                deploymentMode: 'Incremental'