Azure DevOps: How to Pass Variables Between Jobs and Stages

Learning how to pass variables seamlessly between jobs and stages in Azure DevOps pipelines to create more dynamic and flexible workflows.

In Azure DevOps pipelines, passing variables between jobs and stages is a fundamental aspect of creating dynamic and flexible workflows. This capability allows data, such as build outputs, version numbers, or environment variables, to be shared between different parts of the pipeline. Whether you’re building, testing, or deploying applications, having a smooth and automated flow of information between stages is essential.

In this blog post, we’ll walk through how to define and pass variables between jobs and stages using both Bash and PowerShell scripts. You’ll also learn how to utilize these techniques to create more dynamic and reusable CI/CD workflows.

Key Concepts

Before diving into examples, let’s cover some key concepts:

  • Variables in Azure DevOps:
    These are values that can be used throughout your pipeline. Variables can be predefined, such as Build.BuildId or System.DefaultWorkingDirectory, or custom variables that you define.

  • Job Outputs:
    A job in Azure DevOps can output variables that other jobs can access using a specific syntax. This allows the result of one job (e.g., a generated version number) to be passed to another job without needing manual input.

  • Stage Dependencies
    In Azure DevOps, stages represent major phases in your pipeline. You can organize your pipeline into multiple stages, and you can control the execution flow between them using stage dependencies.

    • Purpose:
      Stage dependencies help you control the order in which stages run. By default, stages in a pipeline run sequentially, but you can adjust this behavior to make certain stages dependent on the outcome of other stages.

    • Syntax:
      Stage dependencies are specified using the dependsOn keyword in a stage’s configuration. You can also use the stageDependencies syntax to pass variables between stages.

  • Job Dependencies
    A job is a set of steps that run on an agent. Just like stages, jobs can also have dependencies to control the order in which they run.

    • Purpose:
      Job dependencies allow you to control the execution order of jobs within a stage or across stages. This is useful when one job needs the output or result of another job to proceed.

    • Syntax:
      You can specify job dependencies using dependsOn. By default, jobs within a stage run in parallel unless specified otherwise.

Example: Job - Bash

In the first example, we have two jobs (JobA and JobB). JobA generates a variable, and JobB consumes that variable. The following example uses Bash for both jobs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: JobA
  displayName: "Generate a variable"
  steps:
    - task: Bash@3
      displayName: "Generate variable in Bash"
      name: OutputVars
      inputs:
        targetType: 'inline'
        script: |
          outputValue="Hello from Job A!"
          echo "outputValue: $outputValue"
          echo "##vso[task.setvariable variable=generatedVar;isOutput=true]$outputValue"

- job: JobB
  displayName: "Consume the variable from Job A"
  dependsOn: JobA
  variables:
    jobAOutput: $[ dependencies.JobA.outputs['OutputVars.generatedVar'] ]

  steps:
    - task: Bash@3
      displayName: "Print the variable from Job A"
      inputs:
        targetType: 'inline'
        script: |
          echo "Job A Output: $(jobAOutput)"

Explanation

JobA generates a variable generatedVar using a Bash script and sets it as an output variable with the ##vso[task.setvariable] syntax. JobB uses dependencies.JobA.outputs to reference the variable from JobA and prints it in its Bash script.

Job - PowerShell

In this next example, we’ll use PowerShell for the same job structure. The setup is similar, but we’re utilizing PowerShell scripting instead of Bash.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: JobA
  displayName: "Generate a variable"
  steps:
    - task: PowerShell@2
      displayName: "Generate variable in PowerShell"
      name: OutputVars
      inputs:
        targetType: 'inline'
        script: |
          $outputValue = "Hello from Job A!"
          Write-Host "outputValue: $outputValue"
          Write-Host "##vso[task.setvariable variable=generatedVar;isOutput=true]$outputValue"

- job: JobB
  displayName: "Consume the variable from Job A"
  dependsOn: JobA
  variables:
    jobAOutput: $[ dependencies.JobA.outputs['OutputVars.generatedVar'] ]

  steps:
    - task: PowerShell@2
      displayName: "Print the variable from Job A"
      inputs:
        targetType: 'inline'
        script: |
          Write-Host "Job A Output: $(jobAOutput)"

Explanation

Similar to the Bash example, JobA generates a variable using PowerShell and outputs it. JobB then consumes the output from JobA and prints it using PowerShell.

Stage - Bash

Now let’s look at how to pass variables between stages. This example uses Bash for both stages.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: Build
  displayName: 'Build Stage'
  jobs:
  - job: BuildJob
    displayName: 'Build Job'
    steps:
    - task: Bash@3
      displayName: 'Set Build Variables'
      name: setBuildVariables
      inputs:
        targetType: 'inline'
        script: |
          echo "##vso[task.setvariable variable=buildVersion;isOutput=true]1.0.0"
          echo "Build Version set to 1.0.0"

