mark
mark

Reputation: 62836

Is it possible to change the deployment environment based on an output variable computed in the previous stage?

Here is a code snippet:

stages:
  - stage: Apply
    dependsOn: Plan
    variables:
      OVERRIDE_ADO_ENVIRONMENT: $[ dependencies.Plan.outputs['Plan.IsTerraformPlanEmpty.OVERRIDE_ADO_ENVIRONMENT'] ]
    condition: and(succeeded(), ${{ parameters.terraform_apply }})
    jobs:
      - deployment: Apply
        environment: ${{ coalesce(variables.OVERRIDE_ADO_ENVIRONMENT, parameters.ado_environment) }}
        strategy:
          runOnce:
            deploy:
              steps:
                - template: start.yaml

                - template: terraform_init.yaml
                  parameters:

I know the build variable OVERRIDE_ADO_ENVIRONMENT is declared correctly, because I can use it in the condition to skip the Apply stage completely.

However, this is incorrect. Even if the plan is empty, there could be a change in the terraform output variables. Therefore I must run the Apply logic always. However, there is no need for approvals in this case.

Therefore I would like to switch the environment to the one in the OVERRIDE_ADO_ENVIRONMENT build variable which is a special environment with no approvals.

However, trying to run this pipeline produces the following error message:

Job Apply: Environment $[ dependencies could not be found. The environment does not exist or has not been authorized for use.

From which I conclude we cannot use a build variable, albeit computed in a previous stage.

The question is - what is the least painful way to implement this logic? If at all possible.

Edit 1

I tried an approach where I create two stages with a condition that is affected by the output variable from the previous stage. However, I found out that:

  1. The condition must be on the stage, not deployment job. Otherwise, the environment is applied even if the deployment job's condition disables it.
  2. However, the condition on the stage does not see the build variables shared at the same level. Thus the condition is always false.

Here is my attempt to use this approach

parameters:
  - name: terraform_apply
    type: boolean
  - name: ado_environment
  - name: working_directory
  - name: application
    default: terraform
  - name: apply_stages
    type: object
    default:
      - name: ApplyNonEmptyPlan
        displayName: Apply Non Empty Plan
        tf_plan_tag: TF_NON_EMPTY_PLAN
      - name: ApplyEmptyPlan
        displayName: Apply Empty Plan
        tf_plan_tag: TF_EMPTY_PLAN
        ado_environment: Empty TF Plan

stages:
  - ${{ each apply_stage in parameters.apply_stages }}:
    - stage: ${{ apply_stage.name }}
      displayName: ${{ apply_stage.displayName }}
      dependsOn: Plan
      variables:
        TF_PLAN_TAG: $[ stageDependencies.Plan.Plan.outputs['IS_TERRAFORM_PLAN_EMPTY.TF_PLAN_TAG'] ]
      condition: and(succeeded(), ${{ parameters.terraform_apply }}, eq(variables['TF_PLAN_TAG'], '${{ apply_stage.tf_plan_tag }}'))
      jobs:
        - deployment: ${{ apply_stage.name }}
          environment: ${{ coalesce(apply_stage.ado_environment, parameters.ado_environment) }}
          strategy:
            runOnce:
              deploy:
                steps:
                  - template: start.yaml

Upvotes: 0

Views: 452

Answers (1)

wade zhou - MSFT
wade zhou - MSFT

Reputation: 8310

Environment creation happens at compile time before run time. It doesn't support dynamic environment name. Hence, in the code snippet shared below, each element in coalesce should be known(or hardcode) before you run the pipeline, it cannot depends on the output calculated from previous stage.

environment: ${{ coalesce(variables.OVERRIDE_ADO_ENVIRONMENT, parameters.ado_environment) }}

and

environment: ${{ coalesce(apply_stage.ado_environment, parameters.ado_environment) }}

Upvotes: 0

Related Questions