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.
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.
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
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
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
For the azure-app-service-prod
environment I’ve added 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
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
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
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
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
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
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
The environment is identified in the UI as having been 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
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
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.