NeuroAgent

Conditional Runtime Approval in Azure DevOps Pipelines

Discover effective solutions for conditional runtime approval in Azure DevOps pipelines. Overcome compile-time evaluation limitations with practical workarounds and best practices.

How to implement conditional run-time approval in Azure DevOps pipelines?

I need to configure a job in Azure DevOps that requires approval only when a runtime variable has a specific value. According to the documentation, approvals are evaluated at compile time, which means even if my condition returns false, the approval is still required to start the job, even if the job doesn’t perform any actions.

Is there a workaround to implement conditional run-time approvals in Azure DevOps pipelines?

NeuroAgent

Conditional run-time approval in Azure DevOps pipelines presents a significant challenge due to the way Azure Pipelines evaluates conditions. While there isn’t direct support for conditional approvals, several effective workarounds can achieve similar functionality.

Azure Pipelines evaluates conditions before pipeline execution, which means runtime variables cannot be used directly in approval conditions. The Microsoft Learn documentation explicitly states: “Conditions are evaluated to determine whether to start a stage, job, or step. Therefore, nothing computed during the runtime of a stage, job, or step is available to use within that same stage, job, or step.”

Here are the most effective workarounds for implementing conditional run-time approvals:


Contents


Understanding the Core Limitation

Azure Pipelines uses a two-phase evaluation system:

  1. Compile-time evaluation: Conditions are evaluated before execution to determine what should run
  2. Runtime evaluation: Variables and expressions are evaluated during execution

This separation means that variables set during runtime cannot influence decisions made at compile time, such as whether to require approval. As explained in the official documentation, “if you set a variable in a job using a runtime expression with $[ ] syntax, you can’t use that variable in conditions within that job.”


Workaround 1: Stage Dependency Variables

The most robust approach is to use stage dependency variables to pass runtime information between stages and make approval decisions based on those values.

yaml
stages:
- stage: evaluation_stage
  jobs:
  - job: determine_approval
    steps:
    - task: PowerShell@2
      name: set_approval_flag
      inputs:
        targetType: inline
        pwsh: true
        script: |
          $requireApproval = $true  # Logic to determine if approval is needed
          echo "##vso[task.setvariable variable=needApproval;isOutput=true]$requireApproval"

- stage: deployment_stage  
  dependsOn: evaluation_stage
  condition: eq(dependencies.evaluation_stage.outputs['determine_approval.set_approval_flag.needApproval'], 'true')
  jobs:
  - deployment: deploy_job
    environment: production
    strategy:
      runOnce:
        deploy:
          steps:
          - task: ManualIntervention@0
            name: manual_approval
            inputs:
              instructions: 'Please review and approve this deployment'

This approach works because:

  • The evaluation stage runs first and computes the approval requirement
  • The result is passed as an output variable
  • The deployment stage condition checks this output before deciding whether to run
  • If the condition is true, the approval gate is executed

According to Black Marble’s blog, “AgentLess job manual approvals are evaluated after the job condition, so do not suffer the same problem.”


Workaround 2: Parameter-Based Conditional Logic

Using parameters with runtime evaluation can provide conditional behavior, though with some limitations:

yaml
parameters:
- name: requireApproval
  type: boolean
  default: false

stages:
- stage: deployment
  condition: eq('${{ parameters.requireApproval }}', 'true')
  jobs:
  - deployment: deploy_job
    environment: production
    strategy:
      runOnce:
        deploy:
          steps:
          - task: ManualIntervention@0
            inputs:
              instructions: 'Manual approval required'

To make this work at runtime, you can use the Azure DevOps REST API or Azure CLI to set the parameter value:

bash
az pipelines run --parameters requireApproval=true --branch main

This approach is limited because parameters are typically set before execution, but it can work when combined with appropriate default values and external logic.


Workaround 3: AgentLess Job Manual Approvals

For deployment jobs, you can use AgentLess jobs with manual approvals that are evaluated after the job condition:

