'Azure Pipeline: Setting variable in one template, use it in expression in another

What I tried to achive:

  1. Set variable featureName in template 1 called set-variable.yml
  2. Pass variable featureName as parameter to template 2 called check-variable.yml
  3. Check if featureName parameter is empty in a compile time expression in template 2.

pipeline.yml:

trigger:
  - '*' # Run on all Branches

pool:
    vmImage: ubuntu-latest

stages:
- stage: test1
  jobs:
  - job: test1
    steps:
    - template: set-variable.yml
    - template: check-variable.yml
      parameters:
        featureName: $(setVariables.featureName)

set-variable.yml:

steps:
- script: |
    featureName=''
    echo "##vso[task.setvariable variable=featureName;isOutput=true]$featureName"
  displayName: Set variable 
  name: setVariables

check-variable.yml:

parameters:
- name: featureName
  type: string
  default: ''


steps:
- ${{ if ne(parameters.featureName, '') }}:
  - script: |
      echo "This should not be executed, but it is!"
    displayName: "featureName != ''"

The problem is, that the step featureName != '' in check-variable.yml gets executed, even though I set the featureName variable in the set-variables.yml template to be empty.

(Passing of the variable featureName as a parameter to template check-variable.yml is working, see debugging attempts below)

=== Debugging 1 ===

For debugging purpose, I've expanded the check-variable.yml with some debugging tasks.

check-variable.yml:

parameters:
- name: featureName
  type: string
  default: ''


steps:
- script: |
    echo "Check feature"
    echo "- featureNameDotted: [${{ parameters.featureName }}]"
    echo "- featureNameArray: [${{ parameters['featureName'] }}]"
    echo "- featureNameEnvDotted: [$featureNameEnvDotted]"
    echo "- featureNameEnvArray: [$featureNameEnvArray]"
    echo "- json: [${{ convertToJson(parameters.featureName) }}]"
    echo "- length: [${{ length(parameters.featureName) }}]"
    echo

    if [[ -z $featureNameEnvDotted ]]; then
      echo "featureNameEnvDotted is (empty or null)"
    else
      echo "featureNameEnvDotted not (empty or null)"
    fi
    echo

    if [[ $featureNameEnvDotted = '' ]]; then
      echo "featureNameEnvDotted = ''"
    else
      echo "featureNameEnvDotted != ''"
    fi
    echo

    if [[ $featureNameEnvDotted = 'hello' ]]; then
      echo "featureNameEnvDotted = 'hello'"
    else
      echo "featureNameEnvDotted != 'hello'"
    fi
  env:
    featureNameEnvDotted: ${{ parameters.featureName }}
    featureNameEnvArray: ${{ parameters['featureName'] }}
  displayName: "Debug: All"
  
- ${{ if eq(parameters.featureName, '') }}:
  - script: |
      echo 
    env:
      featureName: ${{ parameters.featureName }}
    displayName: "Debug: Empty!!"

- ${{ if ne(parameters.featureName, '') }}:
  - script: |
      echo 
    env:
      featureName: ${{ parameters.featureName }}
    displayName: "Debug: Not empty!!"
  
- ${{ if eq(parameters.featureName, 'hello') }}:
  - script: |
      echo 
    env:
      featureName: ${{ parameters.featureName }}
    displayName: "Debug: Equals 'hello'"

- ${{ if ne(parameters.featureName, '') }}:
  - script: |
      echo "This should not be executed, but it is!"
    displayName: "featureName != ''"

The output of the task Debug: All is:

Check feature

  • featureNameDotted: []
  • featureNameArray: []
  • featureNameEnvDotted: []
  • featureNameEnvArray: []
  • json: []
  • length: [27]

featureNameEnvDotted is (empty or null)

featureNameEnvDotted = ''

featureNameEnvDotted != 'hello'

The bash if's work all well. featureName is empty and it is not 'hello'. The 'length' one I don't understand.

And the following tasks get executed:

  • Set variable
  • Debug: All
  • Debug: Not empty!!
  • featureName != ''

Why do the tasks Debug: Not empty!! and featureName != '' get executed, but task Debug: Empty!! not? featureName parameter IMO is empty!

=== Debugging 2 ===

I've left check-variable.yml the same. But I set featureName in the set-variable.yml template to the value "hello". With this I wanted to check if the variable featureName does get properly passed to the parameter of the template check-variable.yml

set-variable.yml:

steps:
- script: |
    featureName='hello'
    echo "##vso[task.setvariable variable=featureName;isOutput=true]$featureName"
  displayName: Set variable 
  name: setVariables

The output of the task Debug: All is:

Check feature

  • featureNameDotted: [hello]
  • featureNameArray: [hello]
  • featureNameEnvDotted: [hello]
  • featureNameEnvArray: [hello]
  • json: [hello]
  • length: [27]

featureNameEnvDotted not (empty or null)

featureNameEnvDotted != ''

featureNameEnvDotted = 'hello'

This again looks well. The parameter does hold the value 'hello' and the if's look good (still, length I don't understand).

And here the list of tasks which get executed:

  • Set variable
  • Debug: All
  • Debug: Not empty!!
  • featureName != ''

Why is Debug: Equals 'hello' not executed?

parameters.featureName holds something like the value 'hello', but it is not exactly the string 'hello'. Am I doing something wrong here?



Solution 1:[1]

I found the problem. The ${{ length(parameters.featureName) }} which evaluates to 27 pushed me in the right direction. 27 is the length of the string $(setVariables.featureName) which is passed to the template check-variable.yml.

This can be tested with the following expression which I put into check-variable.yml:

- ${{ if eq(parameters.featureName, '$(setVariables.featureName)') }}:
  - script: |
      echo 
    env:
      featureName: ${{ parameters.featureName }}
    displayName: "Debug: Equals 'dollar(setVariables.featureName)'"

This always executes, when featureName was set or if it was empty.

I think what happens is that when the compile time expressions like ${{ ... }} are evaluated inside a template, the parameter featureName is evaluated to $(setVariables.featureName). This is the case in the ${{ if ... }} expressions as well as in the script tasks with ${{ parameters...}}. After the ${{ ... }} expressions have been evaluated, it starts executing the script tasks. There it finds the variable $(setVariables.featureName) and evaluates it. But evaluation of the $(setVariables.featureName) is never done for the compile time expressions with an if statement. There it just takes the name of the variable.

So to get it to work I did a work around where I set a variable in a script task, and use a task condition to evaluate it.

check-variable.yml:

parameters:
- name: featureName
  type: string
  default: ''


steps:
- script: |
    echo "##vso[task.setvariable variable=featureName]${{ parameters.featureName }}"
  displayName: "Set featureName var"

- script: |
    echo "Only execute me when featureName is not empty"
  displayName: "featureName != ''"
  condition: and(succeeded(), ne(variables.featureName, ''))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Simon Lang