Reputation: 1378
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
# https://stackoverflow.com/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
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 conditional dependsOn
. This approach is based on the answer to a different SO question.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. In the expanded template that I've downloaded there is no dependsOn
statement for the post-deploy job. 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?
Upvotes: 1
Views: 593
Reputation: 18084
As mentioned by @Alvin Zhao - MSFT, the dependsOn
property should be an array []
rather than a string if your job has multiple dependencies.
join function can't be used in this case because:
So instead of:
dependsOn: ${{join(',',parameters.deployJobs)}}
Try using a filtered array:
dependsOn: ${{ parameters.deployJobs.*.job }}
Explanation:
parameters.deployJobs.*.job
tells the system to operate on parameters.deployJobs
as a filtered array and then select the job
propertytrigger: none
parameters:
- name: deployJobs
type: jobList
default:
- job: A
displayName: Job A
steps:
- script: echo "Hello from Job A"
- job: B
displayName: Job B
steps:
- script: echo "Hello from Job B"
- job: C
displayName: Job C
steps:
- script: echo "Hello from Job C"
jobs:
- ${{ parameters.deployJobs }}
- job: D
displayName: Job D
dependsOn: ${{ parameters.deployJobs.*.job }}
steps:
- script: echo "Hello from Job D"
Upvotes: 3