- stage: Deploy
  displayName: 'Deploy Stage'
  dependsOn: Build
  jobs:
  - job: DeployJob
    displayName: 'Deploy Job'
    variables:
      buildVersion: $[stageDependencies.Build.BuildJob.outputs['setBuildVariables.buildVersion']]
    steps:
    - task: Bash@3
      displayName: 'Use Build Version'
      inputs:
        targetType: 'inline'
        script: |
          echo "Using Build Version: $(buildVersion)"

Explanation

In the Build stage, BuildJob generates a variable buildVersion with the value 1.0.0. In the Deploy stage, DeployJob consumes the buildVersion variable using stageDependencies and prints it.

Stage - PowerShell

Here’s the same example as above but using PowerShell.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: Build
  displayName: 'Build Stage'
  jobs:
  - job: BuildJob
    displayName: 'Build Job'
    steps:
    - task: PowerShell@2
      displayName: 'Set Build Variables'
      name: setBuildVariables
      inputs:
        targetType: 'inline'
        script: |
          Write-Host "##vso[task.setvariable variable=buildVersion;isOutput=true]1.0.0"
          Write-Host "Build Version set to 1.0.0"

- stage: Deploy
  displayName: 'Deploy Stage'
  dependsOn: Build
  jobs:
  - job: DeployJob
    displayName: 'Deploy Job'
    variables:
      buildVersion: $[stageDependencies.Build.BuildJob.outputs['setBuildVariables.buildVersion']]
    steps:
    - task: PowerShell@2
      displayName: 'Use Build Version'
      inputs:
        targetType: 'inline'
        script: |
          $buildVersion = "$(buildVersion)"
          Write-Host "Using Build Version: $buildVersion"

Explanation

In this PowerShell version, BuildJob sets the buildVersion variable using Write-Host. DeployJob accesses the variable using stageDependencies and prints it.

Variable Input for Jobs - Bash

In this example, we demonstrate how to define and pass variables across different jobs within an Azure DevOps pipeline using Bash.

Key Components

  • Parameters:
    We start by defining a parameter myName with a default value. This allows the user to input their name when the pipeline is triggered.

  • Setting Variables in Job A:
    In the first job, we set the variable using a Bash script. The value of myName is passed from the parameters to the script via the ${{ parameters.myName }} syntax. Once the value is set, we use the ##vso[task.setvariable] command to make myName available to other jobs in the pipeline, with the isOutput=true flag ensuring that the variable can be accessed by downstream jobs.

  • Using Variables in Job B:
    The second job, JobB, depends on the successful completion of the SetVariables job. We reference the variable set in JobA by using the dependencies.SetVariables.outputs syntax. In the JobB job, we use the $(jobAOutput) variable to print the output of JobA.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
trigger:
- none

pool:
  vmImage: 'ubuntu-latest'

parameters:
  - name: myName
    displayName: 'Your Name'
    type: string
    default: ''

stages:
- stage: Build
  displayName: 'Build Stage'
  jobs:
  - job: SetVariables
    displayName: 'Set Variables Job'
    steps:
    - task: Bash@3
      displayName: 'Use myName variable'
      name: OutputVars
      inputs:
        targetType: 'inline'
        script: |
          myName="${{ parameters.myName }}"
          echo "Hello, $myName!"
          echo "##vso[task.setvariable variable=myName;isOutput=true]$myName"

  - job: JobB
    displayName: "Consume the variable from Job A"
    dependsOn: SetVariables
    variables:
      jobAOutput: $[ dependencies.SetVariables.outputs['OutputVars.myName'] ]

    steps:
      - task: Bash@3
        displayName: "Print the variable from Job A"
        inputs:
          targetType: 'inline'
          script: |
            echo "Job A Output: $(jobAOutput)"

Variable Input for Jobs - PowerShell

Now let’s look at a similar example but using PowerShell. This approach demonstrates how to define and use pipeline parameters, set variables, and reference them in subsequent jobs.

Key Components

  • Parameters:
    As with the Bash example, we define a myName parameter, which will be passed to the PowerShell script.

  • Setting Variables in Job A:
    In the SetVariables job, we use PowerShell to assign the parameter value to the $myName variable. This is accomplished by reading the environment variable myName from the pipeline context ($env:myName). The Write-Host command prints the message, and again we use the ##vso[task.setvariable] to share the variable as an output.

  • Using Variables in Job B:
    Just like in the Bash example, JobB consumes the output variable from SetVariables. The jobAOutput variable is populated using the dependencies.SetVariables.outputs syntax, and we print the value of the variable in the Print the variable from Job A step.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
trigger:
- none

pool:
  vmImage: 'ubuntu-latest'

parameters:
  - name: myName
    displayName: 'Your Name'
    type: string
    default: ''

stages:
- stage: Build
  displayName: 'Build Stage'
  jobs:
  - job: JobA
    displayName: 'Set Variables Job'
    steps:
    - task: PowerShell@2
      displayName: 'Use myName variable'
      name: OutputVars
      inputs:
        targetType: 'inline'
        script: |
          $myName = $env:myName
          Write-Host "Hello, $myName!"
          Write-Host "##vso[task.setvariable variable=myName;isOutput=true]$myName"
      env:
        myName: ${{ parameters.myName }}

  - job: JobB
    displayName: "Consume the variable from Job A"
    dependsOn: JobA
    variables:
      jobAOutput: $[ dependencies.JobA.outputs['OutputVars.myName'] ]

    steps:
      - task: PowerShell@2
        displayName: "Print the variable from Job A"
        inputs:
          targetType: 'inline'
          script: |
            Write-Host "Job A Output: $(jobAOutput)"

