I wanted to test adding approvals to an Azure DevOps YAML pipeline, outside of Environment approvals.
I created a project that contains the following files:
deploy.bicep– Bicep template that will create a storage account in Azure.validate.ps1– PowerShell script that mocks a validation stage, could be awhat-ifor some other validation.deploy.ps1– PowerShell script to deploy the Bicep template to Azure.deploy.yml– YAML pipeline file.
The repository is located at mattruma/MJR129 (github.com).
I have two branches: main and develop.
I have three environments: develop, staging and production.
The PowerShell scripts expect a -Prefix parameter, which will be different for each environment.
I created three variable groups, one for each environment.

I then added a Prefix variable to each variable group.

For develop the Prefix used dev as the suffix, e.g. mjr129dev, for staging used stg and for production used prd.
I next created three environments in Azure DevOps.

I added a gated approval to the Staging environment.
The Production environment did not need a gated approval as it would be triggered off of a commit to the main branch.
I wanted my workflow to work as such:
- Code changes are committed to the
developbranch which would then trigger the pipeline at theDevelopstage. - The pipeline runs the
validate.ps1script. - After the validation runs an email is sent requesting approval.
- The user approves the request which then runs the
deploy.ps1script, which in turn deploys the resources to thedevelopenvironment in Azure. - Using the gated approval, a notification is sent for approval to
Staging. - User approves the request which then runs the
validate.ps1script. - After the validation runs an email is sent requesting approval.
- The user approves the request which then runs the
deploy.ps1script, which in turn deploys the resources tostagingenvironment Azure. - If everything looks good, a pull request is created for the changes in
develop. - When the pull request is accepted the pipeline is triggered at the
Productionstage. - The pipeline runs the
validate.ps1script. - After the validation runs an email is sent requesting approval.
- The user approves the request which then runs the
deploy.ps1script, which in turn deploys the resources to theproductionenvironment in Azure.
The Develop and Staging stages would be triggered by the develop branch, while the Production stage would be triggered by the main branch.
I was able to accomplish this a combination of AzureCLI@2 and ManualValidation@0 tasks.
The complete Azure YAML Pipeline can be seen at MJR129/deploy.yml at develop · mattruma/MJR129 (github.com).
Some things to call out.
I used conditions on my stage to determine what branch would trigger what stage.
stages:
- stage: Develop
condition: eq(variables['build.sourceBranch'], 'refs/heads/develop')
Variable groups were assigned to the appropriate stage.
variables:
- group: Production
Timeouts were required for the job and the ManualIntervention task.
- job: StagingWaitingApproval
dependsOn: StagingValidate
pool: server
timeoutInMinutes: 4320 # job times out in 3 days
steps:
- task: ManualValidation@0
displayName: StagingWaitingApproval
timeoutInMinutes: 1440 # task times out in 1 day
inputs:
notifyUsers: |
$(Email)
instructions: 'Please validate the build configuration and resume'
onTimeout: 'resume'
Develop and Staging deployments happen within the same pipeline run.

The Production deployment, since it is triggered off the main branch, happens in a different pipeline run.

Seems to get the job done!
If there is a better way or different way, please share!
Discover more from Matt Ruma
Subscribe to get the latest posts sent to your email.
