15 Feb 2019

Using Azure DevOps Pipelines with Azure Automation

Azure Automation is a robust, cross platform and powerful automation engine for script based process-automation. If you are unfamiliar with Azure Automation, have a look at the official docs here.

Source Control

Azure Automation has native support for GitHub and Azure DevOps (vsoGit) repositories used as source control for runbooks. The basic setup of source control integration is a quite simple and easy step. However, the concept behind this out of the box functionality might not fit a CI/CD concept. Azure Automation source control sync jobs are built to sync code changes either automatically or manually into the runbooks container. To learn about the basic steps to activate source control see the docs here.

While this might fit basic requirements, I recently had a project where the customer had the requirement to integrate runbook code changes into a CI/CD pipeline. As he is already using Azure DevOps pipelines the way to the new solution was pretty easy.

So, let’s see how this is built.

Step by Step Procedure

  1. De-activate auto sync (we don’t want to sync the runbooks automatically on code change commits)
  2. Create a new script runbook (Start-DevOpsSourceControlSync), which initiates the manual synchronization. We will call the runbook at a later stage from the CD pipeline
    [string]$connectionName = "AzureRunAsConnection",

    [string]$automationAccountName = "d-aut-automationlab-01",

    [string]$resourceGroupName = "rgr-automationdemo-d"
$errorActionPreference = "stop"

# Import Modules
Import-Module az.Accounts
Import-Module az.Automation

# Get Azure Run As Connection Name
$connectionName = "AzureRunAsConnection"
# Get the Service Principal connection details for the Connection name
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName         

# Logging in to Azure AD with Service Principal
"Logging in to Azure AD..."
Connect-AzAccount -TenantId $servicePrincipalConnection.TenantId `
    -ApplicationId $servicePrincipalConnection.ApplicationId `
    -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint

# Get Automation Account Object
$automationAccount = Get-AzAutomationAccount -Name $automationAccountName -ResourceGroupName $resourceGroupName 

#start source control sync job
$sc = Get-AzAutomationSourceControl -AutomationAccountName $automationAccount.AutomationAccountName -ResourceGroupName $automationAccount.ResourceGroupName 
$syncJob = Start-AzAutomationSourceControlSyncJob -SourceControlName $sc.Name -AutomationAccountName $automationAccount.AutomationAccountName -ResourceGroupName $automationAccount.ResourceGroupName 
$syncJobResult = Get-AzAutomationSourceControlSyncJob -SourceControlName $sc.Name -JobId $syncJob.SourceControlSyncJobId -AutomationAccountName $automationAccount.AutomationAccountName -ResourceGroupName $automationAccount.ResourceGroupName
# Get Sync Job Status, wait for completion 
while ($syncJobResult.ProvisioningState -eq 'New' -or $syncJobResult.ProvisioningState -eq 'Running'){
    $syncJobResult = Get-AzAutomationSourceControlSyncJob -SourceControlName $sc.Name -JobId $syncJob.SourceControlSyncJobId -AutomationAccountName $automationAccount.AutomationAccountName -ResourceGroupName $automationAccount.ResourceGroupName
    write-output "waiting for sync job to complete"
    start-sleep -Seconds 3
  1. Create Service Connection
  1. Create Build Pipeline


  1. Create Release Pipeline
Add the following inline code
#Define constants
$automationAccountName = "d-aut-automationlab-01"
$resourceGroupName = "rgr-automationdemo-d"

#Get automation account object
$automationAccount = Get-AzureRMAutomationAccount -Name $automationAccountName -ResourceGroupName $resourceGroupName 

#start sync runbook
$job = Start-AzureRMAutomationRunbook -Name "Start-DevOpsSourceControlSync" -AutomationAccountName $automationAccount.AutomationAccountName -ResourceGroupName $automationAccount.ResourceGroupName -Parameters $runbookParameters 

#Get Job Status, wait for completion or failure
$jobResult = Get-AzureRMAutomationJob -Id $job.JobId -AutomationAccountName $automationAccount.AutomationAccountName -ResourceGroupName $automationAccount.ResourceGroupName
while ($jobResult.Status -eq 'New' -or $jobResult.Status -eq 'Running'){
    $jobResult = Get-AzureRMAutomationJob -Id $job.JobId -AutomationAccountName $automationAccount.AutomationAccountName -ResourceGroupName $automationAccount.ResourceGroupName
    write-output "waiting for job to complete"
    start-sleep -Seconds 3

As you can see, we now have a fully integrated CI/CD pipeline, everytime someone pushes code changes to our master branch. Needless to note that in production environments you’d have multiple stages for the deployments and branch policy for master branch, preventing direct pushes. But for the purpose of this demo I wanted to keep the steps as simple as possible.

Hope provides some ideas…