【问题标题】:Conditional Stage Execution in Azure DevOps PipelinesAzure DevOps Pipelines 中的条件阶段执行
【发布时间】:2021-03-20 23:16:45
【问题描述】:

我希望 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"
      ...

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

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

对于语法,我遵循documentation here 说:

stageDependencies.StageName.JobName.outputs['StepName.VariableName']

【问题讨论】:

    标签: azure azure-devops azure-pipelines


    【解决方案1】:

    我也看到过同样的问题。您需要使用 dependencies 变量而不是 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"
    

    以下是我使用 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 }}
    

    【讨论】:

    • 在找到答案之前我已经为此奋斗了一天,似乎您可以使用 stageDependencies 提取到用于作业的变量中,但在条件下它必须来自依赖项。你不喜欢一致的语法吗??
    【解决方案2】:

    @Marius 是正确的。所以这行得通

    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
    

    当你引用 stage to stage dependencies 时,你有不同的语法

    "dependencies": {
      "<STAGE_NAME>" : {
        "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
        "outputs": {
            "jobName.stepName.variableName": "value"
        }
      },
      "...": {
        // another stage
      }
    }
    

    当您跨阶段引用作业时,您有不同的语法

    "stageDependencies": {
      "<STAGE_NAME>" : {
        "<JOB_NAME>": {
          "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
          "outputs": {
              "stepName.variableName": "value"
          }
        },
        "...": {
          // another job
        }
      },
      "...": {
        // another stage
      }
    }
    

    当你在一个阶段有工作要做工作时,我们再次使用dependecies 语法有什么好笑的

    "dependencies": {
      "<JOB_NAME>": {
        "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
        "outputs": {
          "stepName.variableName": "value1"
        }
      },
      "...": {
        // another job
      }
    }
    

    这有点令人困惑,并认为 this in this as

    • 当您在某个级别的阶段、工作和从工作到工作或从一个阶段到另一个阶段引用同一级别时,您有 dependencies 语法
    • 当您想从更深层次(例如从工作到阶段)进行参考时,您应该使用stageDependencies

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

    variables:
          varFromA: $[ stageDependencies.plan_dev.terraform_plan_dev.outputs['terraform_plan.terraform_plan_exitcode'] ]
    

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

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

    【讨论】:

    • 用于调试:如何查看您在答案中显示的 JSON 输出?有没有机会让它可见?
    • 没有。这是模式,至少我不知道如何打印出这样的上下文。听起来绝对是非常有用的东西。但我们就在我们所在的地方。
    猜你喜欢
    • 2020-10-22
    • 1970-01-01
    • 2021-09-12
    • 1970-01-01
    • 2021-01-15
    • 2020-08-13
    • 2021-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多