Search code examples
azure-devopsazure-pipelines

In Azure DevOps Pipeline, how to pass an array of objects from output variable to a template as parameter?


I'm building a variable made of array of objects and can somehow pass it to another job from another stage, and can even print its value. But it seems I'm not passing it properly as a parameter to the template and I'm getting this error:

/templates/process-apps-template.yml (Line: 16, Col: 3): Expected a sequence or mapping. Actual value '$(apps)'

And here are my codes

# applications-pipeline.yml
stages:
  - stage: Initialize
    displayName: Initialize
    jobs:
    - task: Bash@3
      name: Applications
      inputs:
        targetType: inline
        workingDirectory: '$(System.DefaultWorkingDirectory)/01_applications'
        script: |
          # simplified code, the actual one was very long
          applications=(
            '{"application": "app1", "environments": ["dev", "test", "production"]}'
            '{"application": "app2", "environments": ["dev", "staging"]}'
          )
          echo "##vso[task.setvariable variable=apps;isOutput=true]${applications[@]}"

  - stage: ProcessApps
    dependsOn: [Initialize]
    variables: 
      - name: apps
        value: $[ stageDependencies.Initialize.AppsList.outputs['Applications.apps'] ]
    jobs:    
      - template: templates/process-apps-template.yml
        parameters:
          apps: $(apps)


# templates/process-apps-template.yml
parameters:
- name: apps
  type: object
  default: []

jobs:
- job: PaceholderJob

- ${{ each app in parameters.apps }}:
  - job: ProcApp
    steps:
      - task: Bash@3
        inputs:
          targetType: inline
          script: |
            echo ${{ app.application }}

Also, how to append the "app.application" to the name and display name of the job "ProcApp"?


Solution

  • /templates/process-apps-template.yml (Line: 16, Col: 3): Expected a sequence or mapping. Actual value '$(apps)'

    The cause of the issue is that the variable: $(apps) is Runtime variable(Expand at Runtime) and the template will read the variable at compile time. In this case, the Runtime variable value can not be read by the YAML template.

    I am afraid that your requirement can not be achieved in a single pipeline.

    how to append the "app.application" to the name and display name of the job "ProcApp"?

    To meet your requirement, you can split the two stages into two pipelines. One is used to run Initialize stage. It will set the Pipeline variable and trigger the other pipeline with the required array. The other pipeline is used to run ProcessApps stage and it will collect the array object.

    Here is an example:

    Pipeline One:

    stages:
      - stage: Initialize
        displayName: Initialize
        jobs:
        - job: AppsList
          steps:
          - task: Bash@3
            name: Applications
            inputs:
              targetType: inline
              script: |
       
                applications=(
                  '{"application": "app1", "environments": ["dev", "test", "production"]},'
                  '{"application": "app2", "environments": ["dev", "staging"]}'
                )
                echo "##vso[task.setvariable variable=apps;isOutput=true]${applications[@]}"
    
          - task: Bash@3
            inputs:
                targetType: 'inline'
                script: |
                  az pipelines run --id {PipelineID} --organization $(System.CollectionUri) --project {ProjectName} --parameters Passapps="[$(Applications.apps)]"
            env:
             AZURE_DEVOPS_EXT_PAT: $(PAT)
    

    Note: You can use the following format to set the variable value:

            applications=(
              '{"application": "app1", "environments": ["dev", "test", "production"]},'
              '{"application": "app2", "environments": ["dev", "staging"]}'
            )
    

    Pipeline Two:

    Main YAML:

    pool:
      vmImage: ubuntu-latest
    
    parameters:
    - name: Passapps
      type: object
      default: []
    
    stages:
    - stage: ProcessApps
      jobs:    
        - template: templates/process-apps-template.yml
          parameters:
            apps: ${{parameters.Passapps}}
    

    Template:

    parameters:
    - name: apps
      type: object
      default: []
    
    jobs:
    - job: PaceholderJob
    
    - ${{ each app in parameters.apps }}:
      - job: ${{ app.application }}
        steps:
          - task: Bash@3
            inputs:
              targetType: inline
              script: |
                echo ${{ app.application }}
    

    Result:

    enter image description here

    Here is the doc related to Azure DevOps CLI trigger Pipeline: az pipelines run