The aim of this post is to enable a powershell Azure Automation runbook to be fired when a VM is deallocated (Stopped from the Azure management fabric), that will perform given tasks.
This is useful if you have non-urgent infrastructural changes that need to be done, but you want it done with the least amount of user impact. When the user, or a schedule stops the VM, the runbook is trigger.
You will need an OMS workspace set-up and linked to an Azure Automation account.
In the linked Automation Account, create a new Runbook called "DeallocationAction".
This will accept the OMS log alert results and allow the required actions to be performed
Here is one I've written to give you the idea and get you started.
This runbook checks for VMs in East US, created by System and resizes them based on a mapping array.
Param ( [parameter(Mandatory=$false)] [object] $WebhookData ) # Collect properties of WebhookData $WebhookName = $WebhookData.WebhookName $WebhookHeaders = $WebhookData.RequestHeader $WebhookBody = $WebhookData.RequestBody # Collect results. Information converted from JSON. $SearchResults = (ConvertFrom-Json $WebhookBody).SearchResults.value #Retrive the RunAs Connection details $Conn = Get-AutomationConnection -Name 'AzureRunAsConnection' #Login with the RunAs Account Add-AzureRMAccount -ServicePrincipal -Tenant $Conn.TenantID ` -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint $SizeMap = @() $SizeMap += @{Old='Standard_F1';New='Standard_D1'} $SizeMap += @{Old='Standard_F2';New='Standard_D2'} $SizeMap += @{Old='Standard_F4';New='Standard_D3'} $SizeMap += @{Old='Standard_F8';New='Standard_D4'} $CurrSub = $null $CurrSub = (Get-AzureRmContext).Subscription.Id foreach ($Result in $SearchResults) { Write-Output "Result" Write-Output $Result #Make sure it's the final succeeded action, and not Started or Accepted if ($Result.ActivityStatus -eq "Succeeded") { #Change the Subscription if required if ($CurrSub -ne $Result.SubscriptionId) { Select-AzureRmSubscription -SubscriptionId $Result.SubscriptionId $CurrSub = $Result.SubscriptionId } #Get the VM details $ResourceGroup = $Result.ResourceGroup $VMName = $Result.Resource $VM = Get-AzureRmVM -ResourceGroupName $ResourceGroup -Name $VMName $CurrSize = $VM.HardwareProfile.VmSize Write-Output "$($VMName) - Current VM Size: $($CurrSize)" #Check the machine is still deallocated $VMStatus = Get-AzureRmVM -ResourceGroupName $ResourceGroup -Name $VMName -Status if ($VMStatus.Statuses[1].Code -eq "PowerState/deallocated") { #Put any additional filters in here for continuing to change VM size #These are negatives, start with the VM being included then remove based on filters $Enabled = $true if ($VM.Location.ToLower() -ne "eastus") { $Enabled = $false } if ($VM.Tags.CreatedBy -ne "System") { $Enabled = $false } #Continue if passed through previous filters if ($Enabled) { #Make the required changes foreach ($Size in $SizeMap) { if ($CurrSize -eq $Size.Old) { $NewSize = $Size.New } } if ($CurrSize -ne $NewSize) { Write-Output "$($VMName) - New VM Size: $($NewSize)" #Update the VM Size $VM.HardwareProfile.VmSize = $NewSize #Apply the change $UpdateVM = Update-AzureRmVM -VM $VM -ResourceGroupName $ResourceGroup #If the update succeeds, update the SizeChange Tag if ($UpdateVM.IsSuccessStatusCode) { Write-Output "$($VMName) - Change Succeeded" $SizeChange = @{Old=$($CurrSize);UTC=(Get-Date (Get-Date).ToUniversalTime() -Format "dd/MM/yy HH:mm")} $SizeJSON = $SizeChange | ConvertTo-Json -Compress $Tags = $null $Tags = (Get-AzureRmResource -ResourceId $VM.Id).Tags if ($Tags -eq $null) { $Tags = @{ SizeChanged = $SizeJSON } } else { if ($Tags.ContainsKey("SizeChanged")) { #Make sure the current JSON is configured as an array $a = [regex]"[*]" if ($a.Match($Tags.SizeChanged).Success) { $CurrSizeChange = (ConvertFrom-Json $Tags.SizeChanged) } else { $CurrSizeChange = (ConvertFrom-Json "[`n$($Tags.SizeChanged)`n]") } $CurrSizeChange += $($SizeChange) $SizeJSON = $CurrSizeChange | ConvertTo-Json -Compress #Make sure Tag value is less than 256, keep dropping first record off until under 256 in length $RecSkip = 0 do { $RecSkip $SizeJSON = $CurrSizeChange | Select-Object -Skip $RecSkip | ConvertTo-Json -Compress $RecSkip += 1 } until ($SizeJSON.Length -lt 256) #Set Tag value $Tags.SizeChanged = $SizeJSON } else { $Tags += @{SizeChanged=$SizeJSON} } } Set-AzureRmResource -Tag $Tags -ResourceId $VM.Id -Force } else { Write-Output "$($VMName) - Change Failed" } } } } } }
Next, create an alert on the following OMS log query
Type=AzureActivity AND OperationName = "Microsoft.Compute/virtualMachines/deallocate/action" AND ActivityStatus="Succeeded"
Set the other attributes for the alert as follows
Time window: 5 minutes
Frequency: 5 minutes
Number of results greater than 0
Runbook: YES
Select the runbook created above
Now whenever a VM Deallocation event occurs and is logged through from Azure Activity to OMS (this can take up to 15 minutes), the Runbook you created is triggered and sent the VM information (within 5 minutes) so the required actions can be performed.