In my Azure DevOps YAML pipeline, the deployment of a website has been coded in a reusable template. This template will execute a step depending on the value of an input paramter:
# Template 'deployTemplate.yml'
parameters:
- name: preDeployApprovalRequired
type: string
jobs:
- job: PrintVariables
steps:
- ${{if eq(parameters.preDeployApprovalRequired, 'true')}}:
- powershell: |
Write-Host "Pre deploy required: '${{parameters.preDeployApprovalRequired}}'"
- ${{else}}:
- powershell: |
Write-Host "Pre deploy NOT required: '${{parameters.preDeployApprovalRequired}}'"
and this template is called as follows
# Pipeline
# Beginning of pipeline is omitted, works as intended
- stage: Development
variables:
- group: Development #Reference to Library
jobs:
- template: deployTemplate.yml
parameters:
preDeployApprovalRequired: $(PreDeployApprovalRequired)
Here $(PreDeployApprovalRequired)
comes from the Library named 'Development', I've verified that the value is true
, and as can be deduced from the code above, the value will be printed in the Powershell task. When running the pipeline, I see in the output: Pre deploy NOT required: 'true'
. Why?? To make things even more puzzling, if I modify the last line in the pipeline to preDeployApprovalRequired: 'true'
or preDeployApprovalRequired: true
(no quotation marks around the word true
), i.e. the value is hard-coded in the pipeline and does not come from the Library, the output is Pre deploy required: 'true'
, as expected.
Any ideas on how I can conditionally execute a task based on the value in a Library?
-------------- Update --------------
The original question showed only a contrived example, what I actually want to achieve is conditionally invoke a ManualValidation@0
task. The motivation for using this task and why I really, really want the variable to come from a Library is described in this related SO question.Based on the suggestions so far, I've changed the code to the following:
# Template 'deployTemplate.yml'
parameters:
- name: preDeployApprovers
type: string
jobs:
- job: WaitForValidation
pool: server
variables:
approvers: ${{parameters.preDeployApprovers}}
steps:
- task: ManualValidation@0
condition: ne(variables['approvers'],'')
inputs:
notifyUsers: |
$(approvers)
onTimeout: reject
The good news is that the condition works, though I don't really understand why adding a separate variable approvers
works; I'd expect that it too expands parameters.preDeployApprovers
which is not known at compile time. The bad news is that the users are not notified. When I replace the macro syntax $(approvers)
with my hard-code emailaddress, I do get an email. Any ideas?
The issue is because the value of a variable from a variable group in Library is processed at runtime and we shouldn't use the contional insertions to evaluate such variables, which are using the template expression syntax getting processed at compile time, before runtime starts. See this to ducment to help us Understand variable syntax.
For this, you may consider defining a set of variables in a template variables.yml
instead of using a variable group; this option will expand one single powershell
task at compile time;
pipeline.yml
stages:
- stage: Development
variables:
# - group: Development # Reference to Library
- template: variables.yml # Reference to template variables.yml
jobs:
- template: deployTemplate.yml
parameters:
preDeployApprovalRequired: ${{variables.PreDeployApprovalRequired}} # Pass the vaule of the varialbe PreDeployApprovalRequired from variables.yml in TEMPLATE EXPRESSION syntax
variables.yml
# Template 'variables.yml'
variables:
PreDeployApprovalRequired : true
Or if you prefer using variable groups, you will need to opt to add the condition
property of a task/job/stage with runtime expressions instead of using conditional insertions; and we also need to define new variable(s) in the template to catch the parameter values to be passed in, as the template parameters can only be used in template expressions and the value of ${{parameters.preDeployApprovalRequired}}
is ''
at compile time; this on the other hand will expand two powershell
tasks at compile time but skip the one whose condition is evaluated as false
at runtime;
deployTemplate.yml
# Template 'deployTemplate.yml'
parameters:
- name: preDeployApprovalRequired
type: string
default: ''
jobs:
- job: PrintVariables
variables:
newVar: ${{parameters.preDeployApprovalRequired}}
steps:
# - ${{if eq(parameters.preDeployApprovalRequired, 'true')}}:
- powershell: |
Write-Host "Pre deploy required: $(newVar)"
condition: eq(variables['newVar'], 'true')
# - ${{else}}:
- powershell: |
Write-Host "Pre deploy NOT required: $(newVar)"
condition: eq(variables['newVar'], 'false')
If applicable, you can directly evaluate the variable $(PreDeployApprovalRequired)
from variable group Deployment
in runtime expressions within the condition
property, without having to pass and retrieve the value from a template parameter.
# Template 'deployTemplate.yml'
parameters:
- name: preDeployApprovalRequired
type: string
default: ''
jobs:
- job: PrintVariables
steps:
- powershell: |
Write-Host "Pre deploy required: $(PreDeployApprovalRequired)"
condition: eq(variables['PreDeployApprovalRequired'], 'true')
- powershell: |
Write-Host "Pre deploy NOT required: $(PreDeployApprovalRequired)"
condition: eq(variables['PreDeployApprovalRequired'], 'false')