yaml
stages:
- stage: conditional_approval
  jobs:
  - deployment: deploy_with_approval
    environment: production
    pool: none  # AgentLess job
    strategy:
      runOnce:
        deploy:
          steps:
          - task: PowerShell@2
            name: check_conditions
            inputs:
              targetType: inline
              pwsh: true
              script: |
                # Runtime condition evaluation
                $shouldApprove = $true  # Your logic here
                if ($shouldApprove) {
                  echo "##vso[task.logissue type=warning]Manual approval required"
                  echo "##vso[task.complete result=SucceededWithIssues;]"
                } else {
                  echo "##vso[task.complete result=Succeeded;]"
                }
          - task: ManualIntervention@0
            condition: eq(dependencies['deploy_with_approval'].result, 'SucceededWithIssues')
            inputs:
              instructions: 'Please review and approve this deployment'

This approach leverages the fact that AgentLess jobs don’t suffer from the same condition evaluation limitations as regular jobs.


Workaround 4: Template-Based Conditional Stages

Using templates with conditional expressions allows for more flexible approval logic:

yaml
# templates/conditional-approval.yml
parameters:
- name: approvalRequired
  type: boolean
  default: false

stages:
- stage: deploy
  condition: eq('${{ parameters.approvalRequired }}', 'true')
  jobs:
  - deployment: deploy_job
    environment: production
    strategy:
      runOnce:
        deploy:
          steps:
          - task: ManualIntervention@0
            inputs:
              instructions: 'Manual approval required'

# main pipeline
stages:
- template: templates/conditional-approval.yml
  parameters:
    approvalRequired: true

This approach allows you to dynamically include or exclude approval stages based on runtime conditions.


Best Practices and Recommendations

Based on the research findings, here are the key recommendations:

  1. Use Stage Dependency Variables: This is the most reliable approach for conditional approvals
  2. Separate Evaluation Logic: Create dedicated stages/jobs to compute conditions before making approval decisions
  3. Leverage AgentLess Jobs: For deployment scenarios, AgentLess jobs with manual approvals work well
  4. Plan for Pipeline Architecture: Design your pipeline with conditional execution in mind from the beginning

The Microsoft Learn documentation emphasizes: “You need to assign a default value in your YAML file or when you run your pipeline. If you don’t assign a default value or set default to false, the first available value is used.”


Alternative Approaches

If the above workarounds don’t meet your needs, consider these alternatives:

External Approval Service

Integrate with an external approval service that can make decisions based on runtime conditions and trigger pipeline execution accordingly.

Multi-Pipeline Approach

Break your deployment into separate pipelines:

  1. A pipeline that evaluates conditions and sets appropriate variables
  2. A second pipeline that executes based on those variables and includes approval logic

Azure Policy Integration

Use Azure Policy to control deployment permissions based on conditions that can be evaluated at runtime.


Conclusion

While Azure DevOps doesn’t natively support conditional run-time approvals, several effective workarounds exist:

  1. Stage Dependency Variables - Most reliable approach for complex scenarios
  2. Parameter-Based Logic - Simpler but with limitations
  3. AgentLess Job Approvals - Ideal for deployment jobs
  4. Template-Based Conditional Stages - Flexible and reusable

The key is to separate condition evaluation from approval execution, using Azure Pipelines’ dependency system to pass runtime information between pipeline stages. As one Stack Overflow contributor noted, “While Shayki is correct that it is not supported - there is a workaround that I am currently using.”

For mission-critical scenarios, consider combining these approaches with external approval systems or breaking the process into multiple pipelines to achieve the desired conditional approval behavior.


Sources

  1. Pipeline conditions - Azure Pipelines | Microsoft Learn
  2. Using Azure DevOps Stage Dependency Variables with Conditional Stage and Job Execution | Black Marble
  3. Conditional Stage Execution in Azure DevOps Pipelines - Stack Overflow
  4. Conditional Approval gate in deployment jobs in azure pipelines - Stack Overflow
  5. Azure DevOps YAML conditional Stages, Jobs and Steps - CloudShift
  6. Expressions - Azure Pipelines | Microsoft Learn
  7. Conditional execution for stage - Azure DevOps - GeralexGR
  8. Use runtime and type-safe parameters - Azure Pipelines | Microsoft Learn