Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure Devops - passing variables between job templates

Normal (non-template) jobs in Azure DevOps yaml support inter-job variable passing as follows:

jobs:
- job: A
  steps:
  - script: "echo ##vso[task.setvariable variable=skipsubsequent;isOutput=true]false"
    name: printvar

- job: B
  condition: and(succeeded(), ne(dependencies.A.outputs['printvar.skipsubsequent'], 'true'))
  dependsOn: A
  steps:
  - script: echo hello from B

How do I do something similar in the following, given that templates don't support the dependsOn syntax? I need to get an output from the first template and pass it as 'environmentSlice' to the second template.

- stage: Deploy
  displayName: Deploy stage
  jobs:
  - template: build-templates/get-environment-slice.yml@templates
    parameters:
      configFileLocation: 'config/config.json'

  - template: build-templates/node-app-deploy.yml@templates
    parameters:
      # Build agent VM image name
      vmImageName: $(Common.BuildVmImage)
      environmentPrefix: 'Dev'
      environmentSlice: '-$(dependencies.GetEnvironmentSlice.outputs['getEnvironmentSlice.environmentSlice'])'

The reason I want the separation between the two templates is the second one is a deployment template and I would like input from the first template in naming the environment in the second template. I.e. initial part of node-app-deploy.yml (2nd template) is:

  jobs:
  - deployment: Deploy
    displayName: Deploy
    # Because we use the environmentSlice to name the environment, we have to have it passed in rather than 
    # extracting it from the config file in steps below
    environment: ${{ parameters.environmentPrefix }}${{ parameters.environmentSlice }}

Update:

The accepted solution does allow you to pass variables between separate templates, but won't work for my particular use case. I wanted to be able to name the 'environment' section of the 2nd template dynamically, i.e. environment: ${{ parameters.environmentPrefix }}${{ parameters.environmentSlice }}, but this can only be named statically since templates are compiled on pipeline startup.

The downside of the solution is that it introduces a hidden coupling between the templates. I would have preferred the calling pipeline to orchestrate the parameter passing between templates.

like image 927
vipes Avatar asked Nov 25 '19 19:11

vipes


People also ask

How do I use variables in Azure DevOps CLI?

Azure DevOps CLI In the most common case, you set the variables and use them within the YAML file. This allows you to track changes to the variable in your version control system. You can also define variables in the pipeline settings UI (see the Classic tab) and reference them in your YAML.

How do I use multiple variables in azure pipelines?

You can use a variable group to make variables available across multiple pipelines. You can use templates to define variables that are used in multiple pipelines in one file. In addition to user-defined variables, Azure Pipelines has system variables with predefined values.

How to reference previously defined variable groups in Azure DevOps project?

This variable block can also reference previously defined variable groups. Variable groups are managed in the Library tab under the Pipelines menu of your Azure DevOps project. variables: - group: "Contoso Variable Group" - name: anothervariable value: 'Hi there'

How does the testjob template work in Azure API?

The template then references the testJob.templateContext.expectedHTTPResponseCode, which gets set in azure-pipeline.yml and passed to the template. When response code is 200, the template makes a REST request. When the response code is 500, the template outputs all of the environment variables for debugging.


2 Answers

You can apply the depend on and dependency variable into templates.

See below sample:

To make sample more clear, here has 2 template files, one is azure-pipelines-1.yml, and another is azure-pipeline-1-copy.yml.

In azure-pipelines-1.yml, specify the environment value as output variable:

parameters:
  environment: ''
jobs:
- job: preDeploy
  variables:
    EnvironmentName: preDeploy-${{ parameters.environment }}
  steps:
  - checkout: none
  - pwsh: |
      echo "##vso[task.setvariable variable=EnvironmentName;isOutput=true]$($env:ENVIRONMENTNAME)"
    name: outputVars

And then, in azure-pipeline-1-copy.yml use dependency to get this output variable:

jobs:
- job: deployment
  dependsOn: preDeploy
  variables:
    EnvironmentNameCopy: $[dependencies.preDeploy.outputs['outputVars.EnvironmentName']]
  steps:
  - checkout: none
  - pwsh: |
      Write-Host "$(EnvironmentNameCopy)"
    name: outputVars

At last, in YAML pipeline, just need to pass the environment value

stages:
  - stage: deployQA
    jobs:
    - template: azure-pipelines-1.yml
      parameters:
        environment: FromTemplate1
    - template: azure-pipeline-1-copy.yml

Now, you can see the value get successfully in the second template job:

enter image description here

like image 148
Mengdi Liang Avatar answered Oct 18 '22 21:10

Mengdi Liang


It is possible to avoid the dependency in the called template. However, as the OP says, the environment name cannot be created dynamically.

Here is an example of the "calling" template, which firstly calls another template (devops-variables.yml) that sets some environment variables that we wish to consume in a later template (devops-callee.yml):

stages:
- stage: 'Caller_Stage'
  displayName: 'Caller Stage'

  jobs:
  - template: 'devops-variables.yml'
    parameters:
      InitialEnvironment: "Development"

  - template: 'devops-callee.yml'
    parameters:
      SomeParameter: $[dependencies.Variables_Job.outputs['Variables_Job.Variables.SomeParameter']]

In the devops-variables.yml file, I have this:

"##vso[task.setvariable variable=SomeParameter;isOutput=true;]Wibble"

Then, in the "devops-callee.yml", I just consume it something like this:

  parameters:
  - name: SomeParameter
    default: ''

  jobs:
  - deployment: 'Called_Job'
    condition: succeeded()
    displayName: 'Called Job'
    environment: "Development"
    pool:
      vmImage: 'windows-2019'
    dependsOn:
    - Variables_Job
    variables:
      SomeParameter: ${{parameters.SomeParameter}}
    strategy:
      runOnce:
        deploy:
          steps:
          - download: none
          - task: AzureCLI@2
            condition: succeeded()
            displayName: 'An Echo Task'
            inputs:
              azureSubscription: "$(TheServiceConnection)"
              scriptType: pscore
              scriptLocation: inlineScript
              inlineScript: |
                echo "Before"
                echo "$(SomeParameter)"
                echo "After"

Output:

2021-04-10T09:22:29.6188535Z Before
2021-04-10T09:22:29.6196620Z Wibble
2021-04-10T09:22:29.6197124Z After

This way, the callee doesn't reference the caller. Unfortunately, setting the environment in the callee thus:

environment: "$(SomeParameter)"

doesn't work - you'll just get an environment with the literal characters '$(SomeParameter)'.

like image 1
Mark Avatar answered Oct 18 '22 22:10

Mark