<# Copyright 2017-2021 VMware, Inc. SPDX-License-Identifier: Apache-2.0 #> <# Copyright 2021 VMware, Inc. SPDX-License-Identifier: BSD-2-Clause #> # SRM Helper Methods - https://github.com/benmeadowcroft/SRM-Cmdlets <# .SYNOPSIS Get the subset of recovery plans matching the input criteria .PARAMETER Name Return recovery plans matching the specified name .PARAMETER ProtectionGroup Return recovery plans associated with particular protection groups #> Function Get-RecoveryPlan { [cmdletbinding()] Param( [Parameter(position=1)][string] $Name, [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroup, [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer ) begin { $api = Get-ServerApiEndpoint -SrmServer $SrmServer $rps = @() } process { if ($ProtectionGroup) { foreach ($pg in $ProtectionGroup) { $rps += $pg.ListRecoveryPlans() } $rps = Select_UniqueByMoRef($rps) } else { $rps += $api.Recovery.ListPlans() } } end { $rps | ForEach-Object { $rp = $_ $rpi = $rp.GetInfo() $selected = (-not $Name -or ($Name -eq $rpi.Name)) if ($selected) { Add-Member -InputObject $rp -MemberType NoteProperty -Name "Name" -Value $rpi.Name $rp } } } } <# .SYNOPSIS Start a Recovery Plan action like test, recovery, cleanup, etc. .PARAMETER RecoveryPlan The recovery plan to start .PARAMETER RecoveryMode The recovery mode to invoke on the plan. May be one of "Test", "Cleanup", "Failover", "Migrate", "Reprotect" #> Function Start-RecoveryPlan { [cmdletbinding(SupportsShouldProcess=$True, ConfirmImpact="High")] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode] $RecoveryMode = [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode]::Test, [bool] $SyncData = $True ) # Validate with informative error messages $rpinfo = $RecoveryPlan.GetInfo() # Create recovery options $rpOpt = New-Object VMware.VimAutomation.Srm.Views.SrmRecoveryOptions $rpOpt.SyncData = $SyncData # Prompt the user to confirm they want to execute the action if ($pscmdlet.ShouldProcess($rpinfo.Name, $RecoveryMode)) { if ($rpinfo.State -eq 'Protecting') { throw "This recovery plan action needs to be initiated from the other SRM instance" } $RecoveryPlan.Start($RecoveryMode, $rpOpt) } } <# .SYNOPSIS Stop a running Recovery Plan action. .PARAMETER RecoveryPlan The recovery plan to stop #> Function Stop-RecoveryPlan { [cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact="High")] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan ) # Validate with informative error messages $rpinfo = $RecoveryPlan.GetInfo() # Prompt the user to confirm they want to cancel the running action if ($pscmdlet.ShouldProcess($rpinfo.Name, 'Cancel')) { $RecoveryPlan.Cancel() } } <# .SYNOPSIS Retrieve the historical results of a recovery plan .PARAMETER RecoveryPlan The recovery plan to retrieve the history for #> Function Get-RecoveryPlanResult { [cmdletbinding()] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode] $RecoveryMode, [VMware.VimAutomation.Srm.Views.SrmRecoveryResultResultState] $ResultState, [DateTime] $StartedAfter, [DateTime] $startedBefore, [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer ) $api = Get-ServerApiEndpoint -SrmServer $SrmServer # Get the history objects $history = $api.Recovery.GetHistory($RecoveryPlan.MoRef) $resultCount = $history.GetResultCount() if ($resultCount -gt 0) { $results = $history.GetRecoveryResult($resultCount) $results | Where-Object { -not $RecoveryMode -or $_.RunMode -eq $RecoveryMode } | Where-Object { -not $ResultState -or $_.ResultState -eq $ResultState } | Where-Object { $null -eq $StartedAfter -or $_.StartTime -gt $StartedAfter } | Where-Object { $null -eq $StartedBefore -or $_.StartTime -lt $StartedBefore } } } <# .SYNOPSIS Exports a recovery plan result object to XML format .PARAMETER RecoveryPlanResult The recovery plan result to export #> Function Export-RecoveryPlanResultAsXml { [cmdletbinding()] [OutputType([xml])] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryResult] $RecoveryPlanResult, [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer ) $api = Get-ServerApiEndpoint -SrmServer $SrmServer $RecoveryPlan = $RecoveryPlanResult.Plan $history = $api.Recovery.GetHistory($RecoveryPlan.MoRef) $lines = $history.GetResultLength($RecoveryPlanResult.Key) [xml] $history.RetrieveStatus($RecoveryPlanResult.Key, 0, $lines) } <# .SYNOPSIS Add a protection group to a recovery plan. This requires SRM 5.8 or later. .PARAMETER RecoveryPlan The recovery plan the protection group will be associated with .PARAMETER ProtectionGroup The protection group to associate with the recovery plan #> Function Add-ProtectionGroupToRecoveryPlan { [cmdletbinding()] Param( [Parameter (Mandatory=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=2)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup] $ProtectionGroup ) if ($RecoveryPlan -and $ProtectionGroup) { foreach ($pg in $ProtectionGroup) { try { $RecoveryPlan.AddProtectionGroup($pg.MoRef) } catch { Write-Error $_ } } } } <# .SYNOPSIS Remove a protection group to a recovery plan. This requires SRM 6.5 or later. .PARAMETER RecoveryPlan The recovery plan the protection group will be disassociated from .PARAMETER ProtectionGroup The protection group to disassociate from the recovery plan #> Function Remove-ProtectionGroupFromRecoveryPlan { [cmdletbinding()] Param( [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup] $ProtectionGroup ) if ($RecoveryPlan -and $ProtectionGroup) { foreach ($pg in $ProtectionGroup) { try { $RecoveryPlan.RemoveProtectionGroupFromRecoveryPlan($pg.MoRef) } catch { Write-Error $_ } } } } <# .SYNOPSIS Get the recovery settings of a protected VM. This requires SRM 5.8 or later. .PARAMETER RecoveryPlan The recovery plan the settings will be retrieved from. .PARAMETER Vm The virtual machine to retieve recovery settings for. #> Function Get-RecoverySetting { [cmdletbinding()] Param( [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $Vm, [Parameter (ValueFromPipeline=$true)][VMware.Vim.VirtualMachine] $VmView, [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectedVm] $ProtectedVm ) $moRef = Get_MoRefFromVmObj -Vm $Vm -VmView $VmView -ProtectedVm $ProtectedVm if ($RecoveryPlan -and $moRef) { $RecoveryPlan.GetRecoverySettings($moRef) } } <# .SYNOPSIS Get the recovery settings of a protected VM. This requires SRM 5.8 or later. .PARAMETER RecoveryPlan The recovery plan the settings will be retrieved from. .PARAMETER Vm The virtual machine to configure recovery settings on. .PARAMETER RecoverySettings The recovery settings to configure. These should have been retrieved via a call to Get-RecoverySettings #> Function Set-RecoverySetting { [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $Vm, [Parameter (ValueFromPipeline=$true)][VMware.Vim.VirtualMachine] $VmView, [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectedVm] $ProtectedVm, [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings ) $moRef = Get_MoRefFromVmObj -Vm $Vm -VmView $VmView -ProtectedVm $ProtectedVm if ($RecoveryPlan -and $moRef -and $RecoverySettings) { if ($PSCmdlet.ShouldProcess("$moRef", "Set")) { $RecoveryPlan.SetRecoverySettings($moRef, $RecoverySettings) } } } <# .SYNOPSIS Create a new per-Vm command to add to the SRM Recovery Plan .PARAMETER Command The command script to execute. .PARAMETER Description The user friendly description of this script. .PARAMETER Timeout The number of seconds this command has to execute before it will be timedout. .PARAMETER RunInRecoveredVm For a post-power on command this flag determines whether it will run on the recovered VM or on the SRM server. #> Function New-Command { [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="None")] Param( [Parameter (Mandatory=$true)][string] $Command, [Parameter (Mandatory=$true)][string] $Description, [int] $Timeout = 300, [switch] $RunInRecoveredVm = $false ) if($PSCmdlet.ShouldProcess("Description", "New")) { $srmWsdlCmd = New-Object VMware.VimAutomation.Srm.WsdlTypes.SrmCommand $srmCmd = New-Object VMware.VimAutomation.Srm.Views.SrmCommand -ArgumentList $srmWsdlCmd $srmCmd.Command = $Command $srmCmd.Description = $Description $srmCmd.RunInRecoveredVm = $RunInRecoveredVm $srmCmd.Timeout = $Timeout $srmCmd.Uuid = [guid]::NewGuid() return $srmCmd } } <# Internal function #> Function Add_Command { [cmdletbinding()] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand, [Parameter (Mandatory=$true)][bool] $PostRecovery ) if ($PostRecovery) { $commands = $RecoverySettings.PostPowerOnCallouts } else { $commands = $RecoverySettings.PrePowerOnCallouts } if (-not $commands) { $commands = New-Object System.Collections.Generic.List[VMware.VimAutomation.Srm.Views.SrmCallout] } $commands.Add($SrmCommand) if ($PostRecovery) { $RecoverySettings.PostPowerOnCallouts = $commands } else { $RecoverySettings.PrePowerOnCallouts = $commands } } <# .SYNOPSIS Add an SRM command to the set of pre recovery callouts for a VM. .PARAMETER RecoverySettings The recovery settings to update. These should have been retrieved via a call to Get-RecoverySettings .PARAMETER SrmCommand The command to add to the list. #> Function Add-PreRecoveryCommand { [cmdletbinding()] [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand ) Add_Command -RecoverySettings $RecoverySettings -SrmCommand $SrmCommand -PostRecovery $false return $RecoverySettings } <# .SYNOPSIS Remove an SRM command from the set of pre recovery callouts for a VM. .PARAMETER RecoverySettings The recovery settings to update. These should have been retrieved via a call to Get-RecoverySettings .PARAMETER SrmCommand The command to remove from the list. #> Function Remove-PreRecoveryCommand { [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Low")] [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand ) if ($pscmdlet.ShouldProcess($SrmCommand.Description, "Remove")) { $RecoverySettings.PrePowerOnCallouts.Remove($SrmCommand) } return $RecoverySettings } <# .SYNOPSIS Add an SRM command to the set of post recovery callouts for a VM. .PARAMETER RecoverySettings The recovery settings to update. These should have been retrieved via a call to Get-RecoverySettings .PARAMETER SrmCommand The command to add to the list. #> Function Add-PostRecoveryCommand { [cmdletbinding()] [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand ) Add_Command -RecoverySettings $RecoverySettings -SrmCommand $SrmCommand -PostRecovery $true return $RecoverySettings } <# .SYNOPSIS Remove an SRM command from the set of post recovery callouts for a VM. .PARAMETER RecoverySettings The recovery settings to update. These should have been retrieved via a call to Get-RecoverySettings .PARAMETER SrmCommand The command to remove from the list. #> Function Remove-PostRecoveryCommand { [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Low")] [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] Param( [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand ) if ($pscmdlet.ShouldProcess($SrmCommand.Description, "Remove")) { $RecoverySettings.PostPowerOnCallouts.Remove($SrmCommand) } return $RecoverySettings } <# .SYNOPSIS Create a new recovery plan .PARAMETER Name The name for this recovery plan .PARAMETER Description A description of the recovery plan .PARAMETER Folder The recovery plan folder in which to create this recovery plan. Will default to the root recovery plan folder .PARAMETER ProtectionGroups The protection groups to associate with this recovery plan .PARAMETER TestNetworkMappings The test network mappings to configure as part of this recovery plan .PARAMETER SrmServer The SRM Server to operate against #> Function New-RecoveryPlan { [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [Parameter (Mandatory=$true)][string] $Name, [string] $Description, [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanFolder] $Folder, [VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroups, [VMware.VimAutomation.Srm.Views.SrmRecoveryTestNetworkMapping[]] $TestNetworkMappings, [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer ) $api = Get-ServerApiEndpoint -SrmServer $SrmServer if (-not $Folder) { $Folder = Get-RecoveryPlanFolder -SrmServer $SrmServer } $protectionGroupmRefs += @( $ProtectionGroups | ForEach-Object { $_.MoRef } | Select-Object -Unique) [VMware.VimAutomation.Srm.Views.CreateRecoveryPlanTask] $task = $null if ($PSCmdlet.ShouldProcess($Name, "New")) { $task = $api.Recovery.CreateRecoveryPlan( $Name, $Folder.MoRef, $protectionGroupmRefs, $Description, $TestNetworkMappings ) } while(-not $task.IsCreateRecoveryPlanComplete()) { Start-Sleep -Seconds 1 } $task.GetNewRecoveryPlan() } <# .SYNOPSIS Remove a recovery plan permanently .PARAMETER RecoveryPlan The recovery plan to remove .PARAMETER SrmServer The SRM Server to operate against #> Function Remove-RecoveryPlan { [cmdletbinding(SupportsShouldProcess=$True, ConfirmImpact="High")] Param( [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer ) $api = Get-ServerApiEndpoint -SrmServer $SrmServer $rpinfo = $RecoveryPlan.GetInfo() if ($pscmdlet.ShouldProcess($rpinfo.Name, "Remove")) { $api.Recovery.DeleteRecoveryPlan($RecoveryPlan.MoRef) } } <# .SYNOPSIS Get a recovery plan folder .PARAMETER SrmServer The SRM Server to query for the recovery plan folder #> Function Get-RecoveryPlanFolder { [cmdletbinding()] Param( [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer ) $api = Get-ServerApiEndpoint -SrmServer $SrmServer $folder = $api.Recovery.GetRecoveryPlanRootFolder() return $folder }