Search code examples
azure-devopsazure-pipelinesazure-pipelines-yaml

Azure DevOps yaml pipeline: Using output variables in conditions does not evaluate correctly


Im trying to create an output variable that will be later used for other jobs. However there is a chance that the job responsible for its creation gets skipped. To recover from this scenario I tried appending "placeholder" to the variable mentioned earlier. Later stage however does not seem to evaluate not equal condition correctly

#Here is the base pipeline:

trigger: none
pool: '<?>'
parameters:
- name: 'String'
  type: string
  displayName: 'String'
- name: 'skipA'
  type: boolean
  displayName: 'Skip stage A'
  default: false

stages:
- stage: A
  jobs:
  - job: A1
    condition: ne('${{ parameters.skipA }}', true)
    steps:
    - task: PowerShell@2
      displayName: 'Run script 1'
      name: script1
      inputs:
        pwsh: true
        filePath: 'outputVariables\scripts\simpleScript.ps1'
        arguments: >-
          -inputValue ${{ parameters.String }}


- stage: B
  variables:
    myVar: $[ stageDependencies.A.A1.outputs['script1.myVar'] ]
  dependsOn: A
  jobs:
  - job: B1
    steps:
      - task: PowerShell@2
        displayName: 'Run script 1'
        name: script1
        inputs:
          pwsh: true
          filePath: 'outputVariables\scripts\simpleScript.ps1'
          arguments: >-
            -inputValue "placeholder,$(myVar)"
  - job: B2
    steps:
      - template: 'templateFile.yml'
        parameters:
          somethingElse: '$(myVar),placeholder'

#templateFile.yml:

parameters:
  - name: 'somethingElse'
    type: string

steps:
  - task: PowerShell@2
    condition: ne('${{parameters.somethingElse}}', ',placeholder') #NOT EVALUATED CORRECTLY
    displayName: 'Run script 3'
    name: script1
    inputs:
      pwsh: true
      filePath: 'outputVariables\scripts\simpleScript.ps1'
      arguments: >-
        -inputValue "${{parameters.somethingElse}}"
  
  - script: echo step 2.1

#ps1 script:

param (
    [Parameter(Mandatory=$false)]
    [string]$inputValue = "Def val"
)

# Print the input value to the screen
Write-Output "You entered: $inputValue"
Write-Host "##vso[task.setvariable variable=myVar;isOutput=true]$inputValue"

I expect the B2 job not to run the simpleScript


Solution

  • Based on the description, you would like to generate the value for the variable myVar output in script at pipeline runtime and pass it into templateFile.yml as a parameter. However, we should not evaluate the parameter value in template expressions ${{parameters}} which get processed at compile time, before runtime. See this document to help us Understand variable syntax

    Instead of evaluating a parameter which can only be referenced in template expressions, we should opt to define newVar in runtime expressions $[variables] and evaluate its value in the condition property of a step. Here is the working YAML syntax for your reference.

    basePipeline.yml

    trigger: none
    
    pool: 'Default'
    
    
    parameters:
    - name: 'String'
      type: string
      displayName: 'String'
      default: ''
    - name: 'skipA'
      type: boolean
      displayName: 'Skip stage A'
      default: false
    
    stages:
    - stage: A
      jobs:
      - job: A1
        condition: ne('${{ parameters.skipA }}', true)
        steps:
        - task: PowerShell@2
          displayName: 'Run script 1'
          name: script1
          inputs:
            pwsh: true
            filePath: 'outputVariables\scripts\simpleScript.ps1'
            arguments: >-
              -inputValue "${{ parameters.String }}"
        - powershell: |
            Write-Host "MyVar is $(script1.myVar)"
          displayName: 'Check output'
    
    - stage: B
      variables:
        myVar: $[ stageDependencies.A.A1.outputs['script1.myVar'] ]
        stageDeps: $[ convertToJson(stageDependencies) ]
      dependsOn: A
      jobs:
      - job: B1
        steps:
          - task: PowerShell@2
            displayName: 'Run script 2'
            name: script2
            inputs:
              pwsh: true
              filePath: 'outputVariables\scripts\simpleScript.ps1'
              arguments: >-
                -inputValue "placeholder,$(myVar)"
    
          - powershell: |
              Write-Host "myVar output in A1 is $(myVar)"
              Write-Host "myVar output in B1 is $(script2.myVar)"
              Write-Host "stageDeps is $(stageDeps)"
            displayName: 'Check output'
            
      - job: B2
        variables:
          newVar: $[ format('{0}{1}', variables['myVar'], ',placeholder') ]
    
        steps:
          - template: 'templateFile.yml'
            parameters:
              somethingElse: '$(newVar)'
    

    templateFile.yml

    parameters:
      - name: 'somethingElse'
        type: string
    
    steps:
      - task: PowerShell@2
        condition: ne(variables['newVar'], ',placeholder') # Evaluate the variable value generated during pipeline runtime
        displayName: 'Run script 3'
        name: script3
        inputs:
          pwsh: true
          filePath: 'outputVariables\scripts\simpleScript.ps1'
          arguments: >-
            -inputValue "$(newVar)"
    
      - powershell: |
          Write-Host "newVar defined in B2 is $(newVar)"
        condition:  always()
        displayName: 'Check output'