Variable Input for Stages - Bash

In this example, we demonstrate how to define and pass variables across different stages within an Azure DevOps pipeline using Bash.

Key Components

  • Parameters:
    We define a parameter myName with a default value, allowing the user to input their name when the pipeline is triggered.

  • Setting Variables in Stage 1:
    In the first stage (Stage1), we set the variable using a Bash script. The value of myName is passed from the parameters to the script via the ${{ parameters.myName }} syntax. The ##vso[task.setvariable] command makes myName available to other stages in the pipeline with the isOutput=true flag ensuring that the variable can be accessed by downstream stages.

  • Using Variables in Stage 2:
    The second stage, Stage2, depends on the successful completion of Stage1. We reference the variable set in Stage1 by using the dependencies.Stage1.outputs syntax. In Stage2, we use the $(stage1Output) variable to print the output from Stage1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
trigger:
trigger:
- none

pool:
  vmImage: 'ubuntu-latest'

parameters:
  - name: myName
    displayName: 'Your Name'
    type: string
    default: ''

stages:
- stage: Stage1
  displayName: 'First Stage'
  jobs:
  - job: JobA
    displayName: 'Set Variables Job'
    steps:
    - task: Bash@3
      displayName: 'Use myName variable'
      name: OutputVars
      inputs:
        targetType: 'inline'
        script: |
          myName="${{ parameters.myName }}"
          echo "Hello, $myName!"
          echo "##vso[task.setvariable variable=myName;isOutput=true]$myName"

- stage: Stage2
  displayName: 'Second Stage'
  dependsOn: Stage1
  jobs:
  - job: JobB
    displayName: "Consume the variable from Stage1"
    variables:
      stage1Output: $[ stageDependencies.Stage1.JobA.outputs['OutputVars.myName']]
    steps:
      - task: Bash@3
        displayName: "Print the variable from Stage1"
        inputs:
          targetType: 'inline'
          script: |
            echo "Stage1 Output: $stage1Output"

Variable Input for Stages - PowerShell

Now let’s look at a similar example but using PowerShell to define and pass variables across stages.

Key Components

  • Parameters:
    As with the Bash example, we define a myName parameter that will be passed to the PowerShell script.

  • Setting Variables in Stage 1:
    In Stage1, we assign the parameter value to the $myName variable using PowerShell. The Write-Host command prints the message, and the ##vso[task.setvariable] command shares the variable as an output.

  • Using Variables in Stage 2:
    Stage2 consumes the output variable from Stage1. The stage1Output variable is populated using dependencies.Stage1.outputs, and we print the value of the variable in Stage2.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
trigger:
- none

pool:
  vmImage: 'windows-latest'

parameters:
  - name: myName
    displayName: 'Your Name'
    type: string
    default: ''

stages:
- stage: Stage1
  displayName: 'First Stage'
  jobs:
  - job: JobA
    displayName: 'Set Variables Job'
    steps:
    - task: PowerShell@2
      displayName: 'Use myName variable'
      name: OutputVars
      inputs:
        targetType: 'inline'
        script: |
          $myName = "${{ parameters.myName }}"
          Write-Host "Hello, $myName!"
          Write-Host "##vso[task.setvariable variable=myName;isOutput=true]$myName"

- stage: Stage2
  displayName: 'Second Stage'
  dependsOn: Stage1
  jobs:
  - job: JobB
    displayName: "Consume the variable from Stage1"
    variables:
      stage1Output: $[ stageDependencies.Stage1.JobA.outputs['OutputVars.myName'] ]
    steps:
      - task: PowerShell@2
        displayName: "Print the variable from Stage1"
        inputs:
          targetType: 'inline'
          script: |
            Write-Host "Stage1 Output: $stage1Output"

Wrap Up

In this post, we’ve explored how to seamlessly pass variables between jobs and stages in Azure DevOps pipelines. This crucial aspect of pipeline design enables dynamic and flexible workflows, making it easier to automate complex processes.

Key Takeaways

  • Variables in Azure DevOps can be used to store values that persist across jobs and stages, including predefined and custom variables.

  • Job Outputs and Stage Dependencies allow you to share data between different parts of the pipeline, using the ##vso[task.setvariable] syntax for jobs and stageDependencies for stages.

  • We’ve seen examples of how to pass variables using Bash and PowerShell scripts, demonstrating both job-to-job and stage-to-stage variable passing.

By incorporating these practices into your Azure DevOps pipelines, you can create more modular, maintainable, and dynamic automation flows. Whether you’re working with simple jobs or more complex stages, understanding how to leverage output variables and dependencies will improve your pipeline’s efficiency and scalability.

Keep experimenting with these techniques to build more advanced workflows and harness the full potential of Azure DevOps!

Share with your network!

Built with Hugo - Theme Stack designed by Jimmy