Approvals in environments with multi-stage YAML pipelines

Posted by

This week we are going to demonstrate how to setup manual approvals to our multi-stage YAML pipelines. This is achieved through the use of “environments” in Azure Pipelines.

Setting up Azure DevOps

We can add/create environments in two ways. The first is from the environments menu item in the Azure “Pipelines” menu. The second is from the YAML itself – the YAML will create an environment when deployed if it doesn’t already exist. (We saw this first hand when a wry, missing “(“, in our YAML caused us to make a $Dev environment on our first deployment.)

After an environment has been created, we can edit its name, description, security, and approvals, and checks. Let’s click on the approvals and add one to Dev.

In the Add check section, we will select “Approvals”. Note that the “evaluate artifact”, often known as “checks”, only work on artifact container images and aren’t relevant for this project.

We specify who is required to approve the environment. Since most of the time it’s just Sam, we let him approve his own runs, and check the “Allow approvers to approve their own runs”. This is critical for smaller teams, who don’t want to be waiting for one particular approver.

Setting up the YAML

Let’s dive into the YAML changes required to setup a manual approval.

  • We need to use a “deployment” job (in red), instead of the regular “job”
  • We need to specify the “environment” (in green)
  • We need to specify a deployment strategy (in blue). In all of our examples here we are going to use “runOnce”, which fits most small-medium deployments perfectly. If you have a really large deployment, check out the other “rolling” and “canary” strategies. The YAML below is a very simple example, our full deployment YAML can be viewed in our public repo.

We run a pipeline, and when it reaches the dev stage, it stops for approval.

When we click on the “Review” button, we can add comments, and approve or reject the deployment.


Today we’ve shown how to setup manual approvals with a multi-stage YAML pipelines. In general, these manual approvals should be used sparingly, as we don’t want to create too many waiting states in our workflow.



  1. Love your website by the way. Just wanted to point out a little error. The environment is circled (squared?) in green in the example, not blue. Unless I’m colour blind. Keep up the great work! Really enjoy your posts.


    1. Not directly, as far as I’m aware, there are only pre-deployment approvals in YAML. However, you could add another stage after a deployment with an approval to act as a post-deployment approval, before moving to the next stage. For example, QA stage deploys, and then there is a QAApproval stage, with a predeployment approval.


  2. Well, we had the same setup and someone approved production instead of QA, now we are left to wondering who approved it. We looked at Audit Log but it just says Azure service deployed but no log of who approved it? Is there a way we can find it?


  3. extremely useful! I’m just wondering if you can also set an auto cancel time(out). I don’t want my pipelines to stay on waiting for approval forever, they should time out after x time. Is that possible? Didn’t manage to find it anywhere.


  4. Great post! Are you manually creating an environment in Azure DevOps each time you deploy a new PR environment? I’ve tried to follow the code in repo around setting a variable as the prId, passing it into the template as a parameter and then using it as the environment on the deployment but all I get is Job DeployApp: Environment $(prUC) could not be found. The environment does not exist or has not been authorized for use.


  5. Hello Sam, Great post.. I have an issue.

    Before adding the approval process the script was working fine. It is a Terraform apply script

    But after adding the approver process like in this blog. The pipeline couldn’t find the working directory

    Here is my script

    – deployment: terraform_apply
    environment: ‘production’
    – task: TerraformInstaller@0
    displayName: ‘install’
    terraformVersion: ‘latest’
    – task: TerraformTaskV2@2
    displayName: ‘init’
    provider: ‘azurerm’
    command: ‘init’
    backendServiceArm: ‘${{ parameters.backendServiceArm }}’
    backendAzureRmResourceGroupName: ‘${{ parameters.backendAzureRmResourceGroupName }}’
    backendAzureRmStorageAccountName: ‘${{ parameters.backendAzureRmStorageAccountName }}’
    backendAzureRmContainerName: ‘${{ parameters.backendAzureRmContainerName }}’
    backendAzureRmKey: ‘${{ parameters.backendAzureRmKey }}’
    workingDirectory: ‘$(System.DefaultWorkingDirectory)/Terraform/’
    – task: TerraformTaskV2@2
    displayName: ‘apply’
    condition: and(succeeded(), eq(variables[‘Action’], ‘Apply’))
    provider: ‘azurerm’
    command: ‘apply’
    commandOptions: ‘-input=false -auto-approve -var-file=”../${{ parameters.environment }}/${{ parameters.environment }}.tfvars”‘
    environmentServiceNameAzureRM: ‘${{ parameters.backendServiceArm }}’
    workingDirectory: ‘$(System.DefaultWorkingDirectory)/Terraform/’


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s