azure-devopsazure-pipelines-yaml

Azure DevOps Yaml - how to set global variables from parameters?


We have pipelines that are mostly started manually, and the user can set a various options:

enter image description here

The above parameters are defined in the top level YAML, like this:

parameters:
  - name: reasonForManualBuild
    displayName: Reason for manual run
    type: string
    default: ''

  - name: cleanAgents
    displayName: Wipe agents before build
    type: boolean
    default: false
    values:
    - false
    - true

  - name: buildSoftware
    displayName: Build software
    type: boolean
    default: true
    values:
    - false
    - true

  - name: runAnalysis
    displayName: Run static code analysis
    type: boolean
    default: true
    values:
    - false
    - true

This works very well, but, at the moment, I'm passing most of the parameters to templates that call templates with the same parameters etc. Not very nice.

It would be great if I could define global variables from these parameters, like this:

variables:
  - name: cleanAgents
    value: ${{ parameters.cleanAgents }}

  - name: buildSoftware
    value: ${{ parameters.buildSoftware }}

and then use them within the templates, as $(cleanagents), $(buildSoftware), etc.

As far as I understand the documentation, this should work, but it doesn't, when I run the pipeline, they appear empty.

As an alternative I tried passing parameter objects, which doesn't seem to work either.

Am I making a mistake, or is there a correct way to do it, or is it just not possible?

The main product pipeline, where I'm just calling the product "Foo":

parameters:
  - name: reasonForManualBuild
    displayName: Reason for manual run
    type: string
    default: ''

  - name: cleanAgents
    displayName: Wipe agents before build
    type: boolean
    default: false
    values:
    - false
    - true

  - name: buildSoftware
    displayName: Build software
    type: boolean
    default: true
    values:
    - false
    - true

  - name: runAnalysis
    displayName: Run static code analysis
    type: boolean
    default: true
    values:
    - false
    - true

  - name: runUnitTests
    displayName: Run unit tests
    type: boolean
    default: true
    values:
    - false
    - true

  - name: runFunctionalTests
    displayName: Run functional tests
    type: boolean
    default: false
    values:
    - false
    - true

  - name: continueOnError
    displayName: Continue on error
    type: boolean
    default: false
    values:
    - false
    - true

  - name: productVersion
    displayName: Product version number
    type: string
    default: 5.0.0.1

name: QEC-${{ parameters.productVersion }}-$(Date:yyMMdd)$(Rev:rr)

variables:
  - name: productName
    value: FOO

  - name: productVersion
    value: ${{ parameters.productVersion }}

  - name: cleanAgents
    value: ${{ parameters.cleanAgents }}

  - name: buildSoftware
    value: ${{ parameters.buildSoftware }}

  - name: runAnalysis
    value: ${{ parameters.runAnalysis }}    

  - name: runUnitTests
    value: ${{ parameters.runUnitTests }} 

  - name: runFunctionalTests
    value: ${{ parameters.runFunctionalTests }} 
    
  - name: continueOnError
    value: ${{ parameters.continueOnError }} 

resources:
  repositories:
    # skipping resources block as not relevant here

extends:
  template: /BaseTest-pipeline.yml

The BaseTest-pipeline.yml:

stages:
  - stage: stage_Test
    displayName: "Build $(productName), no analysis" 
    jobs:
    - job: jobShow
      displayName: 'Job with $(productName)' 
      steps:
      - script: |
          echo "productName    = $(productName)"
          echo "productVersion = $(productVersion)"
          echo "cleanAgents    = $(cleanAgents)"

shows, as displayname: Job with $(productName)

But then correctly shows three variables, including productName, correctly:

"productName    = FOO"
"productVersion = 5.0.0.1"
"cleanAgents    = False"

