31

How do you add a manual intervention step into a multi-stage Azure Devops YAML pipeline?

In jenkins you can do some thing like:

stage ('approve-prod') {
    steps {
        input "Approve deployment to production?"
    }
}

I am looking for the equivalent in Azure Devops YAML.

Note: this is for the newly released multi-stage Azure Devops pipelines, not the old style release pipelines. Related announcement here https://devblogs.microsoft.com/devops/whats-new-with-azure-pipelines/

Dominique
  • 16,450
  • 15
  • 56
  • 112
cfbd
  • 980
  • 2
  • 9
  • 21

4 Answers4

33

Microsoft have now made available a brand new official Manual Validation task that allows for manual intervention to be added into a YAML pipeline.

Quick example of how to use this task is as follows:

  jobs:  
  - job: waitForValidation
    displayName: Wait for external validation  
    pool: server    
    timeoutInMinutes: 4320 # job times out in 3 days
    steps:   
    - task: ManualValidation@0
      timeoutInMinutes: 1440 # task times out in 1 day
      inputs:
        notifyUsers: |
          test@test.com
          example@example.com
        instructions: 'Please validate the build configuration and resume'
        onTimeout: 'resume'

Some key constraints to be aware of:

  • This task is only supported in YAML pipelines
  • Can be used only in an agentless job of a YAML pipeline.
Adrian Sanguineti
  • 2,455
  • 1
  • 27
  • 29
user14786410
  • 346
  • 3
  • 2
  • 2
    And what exactly does your **another option**? Please explain the code, so other people can benefit more from your answer then just code without explanation. – SwissCodeMen Dec 08 '20 at 11:52
  • 1
    This code is straight from the link provided; however, what really is required is the task section. This will create task will wait until someone goes into the job and hits approve. If is almost identical to the existing Manual intervention task in classic pipelines. The timeout is set in minutes and is optional; however, when expired it will either resume or be set to 'reject' which will kill the job – DreadedFrost Dec 29 '20 at 14:26
  • 1
    Downside to this is that `notifyUsers` does not restrict who can approve the validation step, it is just a list of users who will get an email notification, but anyone with a certain permissions to the pipeline will be able to "approve" the step, which might not be desirable. – Aurimas Stands with Ukraine Oct 14 '21 at 05:58
  • 1
    Users with 'Queue builds' permission on the pipeline can resume or reject the run of a Manual Intervention. Set it from security. – subho Aug 17 '22 at 10:07
15

Azure DevOps/Pipelines now has a feature called Environments which supports approvals. https://learn.microsoft.com/en-us/azure/devops/pipelines/process/environments?view=azure-devops#approvals

We are using them as a workaround. Basically we have specified two environments ApprovalNotRequired and ApprovalRequired in Azure DevOps. On the latter we have specified who can approve deployments. Then in the pipeline we reference the environment like this.

- stage: 'Approval not required'
  jobs:
  - deployment: 'MyDeployment'
    displayName: MyDeployment
    environment: 'ApprovalNotRequired'
    strategy:
      runOnce:
        deploy:
          # whatever

- stage: 'Approval required'
  jobs:
  - deployment: 'MyDeployment2'
    displayName: MyDeployment2
    environment: 'ApprovalRequired'
    strategy:
      runOnce:
        deploy:
          # whatever

The first stage will run without interference and the second will pause until it's approved.

Alert101
  • 166
  • 1
  • 4
  • 3
    as of 20220804 this is the current way to define Approvals and Checks by using an Environment ... goto azure DevOps -> project -> pipelines -> Environments -> create a new env ( choose None and not aks or vm ) -> then click on hamburger menu just to right of Add resource -> Approvals and checks – Scott Stensland Aug 04 '22 at 22:01
9

This doesn't appear to be available yet, but there is a GitHub Issue tracking this: https://github.com/MicrosoftDocs/vsts-docs/issues/4241

From the issue:

So what I heard from the product team is that this "approval per stage" policy isn't available yet but is on their backlog.

There is also a Roadmap work item tracking it: https://dev.azure.com/mseng/AzureDevOpsRoadmap/_workitems/edit/1510336/

Alex Herring
  • 91
  • 1
  • 2
  • 1
    As far as I can see the ticket is closed. I'm not clear on whether this is actually fixed in a practical way. Anyone have a working solution? – Dave Potts Feb 14 '20 at 13:11
  • 1
    "Approvals are now available in all organizations if you use environments in a YAML pipeline. See https://learn.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops for more information. " See discussion section here: https://dev.azure.com/mseng/AzureDevOpsRoadmap/_workitems/edit/1510336/ – Francisco Vilches May 21 '20 at 10:07
  • 1
    Unfortunately the only way its supported is by modelling manual steps as deployment tasks on resources with approvals, which is pretty inflexible. – Adrian Baker Oct 08 '20 at 01:15
3

Because there's a long time since Microsoft is ignoring this, and because this is a critical missing functionality , I will add an workaround here (for the moment, it's working only to ignore the entire step for all machines in case of a multi stage YAML but I think this can be solved also, but I am not looking into it for the moment).

Unfortunately, there is a task that needs to be added before each task. This can be solved also by iterative insertion (https://learn.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops).

Shortly, in order to be able to ignore a specific task:

  • T1 is checking the build run for "IgnoreStep"tag. If found, it will set IgnoreStep variable to true and remove the tag
  • T2 is running only if previous IgnoreStep is on false When something is failing and I want to ignore the step, I will add "IgnoreStep" tag for the run and retry.

For adding tags, I am using the API because there is no task to do it yet. For request details, F21 in Chrome and check what it sent to server after you will add a tag, and export the request to power shell.

Below you have the YAML:

trigger: none

jobs:
  - deployment: Dev
    environment: 
        name: Dev
        resourceType: virtualMachine
        tags: online
    strategy:                  
          runOnce:
            deploy:
              steps:
              - task: PowerShell@2
                displayName: CheckIfWeShouldIgnoreStep
                name: CheckIfWeShouldIgnoreStep
                inputs:
                  targetType: 'inline'
                  script: |
                    $user = "user"
                    $pass= "pass"
                    $secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
                    $credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)

                    $response = Invoke-RestMethod -Uri "https://server/tfs/collection/projectId/_apis/build/builds/$(Build.BuildId)/tags" `
                      -Method "GET" `
                      -Headers @{
                        "accept"="application/json;api-version=6.0;excludeUrls=true;enumsAsNumbers=true;msDateFormat=true;noArrayWrap=true"
                      } `
                      -ContentType "application/json" `
                      -Credential $credential -UseBasicParsing

                      Write-Host "##vso[task.setvariable variable=IgnoreStep]false"

                      Write-Host "Tags: $response"
                      foreach($tag in $response)
                      {
                          if($tag -eq "IgnoreStep")
                          {
                            Write-Host "##vso[task.setvariable variable=IgnoreStep]true"

                            
                            Invoke-RestMethod -Uri "https://server/tfs/collection/projectId/_apis/build/builds/$(Build.BuildId)/tags/IgnoreStep" `
                                -Method "DELETE" `
                                -Headers @{
                                  "accept"="application/json;api-version=6.0;excludeUrls=true;enumsAsNumbers=true;msDateFormat=true;noArrayWrap=true"
                                }`
                            -Credential $credential -UseBasicParsing
                          }
                      }
              - task: PowerShell@2
                displayName: Throw Error
                condition: eq (variables.IgnoreStep, false)
                inputs:
                  targetType: 'inline'
                  script: |   
                    throw "Error"