Search code examples
yamlazure-pipelines

Pass a collection of environment variables to a yml step template in Azure DevOps


I need to pass a collection of environment variables to a yml template so that those variables can be used for one of its task.

I developed this sample yml template named my-new-template.yml:

parameters:
- name: envTests
  type: object
  default: []

steps:

- powershell: |
     Write-Host "$env:variable1"
  displayName: 'Display environment'
  env:
    ${{ parameters.envTests }}

where practically I pass the collection as an object input parameter named envTests and then this object is passed as it is to the env group of my powershell task.

The solution was taken from this SO thread Set environment variables into azure devops pipelines step from template.

In my main pipeline I call that template this way:

- job: Job1
    pool:
      vmImage: 'ubuntu-latest'

    steps:
    - checkout: none

    - template: my-new-template.yml
      parameters:
        envTests:
          - name: variabile1
            value: valuevariabile1
          - name: variabile2
            value: valueVariabile2

    - template: my-new-template.yml

I make two calls just to see that build is not broken if no environment variables are provided. It seems good to me but I got this exception

my-new-template.yml (Line: 12, Col: 5): A sequence was not expected

Could someone please tell me what I'm doing wrong? Thanks a lot


Solution

  • In the main YAML you can change to like as below to fix the issue.

    - job: Job1
        pool:
          vmImage: 'ubuntu-latest'
    
        steps:
        - checkout: none
    
        - template: my-new-template.yml
          parameters:
            envTests:
              variabile1: valuevariabile1
              variabile2: valueVariabile2
    

    EDIT:

    If you want conditionally execute the powershell task based on the length of parameter 'envTests' in the template YAML, you can add the conditional like as below in the template YAML.

    # my-new-template.yml
    
    parameters:
    - name: envTests
      type: object
      default: []
    
    steps:
    - ${{ if not(eq(length(parameters.envTests), 0)) }}:
      - powershell: |
          Write-Host "variable1 = $env:variable1"
          Write-Host "variable2 = $env:variable2"
        displayName: 'Display environment'
        env:
          ${{ parameters.envTests }}
    

    See below example:

    # azure-pipelines.yml
    
    stages:
    - stage: A
      jobs:
      - job: A1
        steps:
        - checkout: none
        - template: my-new-template.yml
          parameters:
            envTests:
              variable1: valuevariabile1
              variable2: valueVariabile2
      
      - job: A2
        steps:
        - checkout: none
        - template: my-new-template.yml
    
    • In job A1, the powershell task is executed as the values are provided to the parameter 'envTests'.
    • In job A2, the powershell task is not executed as no values are provided to the parameter 'envTests'.

    enter image description here


    EDIT_2:

    If you want to run the PowerShell task in some jobs when no any environment variables provided, you can update your template YAML referring the sample below.

    # my-new-template.yml
    
    parameters:
    - name: envTests
      type: object
      default: []
    
    steps:
    - ${{ if not(eq(length(parameters.envTests), 0)) }}:
      - powershell: |
          Write-Host "PowerShell scripts that need to provide some environment variables."
          Write-Host "variable1 = $env:variable1"
          Write-Host "variable2 = $env:variable2"
        displayName: 'Have env var provided'
        env:
          ${{ parameters.envTests }}
    
    - ${{ if eq(length(parameters.envTests), 0) }}:
      - powershell: |
          Write-Host "PowerShell scripts that do not need to provide some environment variables."
          Write-Host "There are no any environment variables provided."
        displayName: 'No env var provided'
    
    - powershell: |
        Write-Host "This task will always execute."
        Write-Host "Regardless of whether there are environment variables provided."
      displayName: 'Always run'
    
    # azure-pipelines.yml
    
    stages:
    - stage: A
      jobs:
      - job: A1
        steps:
        - checkout: none
        - template: my-new-template.yml
          parameters:
            envTests:
              variable1: valuevariabile1
              variable2: valueVariabile2
    
      - job: A2
        steps:
        - checkout: none
        - template: my-new-template.yml
    

    enter image description here


    EDIT_3:

    In above EDIT_2, If you want to combine the first and the second PowerShell tasks to be only one PowerShell task, you can do like as below.

    # my-new-template.yml
    
    parameters:
    - name: envTests
      type: object
      default: []
    
    steps:
    - powershell: |
        Write-Host "variable1 = $env:variable1"
        Write-Host "variable2 = $env:variable2"
      displayName: 'Show env var'
      ${{ if not(eq(length(parameters.envTests), 0)) }}:
        env:
          ${{ parameters.envTests }}
    
    . . .
    

    enter image description here