Search code examples
azure-devopsazure-pipelines

nested pipelines error - unexpected value 'jobs'


I have one main pipeline pipeline.yml that calls a template called firewall.yml which finally calls the template artifact.deploy.yml.

The validation fails on the 3rd pipeline (artifact.deploy.yml) throwing errors like: Unexpected value 'jobs' and Unexpected value 'stages' when I try using stages. I've tried all kinds of alignments, jobs and stages but to no avail. All input is greatly appreciated.

pipeline.yml:

  - stage: Firewall
    variables: 
      RCGFileNames: $[ stageDependencies.FirewallPreparation.ParamFiles.outputs['PS.RCGParamFileNames'] ]
    displayName: "Azure Firewall - Deployment"
    dependsOn:
      - Network
      - IPGroups
      - RemoveLocks
      - FirewallPreparation
    jobs:
      - template: ./firewall.yml
        parameters:
          vmImage: "$(vmImage)"
          poolName: "$(poolName)"
          serviceConnection01: "$(serviceConnection01)"

firewall.yml:

parameters:
  vmImage: $(vmImage)
  poolName: $(poolName)
  serviceConnection01: "$(serviceConnection01)"
  
jobs:

  ## Azure Firewall base Policy
  - job: basePolicy
    displayName: "Deploy firewall basepolicy p weu 01"
    steps:
      - template: ./artifact.deploy.yml
        parameters:
          moduleName: "$(fwpModuleName)"
          moduleVersion: "$(fwpModuleVersion)"
          parameterFilePath: "../source/parameters/firewall-policies/afwp-basepolicy-p-weu-01.parameters.json"
          vmImage: "${{ parameters.vmImage }}"
          poolName: "${{ parameters.poolName }}"
          serviceConnection: "${{ parameters.serviceConnection01 }}"
          enabled: true

artifact.deploy.yml:

parameters:
  moduleName: ""
  moduleVersion: ""
  parameterFilePath: ""
  dependsOn: []
  timeoutInMinutes: 60
  artifactFeedPath: "$(artifactFeedPath)"
  serviceConnection: "$(serviceConnection)"
  vmImage: "$(vmImage)"
  poolName: "$(poolName)"
  location: "$(location)"
  resourceGroupName: "$(resourceGroupName01)"
  managementGroupId: "$(managementGroupId)"
  displayName: "Deploy module"
  enabled: true

jobs:
  - job: DeployModule
    displayName:  deploy ${{ parameters.moduleName }} ${{ parameters.moduleVersion }}
    steps:
      - script: |
          oras pull coolestacr.azurecr.io/bicep/modules/$(parameters.moduleName):$(parameters.moduleVersion)
          echo '{
            "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {
              "resourceGroupName": {
                "value": "$(parameters.resourceGroupName)"
              },
              "tags": {
                "value": {
                  "cooltags": "aintIt?" 
                }
              }
            }
          }' > "$(Build.SourcesDirectory)"/parameters.json
        displayName: download acr module

Solution

  • Please refer to the YAML pipeline schema. A pipeline has the following structure:

    • one or more stages
    • each stage contains one or more jobs
    • each job has one or more steps

    You can write templates for stages, jobs, steps and variables and you can substitute each of these elements with a corresponding template.

    In your scenario, you're trying to nest a job inside a list of steps, which isn't valid.

    Two options:

    1. You could modify your firewall.yml by replacing your basePolicy job with a reference to your artifact.deploy.yml job template.

      # firewall.yml
      parameters:
        ...
      
      jobs:
      - template: ./artifact.deploy.yml
        parameters:
          moduleName: "$(fwpModuleName)"
          moduleVersion: "$(fwpModuleVersion)"
          parameterFilePath: "../source/parameters/firewall-policies/afwp-basepolicy-p-weu-01.parameters.json"
          vmImage: "${{ parameters.vmImage }}"
          poolName: "${{ parameters.poolName }}"
          serviceConnection: "${{ parameters.serviceConnection01 }}"
          enabled: true
      
      
    2. Or, change artifact.deploy.yml from a job template into a step template.

      # artifact.deploy.yml
      parameters:
        ...
      
      steps: # <-- change "jobs" to "steps"
      - script: |
        ...