More on when Azure DevOps variables are available in pipeline runs

Introduction

I have previously blogged a good deal on Azure DevOps variable evaluation, see here and here, but the saga continues…

When variables exist

Today I realised, something I guess should have been obvious, that when you manually queue a run, pre-defined variables such as $(Build.SourceBranchName) are not available until the pipeline is compiled and starts running. This is because though there is a value in UI branch combo but this value is not in $(Build.SourceBranchName) until the pipeline starts running.

This would seem unimportant, and it is, except in the case when you are manually picking which stages to run via the run pipeline dialog. Only stages that have no conditional expressions, or ones where the $(Build.SourceBranchName) name is empty are shown in the list of stages to run.

Updated 22 July 2024 - Rik Hepworth pointed out to me that a good use of the Stage Picker UI is to do a ‘richer’ validation of the YAML pipeline.

This is useful as though the Azure DevOps UI does a validation of any YAML edits, it only shows compile time issues, mostly typos and indenting issues, it does not validate any runtime values.

Rik’s suggested that he had found loading the Stage Picker UI is a good way to do further validation of “some of the runtime values”. This is a good suggestion, it is not perfect validation, but allows you to do a bit more checking before queuing a run.

Run Pipeline Dialog

trigger: none

pool:
  vmImage: ubuntu-latest

stages:
  - stage: No_Conditions
    jobs:
      - job: Job1
        steps:
        - script: echo 'Running on $(Build.SourceBranchName)'

  - ${{ if eq(variables['Build.SourceBranchName'], 'main') }}:
    - stage: BranchName_is_main
      jobs:
        - job: Job2
          steps:
          - script: echo 'Running on $(Build.SourceBranchName)'

  - ${{ if or(eq(variables['Build.SourceBranchName'], 'main'), eq(variables['Build.SourceBranchName'], '')) }}:
    - stage: BanchName_is_main_or_empty
      jobs:
        - job: Job3
          steps:
          - script: echo 'Running on $(Build.SourceBranchName)'

  - stage: Using_Conditions
    condition: eq(variables['Build.SourceBranchName'], 'main')
    jobs:
      - job: Job4
        steps:
        - script: echo 'Running on $(Build.SourceBranchName)'

Remember if you wish to if a {{if }} expression to actually filter on a branch name you need to use an Or to handle both the UI and runtime conditions

Basically, you cannot rely on the value of Build.SourceBranchName and -{{ if }} expressions to trim the list of available stages in the run build UI. In my opinion, best to avoid even trying to do this.

What can I do with conditions?

If you want to be able to pick the stages to run in the UI, and need checks against items like branch names, the simple answer is to use conditions on the stages. These are only evaluated at the moment the stage is started, unlike expressions which are evaluated when the run is queued.

But how can we give users an indication of what will be run, or not, as soon as the run is started as opposed to only finding out as it completes?

Variables and expressions are the answer here. Using expressions we can set a variable based on the branch name at queue time. This can be used in a displayname for the stage so in the UI it shows if a stage will be run or not

variables:
  - ${{ if eq(variables['Build.SourceBranchName'], 'main') }}:
    - name: willdeployToProdString
      value: Yes
  - ${{ if ne(variables['Build.SourceBranchName'], 'main') }}:
    - name: willdeployToProdString
      value: No
stages:
   # other stages shown above left of for clarity of the example

  - stage: Using_Conditions
    displayName: Will deploy ${{variables.willdeployToProdString}}
    condition: eq(variables['Build.SourceBranchName'], 'main')
    jobs:
      - job: Job4
        steps:
        - script: echo 'Running on $(Build.SourceBranchName)'

Run Pipeline Dialog

Remember if you are skipping steps with conditions, you may need to set the condition on subsequent stages to run. The default (if no condition is set) is to only run on success of the previous stage. See the official docs for more details on your options

Conclusion

As I have written about before, use compile time expressions in Azure DevOps Pipelines with care, their evaluation can get complex.

So a bit of a niche blog post, hope it is of use to someone

For the original version of this post see Richard Fennell's personal blog at More on when Azure DevOps variables are available in pipeline runs