Search code examples
azure-devopsyamlazure-pipelinespipeline

Azure Devops Yaml pipeline condition not met when variable comes from Library


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?


Solution

  • 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
    

    Image

    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')
    
    

    Image

    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')