I’m sharing a template PowerShell-based Azure Automation runbook that I’ve written, to enable advanced automation in the Recovery Plans that can be used in Azure Site Recovery (ASR) failover.
ASR allows us to orchestrate the failover and failback of virtual machines. This can be a basic ordering of virtual machine power-up and power-down actions, but we can also inject runbooks from Azure Automation. This allows us to do virtually anything inside of Azure during a failover or failback.
I needed to write a runbook for ASR recently and I found that I needed this runbook to be able to work differently in different scenarios:
- Planned failover
- Unplanned failover
- Test failover
- Cleanup after a test failover
That last one is tricky, but I’ll come back to it. They key to the magic is a parameter in the runbook called $RecoveryPlanContext. When an ASR executes a runbook it passes some information into the script via this parameter. The parameter has multiple attributes, as documented by Microsoft here. Some of the interesting values we can query for are:
- FailoverType: test, unplanned, or planned.
- FailoverDirection: primary or secondary are the destinations.
- VmMap: An array listing the names of the VMs included in the runbook.
- ResourceGroupName: The name of the resource group that the failover VMs are in.
My template currently only queries for FailoverType, because my goal was to automate failover to Azure. However, there is as much value in dealing with FailoverDirection, because the runbook can be used to orchestrate a failback.
Let’s assume that I use a runbook to start up some machine(s) during a failover. This machine could be a jump box, enabling remote access into that machine, and from there we can jump to failed over machines. That would be useful in all kinds of failover, including a test failover. But what happens if I run a test, am happy with everything and run a cleanup task? Guess what … our recovery plan does not execute in reverse order, and the runbook is not executed. So what I’ve come up with is a way to tell the runbook that I want to do a cleanup. The runbook will typically be executed before the cleanup (some tasks must be done before VMs are removed to make scripting easier, e.g. finding PIPs used by VMs before the VMs and their NICs are deleted). The runbook still expects a parameter; you are prompted to enter a value, so I enter “cleanup”. Once my script sees that task it runs a cleanup function.
My template has 4 functions, one for each of the above 4 scenarios. There is an if statement to look for cleanup, and if that’s not the value, a switch statement checks $RecoveryPlanContext.FailoverType to see what the scenario is. If some unexpected value is found, the runbook will exit.
param ( [Object]$RecoveryPlanContext ) function DoTestCleanup () { Write-Output ("This is a cleanup after test failover") # Do stuff here } function DoTestFailover () { Write-Output ("This is a test failover") # Do stuff here } function DoPlannedFailover () { Write-Output ("This is a planned failover") # Do stuff here } function DoUnplannedFailover () { Write-Output ("This is an unplanned failover") # Do stuff here } # The runbook starts here Sleep 10 $connectionName = "AzureRunAsConnection" try { # Get the connection "AzureRunAsConnection " $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName "Logging in to Azure using $connectionName ..." Add-AzureRmAccount -ServicePrincipal -TenantId $servicePrincipalConnection.TenantId -ApplicationId $servicePrincipalConnection.ApplicationId -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint } catch { if (!$servicePrincipalConnection) { $ErrorMessage = "Connection $connectionName not found." throw $ErrorMessage } else{ Write-Error -Message $_.Exception throw $_.Exception } } write-output ("The failover type parameter is $RecoveryPlanContext") if($RecoveryPlanContext -eq 'cleanup') { DoTestCleanup } else { switch ($RecoveryPlanContext.FailoverType) { "Test" { DoTestFailover } "Planned" { DoPlannedFailover } "Unplanned" { DoUnplannedFailover } default { Write-Output ("Runbook aborted because there no failover type was specified") } } }