How to trigger build pipeline from Azure Automation Runbook

Introduction

I am writing this post based on my last weeks new learning. I learnt to trigger Azure DevOps build pipeline form Azure Automation runbook.

In my previous post, I have explained step by step approach to create azure automation account and runbook. I have also explained how to reference Azure Repos and GitHub repository to automation account in order to synchronize and publish runbook as soon as developer commit code to source repository.

Pre-requisite

 Before starting to write runbook make sure you have,

  1. Azure Account and Subscription
  2. Azure Automation Account
  3. Azure DevOps Account with handy Personal Access Token(PAT) 

Let’s Get Started

1. Create Azure DevOps Repo and Pipeline

1.1.Create empty git repository say something like azure-devops-runbook-pipelines

1.2 Setup build Pipeline as a Code

Navigate to Azure DevOps project => Repos => Pipeline => New Pipeline => Azure Repos Git(Yaml) => Select repository created in step 1.1 => Starter pipeline => replace below code to azure-pipelines.yml file

trigger:
- master

variables:
  MyPipelineVaiable: "MyPipelineVaiable"

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: PowerShell@2
  displayName: 'Print Pipeline Variables'
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "--------------------------------------"
      Write-Host "Azure DevOps Pipeline Variables"
      Write-Host "--------------------------------------"
      Write-Host "Organization Name: $(OrganizationName)"
      Write-Host "Project Name: $(ProjectName)"
      Write-Host "Parameter1: $(Parameter1)"
      Write-Host "Parameter2: $(Parameter2)"
      Write-Host "MyPipelineVaiable: $(MyPipelineVaiable)"

1.3 Create below Pipeline Variables

1.4 Save and run

It would display following output

2. Create Powershell script to trigger from Azure Automation Runbook

2.1 Create automation account variable VSTSToken and paste your azure devops PAT token and keep it secret by enabling encrypting it

2.2 Create variables required for automation runbook

[CmdLetBinding()]
Param(
    # Parameters to be received from ServiceNow 
    [Parameter(Mandatory = $true)][String] $OrganizationName = "MyOrganization",
    [Parameter(Mandatory = $true)][String] $ProjectName = "MyProject",
    [Parameter(Mandatory = $true)][String] $Parameter1 = "Parameter1",
    [Parameter(Mandatory = $true)][String] $Parameter2 = "Parameter2"
)

2.2. Get encoded access token to invoke Azure DevOps REST API’s 

$authorisationType = "Basic"
$vstsToken =  Get-AutomationVariable -Name "VSTSToken"
$encodedToken = [Convert]::ToBase64String(([Text.Encoding])::ASCII.GetBytes((":{1}" -f "", $vstsToken)))

2.3 Get the build definition of pipeline created in Step 1(Create Azure DevOps Repo and Pipeline). This can be done by invoking Azure DevOps REST API for Definitions – List

$buildDefinitionApiUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/definitions?name={pipeline_name}&api-version=5.1"
[PSObject]$buildDefinitions = Invoke-RestMethod -Uri $buildDefinitionApiUrl -Method Get -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken); ContentType = ("application/json"); }  

2.4 Update the retrieved build definition to set build agent, repository and pipeline parameters to pass. This is little tough to find. in order to find request details I ran my existing pipeline and observed the ajax request and based on Azure DevOps Request Body to Update Build Definition, I prepared below object  

$updateBuildJsonContent = [PSCustomObject]@{
        "comment" = "your build definition comment goes here"
        "description" = "your build definition comment description goes here"
        "id" = "$($buildDefinitions.value[0].id)"
        "name" = "$($buildDefinitions.value[0].name)"
        "revision" = "$($buildDefinitions.value[0].revision)"
        "queue" = [PSCustomObject]@{
            "pool" = [PSCustomObject]@{
                "name" = "Hosted VS2019"
                "isHosted" = "true"
            }
        }
        "process" = [PSCustomObject]@{
            "yamlFilename" = "/azure-pipelines.yml"
            "type" = 2
        }
        "repository" = [PSCustomObject]@{
            "name" = "your_git_repository_name"
            "url" = "https://dev.azure.com/{organization}/{project}/_git/{your_git_repository_name}"
            "defaultBranch" =  "refs/heads/master"
            "type" = "TfsGit"
        }
        "variables" = [PSCustomObject]@{               
            "Parameter1"   = [PSCustomObject]@{
                                        "allowOverride" = "false"
                                        "isSecret" = "false"
                                        "value" = "$($Parameter1)"
                                    }
            "Parameter2"             = [PSCustomObject]@{
                                        "allowOverride" = "false"
                                        "isSecret" = "false"
                                        "value" = "$($Parameter2)"
                                    }            
        }

2.5 Convert the request to JSON and Invoke Azure DevOps REST API to Update Build Definition

$updateBuildJsonContent = ($updateBuildJsonContent | ConvertTo-Json -Compress -Depth 100)
    $updateBuildDefinitionUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/definitions/$($buildDefinitions.value[0].id)?api-version=5.1"
    Invoke-RestMethod -Uri $updateBuildDefinitionUrl -Method Put -Body $updateBuildJsonContent -ContentType 'application/json' -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken) } | Out-Null

2.6 Create json request body to queue build

$jsonContent = [PSCustomObject]@{
        "definition" = [PSCustomObject]@{
                                            "id" = "$($buildDefinitions.value[0].id)"
                                        }
        "repository" = [PSCustomObject]@{
                                            "name" = "your_build_pipeline_name"
                                            "url" = "https://dev.azure.com/{organization}/{project}/_git/{your_git_repository_name}"
                                            "defaultBranch" =  "refs/heads/master"
                                        }
        "reason"     = [PSCustomObject]@{
                                            "userCreated" = "User defined some comment to queue build from azure automation runbook"
                                        }
    }

 2.7 Queue Azure DevOps build using Azure DevOps REST API for Queue Build

$jsonContent = ($jsonContent | ConvertTo-Json -Compress -Depth 100)
    $queueBuildUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=5.1"
    $queuedBuild = Invoke-RestMethod -Uri $queueBuildUrl -Method Post -Body $jsonContent -ContentType 'application/json' -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken) } 

2.8 Check the queue status until pipeline complete execution. Status of running build pipeline can be found at Azure DevOps REST API 

$buildId = $queuedBuild.id
    $queuedBuildDetailsApiUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/builds/$($buildId)?api-version=5.1"
    $automationBuildStatus = Invoke-RestMethod -Uri $queuedBuildDetailsApiUrl -Method Get -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken); ContentType = ("application/json"); }  
    While ($true) {
        Start-Sleep -Seconds 60
        $automationBuildStatus = Invoke-RestMethod -Uri $queuedBuildDetailsApiUrl -Method Get -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken); ContentType = ("application/json"); }  
        Write-Output("MESSAGE: ...")
        If ($automationBuildStatus.status -ne 'inProgress') {
            Break
        }
    }

Check build is triggered

2.9 Save the powershell script in runbook and test it. This will display following output on console

2.10 Verify pipeline output 

Yes the output is what I expected. The variables passed from azure automation are reflected here

Leave a Reply