Eksekusi Tahap Bersyarat di Azure DevOps Pipelines

Dec 09 2020

Saya ingin tahapan dalam pipeline Azure DevOps dijalankan bergantung pada konten set variabel di tahap sebelumnya.

Ini pipeline saya:

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"
      ...

Idenya adalah melewati apply_devpanggung, jika plan_devpanggung tidak menunjukkan perubahan. Latar belakangnya adalah kami memiliki persetujuan manual untuk penerapan dalam plan_devtahap yang ingin kami lewati jika tidak ada perubahan untuk disetujui.

Sayangnya ini sepertinya tidak berhasil. Tidak peduli apakah variabel terraform_plan_exitcodedisetel dengan nilai yang diharapkan (2) atau tidak, apply_devtahapan dilewati.

Untuk sintaks, saya mengikuti dokumentasi di sini yang mengatakan:

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

Jawaban

1 MariusSolbakkenMellum Dec 09 2020 at 22:00

Saya telah melihat masalah yang sama ini. Anda perlu menggunakan variabel dependensi sebagai ganti 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"

Berikut ini adalah contoh yang lebih lengkap dari sesuatu yang saya kerjakan dengan Terraform Plan + Conditional Apply:


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 }}
1 KrzysztofMadej Dec 09 2020 at 22:32

@Marius benar. Jadi ini berhasil

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

Saat Anda merujuk dependensi tahap ke tahap, Anda memiliki sintaks yang berbeda

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

Dan ketika Anda merujuk ke pekerjaan ke pekerjaan di seluruh tahap, Anda memiliki sintaks yang berbeda

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

Yang lucu ketika Anda memiliki pekerjaan untuk pekerjaan dalam satu tahap kami menggunakan dependeciessintaks lagi

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

Ini agak membingungkan dan menganggap ini sebagai

  • ketika Anda berada di beberapa level level, pekerjaan dan merujuk ke level yang sama dari satu pekerjaan ke pekerjaan atau dari tahap ke tahap Anda memiliki dependenciessintaks
  • Bila Anda ingin mereferensikan dari level yang lebih dalam seperti dari satu pekerjaan ke tahap lainnya sebaiknya Anda gunakan stageDependencies

Yang lucu, dalam contoh di atas saya menggunakan ini di level panggung:

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

tetapi ini dievaluasi pada waktu proses dan dievaluasi dari pekerjaan, sehingga benar dan dievaluasi dengan benar.

Saya berharap ini menambah nilai pada jawaban sebelumnya.