Azure Devops REST API Automation
, automation | azure
I have worked on a deployment solution by use of Azure Devops and wanted a generic way to automate start of Azure Devops pipelines from a production server. An Azure Devops deployment pipeline was setup to deliver the build artifact and a Windows Scheduled Task was setup on the production server. The execution time of the task was beyond the timeout of a Azure Devops pipeline and hence needed a way to decouple deployment from task execution in order to obtain information when the task was done.

The solution was two pipelines in Azure Devops. One pipeline for deployment and one pipeline for post trigger after the task execution was done. The deployment pipeline was either triggered manually or by build pipeline dependency. The post trigger pipeline was started by the following Python script using the Azure Devops REST API:
import os
import sys
import base64
from pathlib import Path
import requests
from requests.structures import CaseInsensitiveDict

sourceVersion = ""
if len( sys.argv ) > 1:
  sourceVersion = sys.argv[1]

branch = "refs/heads/master"
if len( sys.argv ) > 2:
  branch = sys.argv[2]

pipeline_definition_id = 74
if len( sys.argv ) > 3:
  pipeline_definition_id = int( sys.argv[3] )

token_file = "build_token"
token_path = os.path.join( Path.home(), token_file )
with open( str( token_path ) ) as f:
  token = f.readline().rstrip()
  url = "https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=6.0"

  userpass = ':' + token
  encoded_u = base64.b64encode(userpass.encode()).decode()

  headers = CaseInsensitiveDict()
  headers["Authorization"] = "Basic %s" % encoded_u
  headers["Content-Type"] = "application/json"
  headers["Content-Length"] = "0"

  payload = '{ "definition": { "id": %d }, "sourceBranch": "%s", "sourceVersion": "%s", "reason": 1, "demands": [] }' % ( pipeline_definition_id, branch, sourceVersion )
  resp = requests.post( url, headers=headers, data=payload )

  print( payload )
  print( resp.status_code )
In short the script calls the REST API backend requesting a specific Azure Devops pipeline to be started. This is possible if 'sourceVersion', 'branch', and 'pipeline_definition_id' are defined. The pipeline definition id can be found in the Azure Devops Pipelines URL when an already created pipeline is selected. Additional a build token ( Azure Devops PAT ) is required and must be placed in the home folder of the execution user.

At build time save the Azure Devops source version and branch to file and store the information in the build artifact:
Set-Content -Path git-fullhash -Value "$(Build.SourceVersion)"
Set-Content -Path git-branch -Value "$(Build.SourceBranch)"
At execution time call the Python script on the production server:
$gitfullhash = Get-Content -Path "git-fullhash" -TotalCount 1
$gitbranch = Get-Content -Path "git-branch" -TotalCount 1
& python.exe post_trigger.py $gitfullhash $gitbranch $post_pipeline_id 2>&1 > post_trigger_log.txt
Adding this to the final step in task execution will trigger the specified Azure Devops pipeline which can do whatever is needed such as cleanup, save files and/or report errors. I choose to add the 'sourceVersion', 'branch', and 'pipeline_definition_id' to the build artifact as files allowing the script to be called from any deployment and still have the correct artifact information. Change this with respect to your deployment needs and workflow. The script has not been tested on a Linux distribution. Change accordingly if you want to use it on Linux.

Links