Triggering Azure Automation Runbooks using the Azure ARM REST API

There are many ways to start an Azure Automation Runbook. You can start the Runbook from the Azure Portal, using PowerShell cmdlets or a Webhook. And maybe even more.

If you want to call a Runbook from another tool, like OMS or any other tooling supporting web requests you can create a Webhook for the Runbook which to be called from the external tool.

A Webhook allows you to start a particular runbook in Azure Automation through a single HTTP request. This allows external services such as Visual Studio Team Services, GitHub, Microsoft Operations Management Suite Log Analytics, or custom applications to start runbooks without implementing a full solution using the Azure Automation API.

A disadvantage of using a Webhook for an Azure Automation Runbook is the lack of authentication for calling the Webhook. The only security available for calling the Azure Automation Runbook Webhook is the secret token that is generated during the creation of the Webhook.

webhook

After creation the URL can't be viewed anymore but anyone knowing the Webhook URL is able to call the Runbook via the Webhook. Provided they also know the needed parameter inputs.

So how could you call an Azure Automation Runbook via a web request using a username and password?

Azure REST API

Azure Resource Manager provides a way for you to deploy and manage the services that make up your applications. For an introduction to deploying and managing resources with Resource Manager, see Azure Resource Manager Overview. Most, but not all, services support Resource Manager, and some services support Resource Manager only partially. Microsoft will enable Resource Manager for every service that is important for future solutions, but until the support is consistent, you need to know the current status for each service. For information about the available services and how to work with them, see Resource Manager providers, regions, API versions and schemas. [*from Azure Resource Manager REST API Reference]

How to call an Azure Automation Runbook with Azure ARM REST API?

So how does the authentication work when you want to to do a web request call against the Azure ARM REST API? You need to supply a bearer Access Token in the request Header of the web request. But how do you get that AccessToken? You can retrieve the AccessToken by creating an Active Directory application and service principal and use a ClientID and ClientSecret to retrieve the AccessToken. We will use PowerShell to create the Service Principal to access resources in Azure.

Create a service principal to access resources

  1. Create the AD application with a password
  2. Create the service principal
  3. Assign the Contributor role to the service principal
 #region variables
$ADApplicationName = 'demowebrequest'
$HomePage = 'https://www.stranger.nl/demowebrequest'
$ADApplicationPassword = 'P@ssw0rd!'
#endregion

#region Login to Azure
Add-AzureRmAccount
 
#Select Azure Subscription
$subscription = 
(Get-AzureRmSubscription |
        Out-GridView `
        -Title 'Select an Azure Subscription ...' `
        -PassThru)
 
Set-AzureRmContext -SubscriptionId $subscription.Id -TenantId $subscription.TenantID

Select-AzureRmSubscription -SubscriptionName $subscription.Name
#endregion

#region create SPN with Password
New-AzureRmADApplication -DisplayName "demowebrequest" -HomePage $ADApplicationName -IdentifierUris $HomePage -Password $ADApplicationPassword -OutVariable app
New-AzureRmADServicePrincipal -ApplicationId $app.ApplicationId
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $app.ApplicationId.Guid

Get-AzureRmADApplication -DisplayNameStartWith 'demowebrequest' -OutVariable app
Get-AzureRmADServicePrincipal -ServicePrincipalName $app.ApplicationId.Guid -OutVariable SPN
#endregion

Remark:

If you want the Service Principal only to manage Automation Runbooks you should give this service account the "Automation Operator" Role and limit access to the Scope where the Automation Account is created.

You now need to follow the steps described in the blog post Using the Azure ARM REST API – Get Access Token.

If you followed the steps described there you should have a ClientId and ClientSecret which are going to be used to Authenticate against the Azure ARM REST API.

You can verify correct authentication using the following commands from bash. An access token is returned.

 curl --request POST "https://login.windows.net/[tennantid]/oauth2/token" --data-urlencode "resource=https://management.core.windows.net" --data-urlencode "client_id=[clientid]" --data-urlencode "grant_type=client_credentials" --data-urlencode "client_secret=[clientsecret]"

Or if you prefer PowerShell you can use the following commands:

 #Azure Authtentication Token

