Azure Automation Week: Run a Runbook Without the Cmdlets!

3 minute read

Now that I have an Azure Automation account, and a user with the correct permissions, I have the ability to run Azure Automation runbooks without relying on the cmdlets or webhooks. I don’t want to have to rely on cmdlets in WinPE, and I also don’t want to have to rely on webhooks because you cannot get the results back from them.

Before I can start, I need a runbook to test! So, let’s fire up portal.azure.com and select our Automation Account:

image

Now, you’ll want to select Runbooks:

image

Add a runbook

image

Quick Create

image

Then give it a name and use the type PowerShell:

image

Now you have a screen that is similar to the PowerShell ISE window. I’m not going to do anything fancy here. I’ll just have a parameter and return the parameter:

image

Save it, then publish it:

image

You now have a runbook you can use in a script!

Now, we want to be able to call the runbook in PowerShell and wait for a response. To do this, I’ll be using the Azure Automation REST API. There is a great guide on Laurie Rhode’s blog that details how to do this in PowerShell, but they don’t leave us with a workable function. I took this information and put it into a function so we can easily use it in OSD! Here’s my function:

Function Run-AzureRunbook {
    Param (
        $AutomationAccount, 
        $adTenant, 
        $UserName, 
        $Password,
        $RunBookName,
        $RunBookParamHash,
        $HybridWorkerGroup,
        $Params
    )
    $ARMResource = 'https://management.core.windows.net/'
    $resourceAppIdURI = 'https://management.core.windows.net/'
    $ClientID = '1950a258-227b-4e31-a9cf-717495945fc2'
    $FullUserName = "[email protected]$($adTenant)"
    $TokenEndpoint = "https://login.windows.net/$($adTenant)/oauth2/token"

    $UserAuthPayload = "resource=$($resourceAppIdURI)&client_id=$($clientId)"+"&grant_type=password&username=$($FullUserName)&password=$($password)&scope=openid"
    $null = [System.Reflection.Assembly]::LoadWithPartialName("System.Web")
 
$payload [email protected]"
   $($UserAuthPayload),
   $([System.Web.HttpUtility]::UrlEncode($ARMResource)),
   $([System.Web.HttpUtility]::UrlEncode($FullUserName)),
   $([System.Web.HttpUtility]::UrlEncode($Password)),
"@

    $Header = @{
     "Content-Type" = "application/x-www-form-urlencoded";
    }

    try {
        $AuthResult = Invoke-RestMethod -Uri $TokenEndpoint -Method Post -body $Payload -Headers $Header
    }
    catch {
        $ThrowMessage = "Could not create the authorization token. Error Message: $($_.Exception.Message)"
        throw $ThrowMessage
        Exit
    }

    $JobId = [GUID]::NewGuid().ToString()
    $RequestHeader = @{
        "Content-Type" = "application/json";
        "x-ms-version" = "2013-08-01";
        "Authorization" = "$($authResult.token_type) $($authResult.access_token)"
    }
    $Body = @"
        {
           "properties":{
           "runbook":{
               "name":"$RunBookName"
           },
           "parameters":{
                $Params
           },
           "runOn":"$HybridWorkerGroup",
           "isEncrypted":1
          }
       }
"@
    $URI = 'https://management.azure.com' + "$($AutomationAccount)/jobs/$($jobid)?api-version=2015-10-31"
    try {
        $Response = Invoke-RestMethod -Uri $URI -Method Put -body $body -Headers $RequestHeader
    }
    catch {
        $ThrowMessage = "Could not start job. Error Message: $($_.Exception.Message)"
        Throw $ThrowMessage
        Exit
    }
    $LoopStart = Get-Date
    $Loop = $true
    while($Loop) {
        $job = $null
        try {
            $job = Invoke-RestMethod -Uri $URI -Method GET -Headers $RequestHeader
        }
        catch {
            $ThrowMessage = "Could not get the job results. Error Message: $($_.Exception.Message)"
            Throw $ThrowMessage
            exit
        }
        $Script:Status = $Job.Properties.provisioningState
        $Loop = (($Status -ne 'Succeeded') -and ($Status -ne 'Failed') -and ($Status -ne 'Suspended') -and ($Status -ne 'Stopped'))
        $TimeElapsed = (Get-Date) - $LoopStart
        if ($TimeElapsed.Minutes -ge 5) { $Loop = $false }
    }
    $responseURI = 'https://management.azure.com' + "$($AutomationAccount)/jobs/$($jobid)/output?api-version=2015-10-31"
    $Response = $null
    try {
        $Response = Invoke-RestMethod -Uri $responseURI -Method Get -Headers $RequestHeader
    }
    catch {
        $ThrowMessage = "Could not get the job output. Error Message: $($_.Exception.Message)"
        Throw $ThrowMessage
        exit
    }
    return $Response
}

The parameters and where you get them from are:

RunbookName: The name of the runbook you created Params: The parameters in this format:  “myinput”:”datahere” HybridWorkerGroup: The name of the hybrid worker group from  yesterday UserName: OSD Service Account username that was set up yesterday Password: OSD Service Account password that was set up yesterday Automation Account: If you go to your runbook and then select settings –> Properties, you’ll see the Automation Account. It’s a VERY long string that starts with /subscriptions adTenant: The adTenant of UserName. If you don’t already know it, you can go to manage.windowsazure.com, go down to Active Directory, select your domain, find the username, then it’s whatever is after @.

image

 

Now, run the runbook with this command, and you should get the right data returned!

image

Leave a Comment