Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure Pipelines: Bulk approve of deployments to environments

Is there any way to approve runs via the CLI or the API (or anything else)? I'm looking for a way to bulk approve multiple runs from different pipelines but it's not available in the UI.

Let's say I have 100 pipelines that have a deployment job to a production environment. I would like to approve all awaiting for approval runs.

Currently, I cannot find something like it in the docs of the Azure DevOps REST API or the CLI.

The feature docs: https://docs.microsoft.com/en-us/azure/devops/pipelines/process/environments https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals

The following question is related but I'm looking for any way of solving it but not just via API: Approve a yaml pipeline deployment in Azure DevOps using REST api

like image 944
ebashmakov Avatar asked Feb 09 '20 03:02

ebashmakov


2 Answers

I was just searching for an answer for this regarding getting the approval id that you would need. In fact there is an undocumented API to approve an approval check.

This is as Merlin explain the following

https://dev.azure.com/{org}/{project}/_apis/pipelines/approvals/{approvalId}

The body has to look like this

[{
    "approvalId": "{approvalId}",
    "status": {approvalStatus},
    "comment": ""
}]

where {approvalStatus} is telling the API if you approved or not. You probly have to try, but I had a 4 as a status. I guess there are only 2 possibilities. Either for "approved" or "denied".

The question is now how you get the approval ID? I found it. You get it by using the timeline API of a classic build. The build API documentation says that you get it by the following

https://dev.azure.com/{organization}/{project}/_apis/build/builds/{buildId}?api-version=5.1

the build timeline you get in the response of the build run, but it has a pattern which is

https://dev.azure.com/{organization}/{project}/_apis/build/builds/{buildId}/Timeline?api-version=5.1

Besides a flat array container a parent / child rleationship from stage, phase, job and tasks, you can find within it something like the following:

{
  "records": [
    {
      "previousAttempts": [
        
      ],
      "id": "95f5837e-769d-5a92-9ecb-0e7edb3ac322",
      "parentId": "9e7965a8-d99d-5b8f-b47b-3ee7c58a5b1c",
      "type": "Checkpoint",
      "name": "Checkpoint",
      "startTime": "2020-08-14T13:44:03.05Z",
      "finishTime": null,
      "currentOperation": null,
      "percentComplete": null,
      "state": "inProgress",
      "result": null,
      "resultCode": null,
      "changeId": 73,
      "lastModified": "0001-01-01T00:00:00",
      "workerName": null,
      "details": null,
      "errorCount": 0,
      "warningCount": 0,
      "url": null,
      "log": null,
      "task": null,
      "attempt": 1,
      "identifier": "Checkpoint"
    },
    {
      "previousAttempts": [
        
      ],
      "id": "9e7965a8-d99d-5b8f-b47b-3ee7c58a5b1c",
      "parentId": null,
      "type": "Stage",
      "name": "Power Platform Test (orgf92be262)",
      "startTime": null,
      "finishTime": null,
      "currentOperation": null,
      "percentComplete": null,
      "state": "pending",
      "result": null,
      "resultCode": null,
      "changeId": 1,
      "lastModified": "0001-01-01T00:00:00",
      "workerName": null,
      "order": 2,
      "details": null,
      "errorCount": 0,
      "warningCount": 0,
      "url": null,
      "log": null,
      "task": null,
      "attempt": 1,
      "identifier": "Import_Test"
    },
    {
      "previousAttempts": [
        
      ],
      "id": "e54149c5-b5a7-4b82-8468-56ad493224b5",
      "parentId": "95f5837e-769d-5a92-9ecb-0e7edb3ac322",
      "type": "Checkpoint.Approval",
      "name": "Checkpoint.Approval",
      "startTime": "2020-08-14T13:44:03.02Z",
      "finishTime": null,
      "currentOperation": null,
      "percentComplete": null,
      "state": "inProgress",
      "result": null,
      "resultCode": null,
      "changeId": 72,
      "lastModified": "0001-01-01T00:00:00",
      "workerName": null,
      "details": null,
      "errorCount": 0,
      "warningCount": 0,
      "url": null,
      "log": null,
      "task": null,
      "attempt": 1,
      "identifier": "e54149c5-b5a7-4b82-8468-56ad493224b5"
    }
  ],
  "lastChangedBy": "00000002-0000-8888-8000-000000000000",
  "lastChangedOn": "2020-08-14T13:44:03.057Z",
  "id": "86fb4204-9c5e-4e72-bdb1-eefe230480ec",
  "changeId": 73,
  "url": "https://dev.azure.com/***"
}

below you can see a step that is called "Checkpoint.Approval". The id of that step IS the approval Id you need to approve everything. If you want to know from which stage the approval is, then you can follow up the parentIds until the parentId property is null. This will then be the stage.

With this you can successfully get the approval id and use it to approve with the said

like image 94
Sebastian Schütze Avatar answered Sep 29 '22 23:09

Sebastian Schütze


I added support to the latest version of the AzurePipelinesPS Powershell module to support bulk pipeline approvals.

Code snippet without using the AzurePipelinesPS sessions

$instance = 'https://dev.azure.com'
$collection = 'your_project'
$project = 'your_project'
$apiVersion = '5.1-preview'
$securePat = 'your_personal_access_token' | ConvertTo-SecureString -Force -AsPlainText 
Get-APPipelinePendingApprovalList -Instance $instance -Collection $collection -Project $project -PersonalAccessToken $securePat -ApiVersion $apiVersion | Out-GridView -Passthru | % { Update-APPipelineApproval -Instance $instance -Collection $collection -Project $project -PersonalAccessToken $securePat -ApiVersion $apiVersion -ApprovalId $PSitem.approvalId -status 'approved'}

Code snippet with AzurePipelinesPS sessions

$session = 'your_session'
Get-APPipelinePendingApprovalList $session | Out-GridView -Passthru | % { Update-APPipelineApproval $session -ApprovalId $PSitem.approvalId -status 'approved'} 

See the AzurePipelinesPS project page for details on secure session handling.

Function Definitions used in the code above

Get-APPipelinePendingApprovalList

  1. Loops through pipeline build runs with the status of 'notStarted' or 'inProgress' in a project. This build lookup supports filters like pipeline definition ids or a source branch name.
  2. For each build it then looks up the timeline record where the approval id, the stage name and the stage identifier are found.
  3. Optionally with the ExpandApproval switch it can expand each approval with details

The object returned from this function contains the following properties, the values have been mocked

pipelineDefinitionName : MyPipeline
pipelineDefinitionId   : 100
pipelineRunId          : 2001
pipelineUrl            : https://dev.azure.com/your_project/_build/results?
sourceBranch           : refs/heads/master
stageName              : Prod Deployment
stageIdentifier        : Prod_Deployment
approvalId             : xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx

Out-GridView

Displays data in a Grid View where the results can be filtered, ordered and selected.

%

The percent sign is shorthand for Foreach-Object

Update-APPipelineApproval

Updates the status of an approval to approved or rejected.

Credit

Thanks to Sebastian Schütze for cracking the timeline part!

like image 39
Dejulia489 Avatar answered Sep 29 '22 22:09

Dejulia489