Solution

  • EDIT:

    I attempted on my side, and it can work fine to use the parameters to set global variables. See below example as reference.

    1. The YAML files.

      enter image description here

      • The file azure-pipelines.yml

        # azure-pipelines.yml
        
        parameters:
        - name: param01
          type: boolean
          default: true
        - name: param02
          type: boolean
          default: false
        
        variables:
        - name: varParam01
          value: ${{ parameters.param01 }}
        - name: varParam02
          value: ${{ parameters.param02 }}
        
        steps:
        - script: |
            echo "This steps is from the main YAML azure-pipelines.yml"
            echo "param01 = ${{ parameters.param01 }}"
            echo "param01 = ${{ parameters.param02 }}"
            echo "varParam01 = $(varParam01)"
            echo "varParam02 = $(varParam02)"
        
        - template: /templates/temp-steps01.yml
        
      • The template YAML /templates/temp-steps01.yml

        # /templates/temp-steps01.yml
        
        steps:
        - script: |
            echo "This steps is from template YAML temp-steps01.yml"
            echo "varParam01 = $(varParam01)"
            echo "varParam02 = $(varParam02)"
        
        - template: /templates/temp-steps02.yml
        
      • The nested template YAML /templates/temp-steps02.yml

        # /templates/temp-steps02.yml
        
        steps:
        - script: |
            echo "This steps is from the nested template YAML temp-steps02.yml"
            echo "varParam01 = $(varParam01)"
            echo "varParam02 = $(varParam02)"
        
    2. The result.

      enter image description here


    EDIT_2:

    For boolean type parameter, the available values are only true and false. So, you do not need to use the 'values' key to provide a list of all the available values. But you must provide the default value (true or false) using the 'default' key. Just like as the sample I shared above.

    For the string type parameter, you are correct, you can provide an empty value to the 'default' key.

    When users manually trigger the pipeline, for the boolean type parameter, the users can manually check or uncheck the option on the UI to change the value to be true (check) or false (uncheck). For the string type parameter, the users can manually enter the the value in the field on the UI.

    After users manually set/enter the parameter values and click "Run" on the UI to trigger a new run, the pipeline gets compiled with the parameter values set/entered by users.

    See below example.

    1. The main YAML azure-pipelines.yml. Updated based on above sample.
    # azure-pipelines.yml
    
    parameters:
    - name: param01
      type: boolean
      default: true
    - name: param02
      type: boolean
      default: false
    - name: param03
      type: string
      default: ''
    
    variables:
    - name: varParam01
      value: ${{ parameters.param01 }}
    - name: varParam02
      value: ${{ parameters.param02 }}
    - name: varParam03
      value: ${{ parameters.param03 }}
    
    steps:
    . . .
    
    1. Manually trigger the pipeline.

      enter image description here


    EDIT_3:

    1. The azure-pipelines.yml

      parameters:
      - name: param01
        type: boolean
        default: true
      - name: param02
        type: boolean
        default: false
      - name: param03
        type: string
        default: ''
      
      variables:
      - name: varParam01
        value: ${{ parameters.param01 }}
      - name: varParam02
        value: ${{ parameters.param02 }}
      - name: varParam03
        value: ${{ parameters.param03 }}
      
      extends:
        template: /templates/temp-steps01.yml
      
    2. The /templates/temp-steps01.yml

      extends:
        template: /templates/temp-steps02.yml
      
    3. The /templates/temp-steps02.yml

      steps:
      - script: |
          echo "This steps is from the nested template YAML temp-steps02.yml"
          echo "varParam01 = $(varParam01)"
          echo "varParam02 = $(varParam02)"
          echo "varParam03 = $(varParam03)"
      
    4. Result.

      enter image description here


    EDIT_4:

    # azure-pipelines.yml
    
    variables:
      myVar01: '123'
    
    stages:
    - stage: A
      variables:
        myVar02: '456'
      jobs:
      - job: A1
        variables:
          myVar03: '789'
        steps:
        . . .
      - job: A2
        variables:
        steps:
        . . .
    
    - stage: B
      . . .
    
    1. The variable myVar01 is a global variable that can be available for all the stages and jobs in this pipeline.

    2. The variable myVar02 is a stage-level variable defined in stage A. It can be available only within stage A, and not available for stage B.

    3. The variable myVar03 is a job-level variable defined in job A1. It can be available only within job A1, and not available for job A2 and stage B.


    EDIT_5:

    The '$(varName)' is a runtime expression that is evaluated at runtime before tasks start running. So, it generally is used as the input of tasks.

    The stage name and job name are evaluated at compile-time before runtime. So, when using the expression '$(varName)' on stage name and job name, it will return the expression itself.

    You need to use the template expression "${{ variables.varName }}" to pass pipeline variables to the stage name and job name, because this expression is evaluated at compile-time.

    However, when using template expression to pass pipeline variables in template YAML, only some of the predefined variables are available, the user-defined variables are not available. All the variables you defined under the 'variables' key are user-defined variables.

    enter image description here