#requires -Version 3
#SPN ClientId and Secret
$ClientID       = "clientid" #ApplicationID
$ClientSecret   = "ClientSecret"  #key from Application
$tennantid      = "TenantID"
 

$TokenEndpoint = {https://login.windows.net/{0}/oauth2/token} -f $tenantid 
$ARMResource = "https://management.core.windows.net/";

$Body = @{
        'resource'= $ARMResource
        'client_id' = $ClientID
        'grant_type' = 'client_credentials'
        'client_secret' = $ClientSecret
}

$params = @{
    ContentType = 'application/x-www-form-urlencoded'
    Headers = @{'accept'='application/json'}
    Body = $Body
    Method = 'Post'
    URI = $TokenEndpoint
}

$token = Invoke-RestMethod @params

$token | select access_token, @{L='Expires';E={[timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($_.expires_on))}} | fl *

The next step is to call an Azure Automation Runbook using a web request against the Azure REST API with the earlier retrieved access token.

Azure Automation Runbook Web Request

Let's first start with retrieving Azure Automation Runbook information.

Method Request URI
GET 'https://management.azure.com/subscriptions/{SubscriptionID}/resourceGroups/{ResourceGroupName}/providers/Microsoft.Automation/automationAccounts/{AutomationAccountName}/runbooks?api-version={ApiVersion}'

Example web request call using curl from bash:

 #!/bin/bash

# bash script to retrieve Azure Runbook information using plain Azure ARM REST API web requests

#Azure Subscription variables
ClientID="[applicatie clientid" #ApplicationID
ClientSecret="application client secret"  #key from Application
TenantID="[azure tenantid]"
SubscriptionID="[azure subscriptionid]"
ResourceGroupName="[resourcegroup name for azure automation account]"
AutomationAccountName="[azure automation account name]"
APIVersion="2015-10-31"

accesstoken=$(curl -s --header "accept: application/json" --request POST "https://login.windows.net/$TenantID/oauth2/token" --data-urlencode "resource=https://management.core.windows.net/" --data-urlencode "client_id=$ClientID" --data-urlencode "grant_type=client_credentials" --data-urlencode "client_secret=$ClientSecret" | jq -r '.access_token')

#Use AccessToken in Azure ARM REST API call for Runbook Info
runbookURI="https://management.azure.com/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.Automation/automationAccounts/$AutomationAccountName/runbooks?api-version=$APIVersion"

curl -s --header "authorization: Bearer $accesstoken" --request GET $runbookURI | jq .

Result running script in WSL (Bash for Windows)

Runbook

Trigger Azure Automation Runbook with web request:

Simple Hello World Runbook PowerShell Script (HelloWorld.ps1):

 [CmdletBinding()]
param(
    $firstname,
    $lastname
)

Write-Output "Hello $firstname $lastname"

This Runbook has two parameters, FirstName and LastName.

If we now want to trigger this Runbook using a web request we need the following information.

Method Request URI
POST 'https://management.azure.com/subscriptions/{SubscriptionID}/resourceGroups/{ResourceGroupName}/providers/Microsoft.Automation/automationAccounts/{AutomationAccountName}/jobs/{GUID}?api-version={ApiVersion}'
BODY body

Example web request using curl:

 #!/bin/bash

# bash script to retrieve Azure Runbook information using plain Azure ARM REST API web requests

#Azure Subscription variables
ClientID="[applicatie clientid" #ApplicationID
ClientSecret="application client secret"  #key from Application
TenantID="[azure tenantid]"
SubscriptionID="[azure subscriptionid]"
ResourceGroupName="[resourcegroup name for azure automation account]"
AutomationAccountName="[azure automation account name]"
APIVersion="2015-10-31"
GUID=$(uuidgen)

accesstoken=$(curl -s --header "accept: application/json" --request POST "https://login.windows.net/$TenantID/oauth2/token" --data-urlencode "resource=https://management.core.windows.net/" --data-urlencode "client_id=$ClientID" --data-urlencode "grant_type=client_credentials" --data-urlencode "client_secret=$ClientSecret" | jq -r '.access_token')

#Use AccessToken in Azure ARM REST API call for Runbook Info
runbookURI="https://management.azure.com/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.Automation/automationAccounts/$AutomationAccountName/jobs/$GUID?api-version=$APIVersion"

curl -s --header "authorization: Bearer $accesstoken" --header "Content-Type: application/json" -d '{"tags":{},"properties":{"runbook":{"name":'"'$RunbookName'"'},"parameters":{"LastName":"Stranger","FirstName":"Stefan"}}}' --request PUT $runbookURI | jq .

Result output:
runbookwebrequest

You can also check the Runbook output in the Azure Portal.

job output

If you prefer to use PowerShell to call the Azure Automation Runbook via the Azure REST API you can use the following code:

 #requires -Version 3

# ---------------------------------------------------
# Script: CallRunbookFromRESTAPI.ps1
# Version:
# Author: Stefan Stranger
# Date: 09/08/2017 15:16:25
# Description: Call Azure Automation Runbook using Azure ARM REST API calls.
# Comments: https://docs.microsoft.com/en-us/rest/api/automation/
# Changes:  
# Disclaimer: 
# This example is provided "AS IS" with no warranty expressed or implied. Run at your own risk. 
# **Always test in your lab first**  Do this at your own risk!! 
# The author will not be held responsible for any damage you incur when making these changes!
# ---------------------------------------------------


#region variables
$ClientID       = '[ClientID]' #ApplicationID
$ClientSecret   = '[ClientSecret]'  #key from Application
$tenantid      = '[Azure Tenant Id]'
$SubscriptionId = '[Azure Subscription Id]'
$resourcegroupname = '[Resource Group Automation Account]'
$AutomationAccountName = '[Automation Account Name]'
$RunbookName = '[Runbook Name]'
$APIVersion = '2015-10-31'
#endregion

#region Get Access Token
$TokenEndpoint = {https://login.windows.net/{0}/oauth2/token} -f $tenantid 
$ARMResource = "https://management.core.windows.net/";

$Body = @{
        'resource'= $ARMResource
        'client_id' = $ClientID
        'grant_type' = 'client_credentials'
        'client_secret' = $ClientSecret
}

$params = @{
    ContentType = 'application/x-www-form-urlencoded'
    Headers = @{'accept'='application/json'}
    Body = $Body
    Method = 'Post'
    URI = $TokenEndpoint
}

$token = Invoke-RestMethod @params
#endregion


#region get Runbooks
$Uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Automation/automationAccounts/{2}/runbooks?api-version={3}' -f $SubscriptionId, $resourcegroupname, $AutomationAccountName, $APIVersion
$params = @{
  ContentType = 'application/x-www-form-urlencoded'
  Headers     = @{
    'authorization' = "Bearer $($token.Access_Token)"
  }
  Method      = 'Get'
  URI         = $Uri
}
Invoke-RestMethod @params -OutVariable Runbooks
#endregion

#region Start Runbook
$Uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Automation/automationAccounts/{2}/jobs/{3}?api-version={4}' -f $SubscriptionId, $resourcegroupname, $AutomationAccountName, $((New-Guid).guid), $APIVersion
$body = @{
  'properties' = @{
    'runbook'  = @{
      'name' = $RunbookName
    }
    'parameters' = @{
      'FirstName' = 'Stefan'
      'LastName' = 'Stranger'
    }
  }
  'tags'     = @{}
} | ConvertTo-Json
$body

$params = @{
  ContentType = 'application/json'
  Headers     = @{
    'authorization' = "Bearer $($token.Access_Token)"
  }
  Method      = 'Put'
  URI         = $Uri
  Body        = $body
}

Invoke-RestMethod @params -OutVariable Runbook
$Runbook.properties
#endregion

#region get Runbook Status
$Uri ='https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Automation/automationAccounts/{2}/Jobs/{3}?api-version=2015-10-31' -f $SubscriptionId, $resourcegroupname, $AutomationAccountName, $($Runbook.properties.jobId)
$params = @{
  ContentType = 'application/application-json'
  Headers     = @{
    'authorization' = "Bearer $($token.Access_Token)"
  }
  Method      = 'Get'
  URI         = $Uri
}
Invoke-RestMethod @params -OutVariable Status
$Status.properties
#endregion

Have fun with calling your Runbooks using web requests against the Azure ARM REST API!

References: