Azure DevOps 管道中的条件阶段执行

Mic*_*ihs 12 azure terraform azure-devops azure-pipelines

我希望根据前一阶段中设置的变量的内容来执行 Azure DevOps 管道中的一个阶段。

这是我的管道:

stages:
  - stage: plan_dev
    jobs:
    - job: terraform_plan_dev
      steps:
      - bash: echo '##vso[task.setvariable variable=terraform_plan_exitcode;isOutput=true]2'
        name: terraform_plan

  - stage: apply_dev
    dependsOn: plan_dev
    condition: eq(stageDependencies.plan_dev.terraform_plan_dev.outputs['terraform_plan.terraform_plan_exitcode'], '2')
    jobs:
    - deployment: "apply_dev"
      ...
Run Code Online (Sandbox Code Playgroud)

apply_dev如果阶段plan_dev没有显示任何变化,我们的想法是跳过该阶段。背景是,我们在该阶段需要手动批准部署,plan_dev如果没有需要批准的更改,我们希望跳过该阶段。

不幸的是这似乎不起作用。无论变量是否terraform_plan_exitcode设置为期望值(2),都会apply_dev跳过该阶段。

对于语法,我遵循此处的文档

stageDependencies.StageName.JobName.outputs['StepName.VariableName']
Run Code Online (Sandbox Code Playgroud)

小智 17

我见过同样的问题。您需要使用依赖项变量而不是 stageDependencies:

stages:
- stage: plan_dev
jobs:
- job: terraform_plan_dev
  steps:
  - bash: echo '##vso[task.setvariable variable=terraform_plan_exitcode;isOutput=true]2'
    name: terraform_plan

- stage: apply_dev
dependsOn: plan_dev
condition: eq(dependencies.plan_dev.outputs['terraform_plan_dev.terraform_plan.terraform_plan_exitcode'], '2')
jobs:
- deployment: "apply_dev"
Run Code Online (Sandbox Code Playgroud)

以下是我使用 Terraform Plan + 条件应用的更完整示例:


stages: 
  - stage: Build_zip_plan
    displayName: Build portal, zip files and terraform plan
    jobs:
    - job: Build_portal_zip_files_terraform_plan
      pool:
        vmImage: 'ubuntu-latest'
      steps:
        - task: Cache@2
          displayName: 'Register TF cache'
          inputs:
            key: terraform | $(Agent.OS) | $(Build.BuildNumber) | $(Build.BuildId) | $(Build.SourceVersion) | $(prefix)
            path: ${{ parameters.tfExecutionDir }}

        - task: TerraformInstaller@0
          displayName: 'Install Terraform'
          inputs:
            terraformVersion: ${{ parameters.tfVersion }}

        - task: TerraformTaskV1@0
          displayName: 'Terraform Init'
          inputs:
            provider: 'azurerm'
            command: 'init'
            workingDirectory: ${{ parameters.tfExecutionDir }}
            backendServiceArm: ${{ parameters.tfStateServiceConnection }}
            backendAzureRmResourceGroupName: ${{ parameters.tfStateResourceGroup }}
            backendAzureRmStorageAccountName: ${{ parameters.tfStateStorageAccount }}
            backendAzureRmContainerName: ${{ parameters.tfStateStorageContainer }}
            backendAzureRmKey: '$(prefix)-$(environment).tfstate'

        - task: TerraformTaskV1@0
          displayName: 'Terraform Plan'
          inputs:
            provider: 'azurerm'
            command: 'plan'
            commandOptions: '-input=false -out=deployment.tfplan -var="environment=$(environment)" -var="prefix=$(prefix)" -var="tenant=$(tenant)" -var="servicenow={username=\"$(servicenowusername)\",instance=\"$(servicenowinstance)\",password=\"$(servicenowpassword)\",assignmentgroup=\"$(servicenowassignmentgroup)\",company=\"$(servicenowcompany)\"}" -var="clientid=$(clientid)" -var="username=$(username)" -var="password=$(password)" -var="clientsecret=$(clientsecret)" -var="mcasapitoken=$(mcasapitoken)" -var="portaltenantid=$(portaltenantid)" -var="portalclientid=$(portalclientid)" -var="customerdisplayname=$(customerdisplayname)" -var="reportonlymode=$(reportonlymode)"'
            workingDirectory: ${{ parameters.tfExecutionDir }}
            environmentServiceNameAzureRM: ${{ parameters.tfServiceConnection }}

        - task: PowerShell@2
          displayName: 'Check Terraform plan'
          name: "Check_Terraform_Plan"
          inputs:
            filePath: '$(Build.SourcesDirectory)/Pipelines/Invoke-CheckTerraformPlan.ps1'
            arguments: '-TfPlan ''${{ parameters.tfExecutionDir }}/deployment.tfplan'''
            pwsh: true
  

  - stage:
    dependsOn: Build_zip_plan
    displayName: Terraform apply
    condition: eq(dependencies.Build_zip_plan.outputs['Build_portal_zip_files_terraform_plan.Check_Terraform_Plan.TFChangesPending'], 'yes')
    jobs:
    - deployment: DeployHub
      displayName: Apply
      pool:
        vmImage: 'ubuntu-latest'
      environment: '$(prefix)'
      strategy:
        runOnce:
          deploy:
            steps:
            - checkout: self

            - task: Cache@2
              displayName: 'Get Cache for TF Artifact'
              inputs:
                key: terraform | $(Agent.OS) | $(Build.BuildNumber) | $(Build.BuildId) | $(Build.SourceVersion) | $(prefix)
                path: ${{ parameters.tfExecutionDir }}
                
            - task: TerraformInstaller@0
              displayName: 'Install Terraform'
              inputs:
                terraformVersion: ${{ parameters.tfVersion }}

            - task: TerraformTaskV1@0
              displayName: 'Terraform Apply'
              inputs:
                provider: 'azurerm'
                command: 'apply'
                commandOptions: 'deployment.tfplan'
                workingDirectory: ${{ parameters.tfExecutionDir }}
                environmentServiceNameAzureRM: ${{ parameters.tfServiceConnection }}
