Ejecución de etapa condicional en Azure DevOps Pipelines
Quiero que se ejecute una etapa en una canalización de Azure DevOps según el contenido de una variable establecida en una etapa anterior.
Aquí está mi canalización:
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"
...
La idea es saltarse el apply_dev
escenario, si el plan_dev
escenario no muestra cambios. El trasfondo es que tenemos aprobación manual para la implementación en la plan_dev
etapa que queremos omitir si no hay cambios que aprobar.
Desafortunadamente, esto no parece funcionar. No importa si la variable terraform_plan_exitcode
se establece con el valor esperado (2) o no, la apply_dev
etapa se salta.
Para la sintaxis, seguí la documentación aquí que dice:
stageDependencies.StageName.JobName.outputs['StepName.VariableName']
Respuestas
He visto este mismo problema. Necesita usar la variable de dependencias en lugar de 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"
El siguiente es un ejemplo más completo de algo que tengo trabajando con Terraform Plan + condicional 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 }}
@Marius tiene razón. Entonces esto funciona
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
Cuando refieres dependencias de etapa a etapa , tienes una sintaxis diferente
"dependencies": {
"<STAGE_NAME>" : {
"result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
"outputs": {
"jobName.stepName.variableName": "value"
}
},
"...": {
// another stage
}
}
Y cuando te refieres a un trabajo a otro en todas las etapas, tienes una sintaxis diferente
"stageDependencies": {
"<STAGE_NAME>" : {
"<JOB_NAME>": {
"result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
"outputs": {
"stepName.variableName": "value"
}
},
"...": {
// another job
}
},
"...": {
// another stage
}
}
Lo que es gracioso cuando tienes un trabajo a otro en una etapa, usamos la dependecies
sintaxis nuevamente
"dependencies": {
"<JOB_NAME>": {
"result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
"outputs": {
"stepName.variableName": "value1"
}
},
"...": {
// another job
}
}
Esto es un poco confuso y considérelo como
- cuando estás en alguna etapa de nivel, trabajo y te refieres al mismo nivel de un trabajo a otro o de una etapa a otra, tienes
dependencies
sintaxis - cuando desee referirse desde un nivel más profundo, como de un trabajo a otro, debe usar
stageDependencies
Lo que es gracioso, en el ejemplo anterior usé esto en el nivel del escenario:
variables:
varFromA: $[ stageDependencies.plan_dev.terraform_plan_dev.outputs['terraform_plan.terraform_plan_exitcode'] ]
pero esto se evalúa en tiempo de ejecución y se evalúa desde el trabajo, por lo que es correcto y se evalúa correctamente.
Espero que haya agregado un valor a la respuesta anterior.