YAML pipelines do not support pre- and post-deploy approvals that can be dynamically added. I’ve found a way of partially achieving this, see final update to my previous SO question. In summary; I created a YAML template that has 3 jobs; the first and last jobs are conditional jobs that use a ManualValidation@0
depending on a variable in a variable group, and the second job is the actual deployment of a web application.
The next step is to make this approach a more general by introducing a YAML template that takes a jobList
as a parameter and conditionally inserts a pre-deploy and post-deploy job. This template looks like
# File: deploy.yml
parameters:
- name: preDeployApprovers
type: string
default: ''
- name: postDeployApprovers
type: string
default: ''
- name: deployJobs
type: jobList
default: []
jobs:
- ${{ if ne(parameters.preDeployApprovers, '')}}:
- template: wait-for-manual-approval.yml
parameters:
jobName: AwaitPreDeployApproval
approvers: ${{parameters.preDeployApprovers}}
instructions: Approve or reject.
dependsOn:
# Inspired by
# /questions/63256692/is-it-possible-to-pass-a-template-with-a-list-of-jobs-to-a-joblist-type-param
- ${{ each job in parameters.deployJobs }}:
- ${{ each pair in job }}:
${{ if ne(pair.key, 'steps') }}:
${{ pair.key }}: ${{ pair.value }}
${{ if ne(parameters.preDeployApprovers, '')}}:
dependsOn: [AwaitPreDeployApproval] # POI 1
steps:
- ${{ job.steps }}
- ${{ if ne(parameters.postDeployApprovers, '')}}:
- template: wait-for-manual-approval.yml
parameters:
jobName: AwaitPostDeployApproval
approvers: ${{parameters.postDeployApprovers}}
instructions: Approve or reject.
dependsOn: ${{join(',',parameters.deployJobs)}} # POI 2. Current solution does NOT work
which references wait-for-manual-approval.yml:
#File: wait-for-manual-approval.yml
parameters:
- name: jobName
type: string
- name: approvers
type: string
default: ''
- name: instructions
type: string
default: 'Approve or reject'
- name: dependsOn
type: object
default: []
jobs:
- job: ${{parameters.jobName}}
${{ if parameters.dependsOn }}:
dependsOn: ${{ parameters.dependsOn }}
displayName: Wait for manual approval after deployment
pool: server
timeoutInMinutes: 240 #4 hours.
steps:
- task: ManualValidation@0
condition: ne(variables['${{parameters.approvers}}'], '')
inputs:
notifyUsers: |
$(${{parameters.approvers}})
instructions: ${{parameters.instructions}}
onTimeout: reject
The points of interest (POI) are
- POI 1: The pre-deploy approval will, depending on the parameter
preDeployApprovers
, be added to the expanded template. If this parameter has a non-default value, the actual deployment jobs must wait for the pre-deploy approval (‘AwaitPreDeployApproval’) to complete, hence there is a conditionaldependsOn
. This approach is based on the answer to a different SO question. - POI 2: The post-deploy approval must wait for all other jobs.
The way of providing dependsOn
as a parameter to the wait-for-manual-approval.yml is heavily inspired by this post.
Point of interest 2 is the source of my headache, it does not work. I’ve tried hard-coding the name of one of the jobs and that works, but for such a template to be useful, I need a way of extracting the names of the jobs that are in the deployJobs
parameters. Any suggestions?