UpdateManagement-HybridWorker.ps1
<#PSScriptInfo .VERSION 1.0 .GUID b5eb0470-89af-4302-8200-144d19c454a8 .AUTHOR zachal .COMPANYNAME Microsoft .COPYRIGHT .TAGS UpdateManagement, Automation .LICENSEURI .PROJECTURI .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .PRIVATEDATA #> <# .DESCRIPTION This script is intended to be run as a part of Update Management Pre/Post scripts. It requires hybrid workers to be configured on the machines which need to run scripts locally. Runs a child Automation Runbook on a hybrid worker #> <# .SYNOPSIS Runs a child Automation Runbook on a hybrid worker .DESCRIPTION This script is intended to be run as a part of Update Management Pre/Post scripts. It requires hybrid workers to be configured on the machines which need to run scripts locally. .PARAMETER RunbookName The name of the Azure Automation runbook you wish to execute on the hybrid workers in a local context .PARAMETER HybridWorkerGroups A hybrid worker group which should run another runbook from a local context. To guarantee execution on the right machine, each hybrid worker group should contain only one machine. KNOWN ISSUE: Pre/Post scripts will not accept arrays or objects as arguments. .PARAMETER SoftwareUpdateConfigurationRunContext This is a system variable which is automatically passed in by Update Management during a deployment. #> param( [parameter(Mandatory=$true)] [string]$RunbookName, [parameter(Mandatory=$true)] [string]$HybridWorkerGroups, [string]$SoftwareUpdateConfigurationRunContext ) #region BoilerplateAuthentication #This requires a RunAs account $ServicePrincipalConnection = Get-AutomationConnection -Name 'AzureRunAsConnection' Add-AzureRmAccount ` -ServicePrincipal ` -TenantId $ServicePrincipalConnection.TenantId ` -ApplicationId $ServicePrincipalConnection.ApplicationId ` -CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint $AzureContext = Select-AzureRmSubscription -SubscriptionId $ServicePrincipalConnection.SubscriptionID #endregion BoilerplateAuthentication $runStatus = New-Object System.Collections.Generic.List[System.Object] $finalStatus = New-Object System.Collections.Generic.List[System.Object] #If you wish to use the run context, it must be converted from JSON $context = ConvertFrom-Json $SoftwareUpdateConfigurationRunContext #https://github.com/azureautomation/runbooks/blob/master/Utility/ARM/Find-WhoAmI # In order to prevent asking for an Automation Account name and the resource group of that AA, # search through all the automation accounts in the subscription # to find the one with a job which matches our job ID $AutomationResource = Get-AzureRmResource -ResourceType Microsoft.Automation/AutomationAccounts foreach ($Automation in $AutomationResource) { $Job = Get-AzureRmAutomationJob -ResourceGroupName $Automation.ResourceGroupName -AutomationAccountName $Automation.Name -Id $PSPrivateMetadata.JobId.Guid -ErrorAction SilentlyContinue if (!([string]::IsNullOrEmpty($Job))) { $ResourceGroup = $Job.ResourceGroupName $AutomationAccount = $Job.AutomationAccountName break; } } #Start script on each machine foreach($machine in $HybridWorkerGroups) { $output = Start-AzureRmAutomationRunbook -Name $RunbookName -ResourceGroupName $ResourceGroup -AutomationAccountName $AutomationAccount -RunOn $machine $runStatus.Add($output) } #Determine status of all runs. foreach($job in $runStatus) { #First, wait for each job to complete $currentStatus = Get-AzureRmAutomationJob -Id $job.jobid -ResourceGroupName $ResourceGroup -AutomationAccountName $AutomationAccount while ($currentStatus.status -ne "Completed") { Start-Sleep -Seconds 5 $currentStatus = Get-AzureRmAutomationJob -Id $job.jobid -ResourceGroupName $ResourceGroup -AutomationAccountName $AutomationAccount } #Then, store the summary $summary = Get-AzureRmAutomationJobOutput -Id $job.jobid -ResourceGroupName $ResourceGroup -AutomationAccountName $AutomationAccount $finalStatus.Add($summary) } #In this case, we want to terminate the patch job if any run fails. #This logic might not hold for all cases - you might want to allow success as long as at least 1 run succeeds foreach($summary in $finalStatus) { if ($summary.Type -eq "Error") { #We must throw in order to fail the patch deployment. throw $summary.Summary } } |