Условное выполнение этапа в Azure DevOps 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"
...
Идея состоит в том, чтобы пропустить apply_dev
этап, если plan_dev
этап не показывает изменений. Предыстория заключается в том, что у нас есть ручное утверждение развертывания на plan_dev
этапе, который мы хотим пропустить, если нет изменений, которые нужно утверждать.
К сожалению, это не работает. Независимо от того, установлено ли для переменной terraform_plan_exitcode
ожидаемое значение (2) или нет, apply_dev
этап пропускается.
Что касается синтаксиса, я следовал приведенной здесь документации, в которой говорится:
stageDependencies.StageName.JobName.outputs['StepName.VariableName']
Ответы
Я видел ту же проблему. Вам нужно использовать переменную 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 }}
@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
Когда вы относите стадию к зависимостям стадии, у вас другой синтаксис
"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
}
}
Это немного сбивает с толку, и рассмотрите это как
- когда вы находитесь на каком-то этапе уровня, работа и ссылка на один и тот же уровень от работы к работе или от этапа к этапу у вас есть
dependencies
синтаксис - когда вы хотите переходить с более глубокого уровня, например, с работы на этап, вы должны использовать
stageDependencies
Что забавно, в приведенном выше примере я использовал это на уровне сцены:
variables:
varFromA: $[ stageDependencies.plan_dev.terraform_plan_dev.outputs['terraform_plan.terraform_plan_exitcode'] ]
но это оценивается во время выполнения и оценивается из задания, поэтому он правильный и оценивается правильно.
Надеюсь, это добавило ценности предыдущему ответу.