Pre-deployment approvals in Azure DevOps multistage pipelines

For users migrating from the “Classic” VSTS/Azure DevOps release experience, it is not entirely obvious how to set up what used to be known as Pre-deployment approvals as part of a multi-stage YAML pipeline.

Screen Capture of Pre-deployment Approval dialog in Classic Azure DevOps Release experience

Pre-deployment approvals in a classic release pipeline

.

The documentation about this is rather unclear, not least because it mixes together concepts from the “Classic” Release Management experience with concepts from the multi-stage YAML experience. There is a clue in the second of these links, which is that manual approvals can be defined on environments .

As the documentation says,

An environment is a collection of resources that can be targeted by deployments from a pipeline. Environments can include Kubernetes clusters, Azure web apps, virtual machines, databases. Typical examples of environment names are Dev, Test, QA, Staging, and Production.

but also

While environment at its core is a grouping of resources, the resources themselves represent actual deployment targets. The Kubernetes resource and virtual machine resource types are currently supported.

This doesn’t mean, however, that we can’t use this feature to manage deployments to any type of enviroment, since

It is possible to create an empty environment and reference it from deployment jobs. This will let you record the deployment history against the environment.

As noted, environments are referenced from deployment jobs, which are a type of pipeline job with a couple of additional features.

  • If we specify an environment for a deployment job, then we get deployment history for that environment recorded across pipelines
  • Deployment jobs allow us to specify deployment strategies, such as canary or rolling, as alternatives to the default runOnce.

For the purposes of this example I’ve created a simple containerised web app which can be deployed to a number of different Azure services. To simplify the pipeline a bit, this is pre-built and pushed to docker hub. The pipeline itself is in a public Azure DevOps project

Deploying to Azure App Service

Creating the environments

This is an example of the “empty” evironment scenario. I’ve used the UI to create two empty environments - meaning environments with no resources added - called azure-app-service-staging and azure-app-service-prod.

Creating an empty environment

Creating an empty environment

The UI tells us how to refer to the environment from a YAML pipeline.

The UI tells us how to refer to the environment from a YAML pipeline

The UI tells us how to refer to the environment from a YAML pipeline

Adding approvals

For the azure-app-service-prod environment I’ve added an approval:

Adding an approval

Adding an approval

As can be seen from this picture, “approvals” are just a special case of “checks”, which provide many options for pre-deployment validation.

It’s generally a good idea to add groups rather than individual users as approvers - in the case of groups only one member of each specified group needs to approve the deployment, but if we specify a list of individual users then all of these users will need to approve.

Adding an approval

Adding an approval

Using the environments

We refer to each environment from a deployment job in the multistage pipeline.

  - stage: deployAzureAppServiceDev
    jobs:
      - deployment: deployWebApp
        pool:
          vmImage: 'ubuntu-latest'
        environment: azure-app-service-staging
        strategy:
          runOnce:
            deploy:
              steps:
              - task: AzureCLI@2
                inputs:
                  azureSubscription: 'MyAzureServiceConnection'
                  scriptType: 'bash'
                  scriptLocation: 'inlineScript'
                  inlineScript: |
                    az group create --name ${{variables.resourceGroupName}} --location ${{variables.resourceGroupLocation}}
                    az appservice plan create --name devops-envs-demo-serviceplan --resource-group ${{variables.resourceGroupName}} --location ${{variables.resourceGroupLocation}} --is-linux --sku F1
                    az webapp create --name envs-demo-gcdotd-stg --plan devops-envs-demo-serviceplan --resource-group ${{variables.resourceGroupName}} --deployment-container-image-name busybox
              - task: AzureWebAppContainer@1
                inputs:
                  azureSubscription: 'MyAzureServiceConnection'
                  appName: 'envs-demo-gcdotd-stg'
                  deployToSlotOrASE: true
                  resourceGroupName: '${{variables.resourceGroupName}}'
                  slotName: 'production'
                  containers: 'docker.io/gcdotd/contained-trombone:latest'

The deployment job itself creates the resource group and app service plan for hosting the web app, then deploys our image to the web app. This pipeline re-uses the same app service plan for both environments.

When we run the pipeline, the staging environment is deployed immediately, but the production evironment waits for approval before proceeding.

The production enviroment requires an approval before deployment

The production enviroment requires an approval before deployment

Environment permissions

There is a wrinkle here which can often be confusing. As the creator - through the UI - of these environments, I’m able to select and deselect the corresponding stages at the point of running the pipeline. However, this is not necessarily the case for other users of the pipeline.

When I log in as “other developer”, I’m able to run the pipeline and deploy to all the environments, subject to the appropriate approval for the production environment.

Other developers can run the pipeline

Other developers can run the pipeline

However, the other developer is unable to select and deselect stages when initiating a pipeline run, and gets a slightly cryptic error message to the effect that “The environment does not exist or has not been authorized for use”.

Other developers cannot select individual stages

Other developers cannot select individual stages

This is because users other than the creator of the environment are assigned the “reader” role for the environment, which is insufficient to allow selection and deselection of pipeline stages involving the environment in question.

Default permissions for an environment created via the UI

Default permissions for an environment created via the UI

Since in this case the other developer is a Contributor to the project, it is sufficent to assign the “User” role to the Contrtibutors group to allow the user to select and deselect stages in the pipeline. It’s worth noting that other users must have appropriate permissions for all environments referenced in the pipeline to avoid the error message noted above.

Assign the User role to the Project Contributors group

Assign the User role to the Project Contributors group

Now the other developer can select stages

Now the other developer can select stages

More confusingly, if my original user creates an environment implicitly through the pipeline rather than via the UI:

  - stage: deployAzureAppServiceOtherEnv
    jobs:
      - deployment: deployWebApp
        pool:
          vmImage: 'ubuntu-latest'
        environment: env-created-implicitly-by-pipeline

The new environment appears in the UI before we have even run the pipeline.

Implicitly created environments automatically appear in the UI

Implicitly created environments automatically appear in the UI

The environment is identified in the UI as having been automatically created.

Implicitly created environments are identified as automatically created

Implicitly created environments are identified as automatically created

More importantly, the default permissions for implicitly created environments are different from the default permissions for environments created through the UI.

For implicitly created environments, project contributors are granted the Administrator role

For implicitly created environments, project contributors are granted the Administrator role

Accordingly, for these environments, no additional permissions need to be granted for our other developer to be able to select them in the portal.

Other Developers can see all the stages in the pipeline

Other Developers can see all the stages in the pipeline

This behaviour is hinted at in the documentation without much discussion of the resulting behaviour.

Note

If you create an environment within a YAML, contributors and project administrators will be granted Administrator role. This is typically used in provisioning Dev/Test environments. If you create an environment through the UI, only the creator will be granted the Administrator role. You should use the UI to create protected environments like for a production environment.

There’s quite a bit more to understand about this “environments” feature and it’s a topic I hope to return to in the future. This is certainly an area of the product that is evolving rapidly, which is presumably how the feature has managed to get ahead of its documentation.