Run Code Online (Sandbox Code Playgroud)

  • 我在找到答案之前为此奋斗了一天,似乎您可以使用 stageDependency 提取到一个变量中以在作业中使用,但在条件中它必须来自依赖项。难道你不喜欢一致的语法吗? (3认同)

Krz*_*tof 9

@马吕斯是正确的。所以这有效

stages:
  - stage: plan_dev
    jobs:
    - job: terraform_plan_dev
      steps:
      - bash: echo '##vso[task.setvariable variable=terraform_plan_exitcode;isOutput=true]2'
        name: terraform_plan

  - stage: apply_dev
    dependsOn: plan_dev
    variables:
      varFromA: $[ stageDependencies.plan_dev.terraform_plan_dev.outputs['terraform_plan.terraform_plan_exitcode'] ]
    condition: eq(dependencies.plan_dev.outputs['terraform_plan_dev.terraform_plan.terraform_plan_exitcode'], 2)
    jobs:
    - job: apply_dev
      steps:
      - bash: echo 'apply $(varFromA)'
        name: terraform_apply
Run Code Online (Sandbox Code Playgroud)

当您引用阶段之间的依赖关系时,您有不同的语法

"dependencies": {
  "<STAGE_NAME>" : {
    "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
    "outputs": {
        "jobName.stepName.variableName": "value"
    }
  },
  "...": {
    // another stage
  }
}
Run Code Online (Sandbox Code Playgroud)

当你跨阶段引用工作时,你有不同的语法

"stageDependencies": {
  "<STAGE_NAME>" : {
    "<JOB_NAME>": {
      "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
      "outputs": {
          "stepName.variableName": "value"
      }
    },
    "...": {
      // another job
    }
  },
  "...": {
    // another stage
  }
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,当你在一个阶段有一项又一项工作时,我们dependecies再次使用语法

"dependencies": {
  "<JOB_NAME>": {
    "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
    "outputs": {
      "stepName.variableName": "value1"
    }
  },
  "...": {
    // another job
  }
}
Run Code Online (Sandbox Code Playgroud)

这有点令人困惑,并将其视为

  • 当您处于某个级别的阶段、工作并从工作到工作或从阶段到阶段引用相同级别时,您就有dependencies语法
  • 当您想从更深层次(例如从工作到阶段)引用时,您应该使用stageDependencies

有趣的是,在上面的例子中我在舞台级别使用了这个:

variables:
      varFromA: $[ stageDependencies.plan_dev.terraform_plan_dev.outputs['terraform_plan.terraform_plan_exitcode'] ]
Run Code Online (Sandbox Code Playgroud)

但这是在运行时评估并从作业中评估的,因此它是正确的并且评估正确。

我希望它能为之前的答案增加价值。