Public/Move-SCVirtualMachineReliably.ps1

function Move-SCVirtualMachineReliably {
    #Requires -Version 3.0
    #Requires -Modules virtualmachinemanager

    [CmdletBinding()]
    Param (
        [Parameter(ParameterSetName = 'ByHost', Mandatory)]
        [Microsoft.SystemCenter.VirtualMachineManager.Host]$SourceVMHost,
        [Parameter(ParameterSetName = 'ByVM', Mandatory)]
        [Microsoft.SystemCenter.VirtualMachineManager.VM[]]$VM,
        [Parameter(ParameterSetName = 'ByHost', Mandatory)]
        [Parameter(ParameterSetName = 'ByVM', Mandatory)]
        [Microsoft.SystemCenter.VirtualMachineManager.Host]$DestinationVMHost,
        [Parameter(ParameterSetName = 'ByHost', Mandatory)]
        [Parameter(ParameterSetName = 'ByVM', Mandatory)]
        [string]$Path,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$Timeout = $ModuleWideMigrationTimeout,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$MaxAttempts = $ModuleWideMigrationMaxAttempts,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$MaxParallelMigrations,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$MigrationJobGetTimeout = $ModuleWideMigrationJobGetTimeout,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$MigrationJobGetMaxAttempts = $ModuleWideMigrationJobGetMaxAttempts,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [System.TimeSpan]$BackupThreshold = $ModuleWideBackupThreshold,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [switch]$Bulletproof,
        [Parameter(ParameterSetName = 'ByHost')]
        [Parameter(ParameterSetName = 'ByVM')]
        [switch]$CrashOnUnmigratable
    )

    $ErrorActionPreference = 'Stop'

    Write-Debug -Message ('ENTER {0}' -f $MyInvocation.MyCommand.Name)

    try {
        Write-Debug -Message ('ENTER TRY {0}' -f $MyInvocation.MyCommand.Name)

        Write-Debug -Message ('$SourceVMHost: ''{0}''' -f [string]$SourceVMHost)
        Write-Debug -Message ('$DestinationVMHost: ''{0}''' -f [string]$DestinationVMHost)
        Write-Debug -Message ('$Path = ''{0}''' -f $Path)
        Write-Debug -Message ('$Timeout = {0}' -f $Timeout)
        Write-Debug -Message ('$MaxAttempts = {0}' -f $MaxAttempts)
        Write-Debug -Message ('$MaxParallelMigrations = {0}' -f $MaxParallelMigrations)
        Write-Debug -Message ('$MigrationJobGetTimeout = {0}' -f $MigrationJobGetTimeout)
        Write-Debug -Message ('$MigrationJobGetMaxAttempts = {0}' -f $MigrationJobGetMaxAttempts)
        Write-Debug -Message ('$VM: ''{0}''' -f [string]$VM.Name)
        Write-Debug -Message ('$BackupThreshold: ''{0}''' -f [string]$BackupThreshold)
        Write-Debug -Message ('$Bulletproof: ''{0}''' -f [string]$Bulletproof)
        Write-Debug -Message ('$CrashOnUnmigratable: ''{0}''' -f [string]$CrashOnUnmigratable)

        Write-Debug -Message 'if ($VM)'
        if ($VM) {
            Write-Debug -Message '$UniqueVMHosts = $VM.VMHost | Select-Object -Unique'
            $UniqueVMHosts = $VM.VMHost | Select-Object -Unique
            Write-Debug -Message ('$UniqueVMHosts: ''{0}''' -f [string]$UniqueVMHosts)
            Write-Debug -Message ('$UniqueVMHosts.Count: {0}' -f $UniqueVMHosts.Count)
            Write-Debug -Message 'if ($UniqueVMHosts.Count -eq 1)'
            if ($UniqueVMHosts.Count -eq 1) {
                # Right now the function supports moving VMs between two SCVMHosts only
                Write-Debug -Message '$SourceVMHost = $UniqueVMHosts'
                $SourceVMHost = $UniqueVMHosts
                Write-Debug -Message ('$SourceVMHost: ''{0}''' -f [string]$SourceVMHost)
            }
            elseif ($UniqueVMHosts.Count -lt 1) {
                $Message = ('Somehow there are no unique VMHosts: {0}' -f [string]$UniqueVMHosts.Name)
                $PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.Management.Automation.PSNotSupportedException' -ArgumentList $Message), 'PSNotSupportedException', [System.Management.Automation.ErrorCategory]::InvalidData, $UniqueVMHosts)))
            }
            else {
                $Message = ('Input VMs are from several VMHosts: {0}' -f [string]$UniqueVMHosts.Name)
                $PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.Management.Automation.PSNotSupportedException' -ArgumentList $Message), 'PSNotSupportedException', [System.Management.Automation.ErrorCategory]::InvalidData, $UniqueVMHosts)))
            }
        }

        Write-Debug -Message 'if ($SourceVMHost -ne $DestinationVMHost)'
        if ($SourceVMHost -ne $DestinationVMHost) {
            Write-Debug -Message ('$SourceVMHost.ServerConnection.ManagedComputer.Name: ''{0}''' -f [string]$SourceVMHost.ServerConnection.ManagedComputer.Name)
            Write-Debug -Message ('$SourceVMHost.ServerConnection.ManagedComputer.ID: ''{0}''' -f [string]$SourceVMHost.ServerConnection.ManagedComputer.ID)
            Write-Debug -Message ('$DestinationVMHost.ServerConnection.ManagedComputer.Name: ''{0}''' -f [string]$DestinationVMHost.ServerConnection.ManagedComputer.Name)
            Write-Debug -Message ('$DestinationVMHost.ServerConnection.ManagedComputer.ID: ''{0}''' -f [string]$DestinationVMHost.ServerConnection.ManagedComputer.ID)
            Write-Debug -Message 'if ($SourceVMHost.ServerConnection.ManagedComputer.ID -eq $DestinationVMHost.ServerConnection.ManagedComputer.ID)'
            if ($SourceVMHost.ServerConnection.ManagedComputer.ID -eq $DestinationVMHost.ServerConnection.ManagedComputer.ID) {
                Write-Debug -Message 'if (-not $VM)'
                if (-not $VM) {
                    Write-Debug -Message '$VM = Get-SCVirtualMachine -VMHost $SourceVMHost'
                    $VM = Get-SCVirtualMachine -VMHost $SourceVMHost
                }
                Write-Debug -Message ('$VM: ''{0}''' -f [string]$VM.Name)

                Write-Debug -Message 'if ($VM)'
                if ($VM) {
                    $SCVMStatesRunning = @(
                        [Microsoft.VirtualManager.Utils.VMComputerSystemState]::Running
                    )
                    Write-Debug -Message ('$SCVMStatesRunning: ''{0}''' -f [string]$SCVMStatesRunning)

                    $SCVMStatesMigrating = @(
                        [Microsoft.VirtualManager.Utils.VMComputerSystemState]::UnderMigration
                    )
                    Write-Debug -Message ('$SCVMStatesMigrating: ''{0}''' -f [string]$SCVMStatesMigrating)

                    Write-Debug -Message '[System.Collections.ArrayList]$UnmigratableVMs = @()'
                    [System.Collections.ArrayList]$UnmigratableVMs = @()
                    Write-Debug -Message '[System.Collections.ArrayList]$BackingUpVMs = @()'
                    [System.Collections.ArrayList]$BackingUpVMs = @()
                    Write-Debug -Message '[System.Collections.ArrayList]$VMMigrationRetryInfo = @()'
                    [System.Collections.ArrayList]$VMMigrationRetryInfo = @()

                    Write-Debug -Message '$SourceVMHostLiveMigrationMaximum = $SourceVMHost.LiveMigrationMaximum'
                    $SourceVMHostLiveMigrationMaximum = $SourceVMHost.LiveMigrationMaximum
                    Write-Debug -Message ('$SourceVMHostLiveMigrationMaximum = {0}' -f $SourceVMHostLiveMigrationMaximum)

                    Write-Debug -Message '$DestinationVMHostLiveMigrationMaximum = $DestinationVMHost.LiveMigrationMaximum'
                    $DestinationVMHostLiveMigrationMaximum = $DestinationVMHost.LiveMigrationMaximum
                    Write-Debug -Message ('$DestinationVMHostLiveMigrationMaximum = {0}' -f $DestinationVMHostLiveMigrationMaximum)
                    Write-Debug -Message ('$LiveMigrationMaximum = (({0}, {1}) | Measure-Object -Minimum).Minimum' -f $SourceVMHostLiveMigrationMaximum, $DestinationVMHostLiveMigrationMaximum)
                    $LiveMigrationMaximum = (($SourceVMHostLiveMigrationMaximum, $DestinationVMHostLiveMigrationMaximum) | Measure-Object -Minimum).Minimum
                    Write-Debug -Message ('$LiveMigrationMaximum = {0}' -f $LiveMigrationMaximum)

                    Write-Debug -Message ('$MaxParallelMigrations = {0}' -f $MaxParallelMigrations)
                    Write-Debug -Message 'if ($MaxParallelMigrations)'
                    if ($MaxParallelMigrations) {
                        Write-Debug -Message ('$LiveMigrationMaximum = (({0}, {1}) | Measure-Object -Minimum).Minimum' -f $LiveMigrationMaximum, $MaxParallelMigrations)
                        $LiveMigrationMaximum = (($LiveMigrationMaximum, $MaxParallelMigrations) | Measure-Object -Minimum).Minimum
                    }
                    Write-Debug -Message ('$LiveMigrationMaximum = {0}' -f $LiveMigrationMaximum)

                    do {
                        Write-Debug -Message ('$PsCmdlet.ParameterSetName: ''{0}''' -f $PsCmdlet.ParameterSetName)
                        switch ($PsCmdlet.ParameterSetName) {
                            'ByHost' {
                                $Filter = {$_ -notin $UnmigratableVMs}
                            }
                            'ByVM' {
                                $Filter = {$_ -in $VM -and $_ -notin $UnmigratableVMs}
                            }
                        }
                        Write-Debug -Message ('$Filter = ''{0}''' -f $Filter)

                        Write-Debug -Message 'Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)'
                        Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)
                        
                        $SourceSCVMs = Get-SCVirtualMachine -VMHost $SourceVMHost | Where-Object -FilterScript $Filter # Getting those VMs of which we care about
                        Write-Debug -Message ('$SourceSCVMs: ''{0}''' -f [string]$SourceSCVMs.Name)
                        Write-Debug -Message 'if ($SourceSCVMs)'
                        if ($SourceSCVMs) {
                            Write-Debug -Message '$SourceSCVMsMigrating = $SourceSCVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}'
                            $SourceSCVMsMigrating = $SourceSCVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}
                            Write-Debug -Message ('$SourceSCVMsMigrating: ''{0}''' -f [string]$SourceSCVMsMigrating.Name)
                            Write-Debug -Message '$SourceSCVMsNotMigrating = $SourceSCVMs | Where-Object -FilterScript {$_ -notin $SourceSCVMsMigrating}'
                            $SourceSCVMsNotMigrating = $SourceSCVMs | Where-Object -FilterScript {$_ -notin $SourceSCVMsMigrating}
                            Write-Debug -Message ('$SourceSCVMsNotMigrating: ''{0}''' -f [string]$SourceSCVMsNotMigrating.Name)
                            Write-Debug -Message '$SourceSCVMsNotMigratingRunning = $SourceSCVMsNotMigrating | Where-Object -FilterScript {$_.Status -in $SCVMStatesRunning}'
                            $SourceSCVMsNotMigratingRunning = $SourceSCVMsNotMigrating | Where-Object -FilterScript {$_.Status -in $SCVMStatesRunning}
                            Write-Debug -Message ('$SourceSCVMsNotMigratingRunning: ''{0}''' -f [string]$SourceSCVMsNotMigratingRunning.Name)
                            Write-Verbose -Message ('VMs left to migrate: {0}' -f [string]$SourceSCVMs.Name)

                            if ($SourceSCVMsNotMigratingRunning) {
                                Write-Debug -Message '$SourceVMsToMigrate = $SourceSCVMsNotMigratingRunning'
                                $SourceVMsToMigrate = $SourceSCVMsNotMigratingRunning # We TRY to migrate VMs which have just been running
                            }
                            else {
                                Write-Debug -Message '$SourceVMsToMigrate = $SourceSCVMsNotMigrating'
                                $SourceVMsToMigrate = $SourceSCVMsNotMigrating
                            }
                            
                            Write-Debug -Message '$FirstVM = $true'
                            $FirstVM = $true
                            Write-Debug -Message ('$FirstVM: ''{0}''' -f [string]$FirstVM)

                            Write-Debug -Message ('$SourceVMsToMigrate: ''{0}''' -f [string]$SourceVMsToMigrate.Name)
                            foreach ($SCVM in $SourceVMsToMigrate) {
                                # If a VM is migrating already - we don't care about it
                                Write-Debug -Message ('$SCVM: ''{0}''' -f $SCVM.Name)

                                Write-Debug -Message ('$FirstVM: ''{0}''' -f [string]$FirstVM)
                                Write-Debug -Message 'if ($FirstVM)'
                                if ($FirstVM) {
                                    # If this is the first VM in the loop, no need to refresh hosts' status again
                                    Write-Debug -Message '$FirstVM = $false'
                                    $FirstVM = $false
                                    Write-Debug -Message ('$FirstVM: ''{0}''' -f [string]$FirstVM)
                                }
                                else {
                                    Write-Debug -Message ('$Bulletproof: ''{0}''' -f [string]$Bulletproof)
                                    Write-Debug -Message 'if ($Bulletproof)'
                                    if ($Bulletproof) {
                                        Write-Debug -Message 'Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)'
                                        Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)
                                    }
                                }

                                Write-Debug -Message '$SourceVMHostVMs = Get-SCVirtualMachine -VMHost $SourceVMHost'
                                $SourceVMHostVMs = Get-SCVirtualMachine -VMHost $SourceVMHost
                                Write-Debug -Message ('$SourceVMHostVMs: ''{0}''' -f [string]$SourceSCVMsNotMigratingRunning.Name)
                                Write-Debug -Message '$SourceVMHostVMs = Get-SCVirtualMachine -VMHost $DestinationVMHost'
                                $DestinationVMHostVMs = Get-SCVirtualMachine -VMHost $DestinationVMHost
                                Write-Debug -Message ('$DestinationVMHostVMs: ''{0}''' -f [string]$SourceSCVMsNotMigratingRunning.Name)
                                
                                Write-Debug -Message '$SourceVMHostMigratingVMs = $SourceVMHostVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}'
                                $SourceVMHostMigratingVMs = $SourceVMHostVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}
                                Write-Debug -Message ('$SourceVMHostMigratingVMs: ''{0}''' -f [string]$SourceVMHostMigratingVMs.Name)
                                Write-Debug -Message '$DestinationVMHostMigratingVMs = $DestinationVMHostVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}'
                                $DestinationVMHostMigratingVMs = $DestinationVMHostVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}
                                Write-Debug -Message ('$DestinationVMHostMigratingVMs: ''{0}''' -f [string]$DestinationVMHostMigratingVMs.Name)
                                Write-Debug -Message ('$VMHostMigratingVMs = {0} + {1}' -f $SourceVMHostMigratingVMs.Count, $DestinationVMHostMigratingVMs.Count)
                                $VMHostMigratingVMsCount = $SourceVMHostMigratingVMs.Count + $DestinationVMHostMigratingVMs.Count
                                Write-Debug -Message ('$VMHostMigratingVMsCount = {0}' -f $VMHostMigratingVMsCount)

                                Write-Debug -Message ('$SourceVMHost.ServerConnection: ''{0}''' -f [string]$SourceVMHost.ServerConnection.FullyQualifiedDomainName)
                                Write-Debug -Message '$SCRunningJobs = Get-SCJob -VMMServer $SourceVMHost.ServerConnection -Running'
                                $SCRunningJobs = Get-SCJob -VMMServer $SourceVMHost.ServerConnection -Running
                                Write-Debug -Message ('$SCRunningJobs: ''{0}''' -f [string]$SCRunningJobs)
                                foreach ($SCRunningJob in $SCRunningJobs) {
                                    Write-Debug -Message ('$SCRunningJob.Name: ''{0}''' -f [string]$SCRunningJob.Name)
                                    Write-Debug -Message ('$SCRunningJob.CmdletName: ''{0}''' -f [string]$SCRunningJob.CmdletName)
                                    Write-Debug -Message ('$SCRunningJob.Source: ''{0}''' -f [string]$SCRunningJob.Source)
                                    Write-Debug -Message ('$SCRunningJob.Target: ''{0}''' -f [string]$SCRunningJob.Target)
                                }

                                # We use a separate filtering command, because it does not work in a single command: somehow the '-Running' parameter prevents subsequent filters from working correctly
                                # Turns out, "Source" and "Target" attributes have a delay for filling up, that's why I look for them in the name of the job as well.
                                Write-Debug -Message ('$MigrationJobs = $SCRunningJobs | Where-Object -FilterScript {{$_.CmdletName -eq ''Move-SCVirtualMachine'' -and ($_.Source -in @(''{0}'', ''{1}'') -or $_.Target -in @(''{0}'', ''{1}'') -or $_.Name -like (''*{{0}}*'' -f ''{0}'') -or $_.Name -like (''*{{0}}*'' -f ''{1}''))}}' -f $SourceVMHost.Name, $DestinationVMHost.Name)
                                $MigrationJobs = $SCRunningJobs | Where-Object -FilterScript {$_.CmdletName -eq 'Move-SCVirtualMachine' -and ($_.Source -in @($SourceVMHost.Name, $DestinationVMHost.Name) -or $_.Target -in @($SourceVMHost.Name, $DestinationVMHost.Name) -or $_.Name -like ('*{0}*' -f $SourceVMHost.Name) -or $_.Name -like ('*{0}*' -f $DestinationVMHost.Name))}
                                Write-Debug -Message ('$MigrationJobs: ''{0}''' -f [string]$MigrationJobs)
                                Write-Debug -Message ('$MigrationJobs.Count: {0}' -f $MigrationJobs.Count)
                                foreach ($MigrationJob in $MigrationJobs) {
                                    Write-Debug -Message ('$MigrationJob.Name: ''{0}''' -f [string]$MigrationJob.Name)
                                    Write-Debug -Message ('$MigrationJob.CmdletName: ''{0}''' -f [string]$MigrationJob.CmdletName)
                                    Write-Debug -Message ('$MigrationJob.Source: ''{0}''' -f [string]$MigrationJob.Source)
                                    Write-Debug -Message ('$MigrationJob.Target: ''{0}''' -f [string]$MigrationJob.Target)
                                }

                                Write-Debug -Message ('$CurrentLiveMigrationCount = (({0}, {1}) | Measure-Object -Maximum).Maximum' -f $MigrationJobs.Count, $VMHostMigratingVMsCount)
                                $CurrentLiveMigrationCount = (($MigrationJobs.Count, $VMHostMigratingVMsCount) | Measure-Object -Maximum).Maximum
                                Write-Debug -Message ('$CurrentLiveMigrationCount = {0}' -f $CurrentLiveMigrationCount)

                                Write-Debug -Message ('$LiveMigrationMaximum = {0}' -f $LiveMigrationMaximum)
                                Write-Debug -Message 'if ($CurrentLiveMigrationCount -lt $LiveMigrationMaximum)'
                                if ($CurrentLiveMigrationCount -lt $LiveMigrationMaximum) {
                                    # If the migration queue is not full (if it is full, we do no care who filled it up)
                                    $SCVirtualMachineLiveMigrationEligibility = Test-SCVirtualMachineLiveMigrationEligibility -VM $SCVM -VMHost $DestinationVMHost
                                    Write-Debug -Message ('$SCVirtualMachineLiveMigrationEligibility.Result: ''{0}''' -f $SCVirtualMachineLiveMigrationEligibility.Result)
                                    Write-Debug -Message ('$SCVirtualMachineLiveMigrationEligibility.Reason: ''{0}''' -f $SCVirtualMachineLiveMigrationEligibility.Reason)
                                    
                                    Write-Debug -Message 'if ($SCVirtualMachineLiveMigrationEligibility.Status -eq [Microsoft.VirtualManager.Utils.VMComputerSystemState]::IncompleteVMConfig)'
                                    if ($SCVirtualMachineLiveMigrationEligibility.Status -eq [Microsoft.VirtualManager.Utils.VMComputerSystemState]::IncompleteVMConfig) {
                                        Write-Debug -Message '$null = Read-SCVirtualMachine -VM $SCVM'
                                        $null = Read-SCVirtualMachine -VM $SCVM
                                        Write-Debug -Message '$null = $VMMigrationRetryInfo.Add($SCVM)'
                                        $null = $VMMigrationRetryInfo.Add($SCVM)
                                        Write-Debug -Message 'Continue'
                                        Continue
                                    }
                                    
                                    Write-Debug -Message 'if ($SCVirtualMachineLiveMigrationEligibility.Result -or $SCVirtualMachineLiveMigrationEligibility.Reason -eq ''NotRunning'')'
                                    if ($SCVirtualMachineLiveMigrationEligibility.Result -or $SCVirtualMachineLiveMigrationEligibility.Reason -eq 'NotRunning') {
                                        $VMMigrationRetryInfoCount = ($VMMigrationRetryInfo | Where-Object -FilterScript {$_ -eq $SCVM}).Count
                                        Write-Debug -Message 'if ($VMMigrationRetryInfoCount -ge $MaxAttempts)'
                                        if ($VMMigrationRetryInfoCount -ge $MaxAttempts) {
                                            Write-Verbose -Message ('VM {0} is unmigratable' -f $SCVM.Name)
                                            if ($CrashOnUnmigratable) {
                                                $Message = ('Tried to migrate VM {0} {1} times - did not succeed' -f $SCVM.Name, $MaxAttempts)
                                                $PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.ServiceModel.Channels.RetryException' -ArgumentList $Message), 'RetryException', [System.Management.Automation.ErrorCategory]::OperationTimeout, $SCVM)))
                                            }
                                            else {
                                                Write-Debug -Message '$null = $UnmigratableVMs.Add($SCVM)'
                                                $null = $UnmigratableVMs.Add($SCVM)
                                            }
                                        }
                                    }

                                    Write-Debug -Message ('$UnmigratableVMs: ''{0}''' -f [string]$UnmigratableVMs.Name)
                                    Write-Debug -Message 'if ($SCVM -notin $UnmigratableVMs)'
                                    if ($SCVM -notin $UnmigratableVMs) {
                                        Write-Debug -Message 'if ($SCVirtualMachineLiveMigrationEligibility.Result)'
                                        if ($SCVirtualMachineLiveMigrationEligibility.Result) {
                                            Write-Debug -Message ('$Bulletproof: ''{0}''' -f [string]$Bulletproof)
                                            Write-Debug -Message 'if ($Bulletproof)'
                                            if ($Bulletproof) {
                                                Write-Debug -Message 'Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)'
                                                Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)
                                            }

                                            Write-Debug -Message ('$SCVM.VMHost: ''{0}''' -f [string]$SCVM.VMHost)
                                            Write-Debug -Message 'if ($SCVM.VMHost -ne $DestinationVMHost)'
                                            if ($SCVM.VMHost -ne $DestinationVMHost) {
                                                # Better safe than sorry
                                                Write-Debug -Message '$null = $VMMigrationRetryInfo.Add($SCVM)'
                                                $null = $VMMigrationRetryInfo.Add($SCVM)
                                                Write-Debug -Message ('$VMMigrationRetryInfo: ''{0}''' -f [string]$VMMigrationRetryInfo.Name)
                                                Write-Verbose -Message ('Trying to live-migrate a VM {0} from {1} to {2}' -f $SCVM.Name, $SCVM.VMHost.Name, $DestinationVMHost.Name)
                                                try {
                                                    Write-Debug -Message ('$null = Move-SCVirtualMachine -VM $SCVM -VMHost $DestinationVMHost -Path ''{0}'' -RunAsynchronously' -f $Path)
                                                    $null = Move-SCVirtualMachine -VM $SCVM -VMHost $DestinationVMHost -Path $Path -RunAsynchronously
                                                }
                                                catch {
                                                    Write-Debug -Message ($_)
                                                    Write-Debug -Message ('Exception.HResult: {0}' -f $_.Exception.HResult)
                                                    Write-Debug -Message '$null = Read-SCVirtualMachine -VM $SCVM'
                                                    $null = Read-SCVirtualMachine -VM $SCVM
                                                    Write-Debug -Message 'Continue'
                                                    Continue
                                                }
                                                Write-Debug -Message ('$MigrationJobGetMaxAttempts = {0}' -f $MigrationJobGetMaxAttempts)
                                                Write-Debug -Message 'for ($MigrationJobGetCounter = 0; $MigrationJobGetCounter -lt $MigrationJobGetMaxAttempts; $MigrationJobGetCounter++)'
                                                for ($MigrationJobGetCounter = 0; $MigrationJobGetCounter -lt $MigrationJobGetMaxAttempts; $MigrationJobGetCounter++) {
                                                    Write-Debug -Message ('$MigrationJobGetCounter = {0}' -f $MigrationJobGetCounter)
                                                    Write-Debug -Message '$VMMigrationJob = Get-SCVirtualMachineMigrationJob -VM $SCVM'
                                                    $VMMigrationJob = Get-SCVirtualMachineMigrationJob -VM $SCVM
                                                    Write-Debug -Message ('$VMMigrationJob: ''{0}''' -f [string]$VMMigrationJob)
                                                    Write-Debug -Message 'if (-not $VMMigrationJob)'
                                                    if (-not $VMMigrationJob) {
                                                        Write-Debug -Message ('Start-Sleep -Seconds {0}' -f $MigrationJobGetTimeout)
                                                        Start-Sleep -Seconds $MigrationJobGetTimeout
                                                    }
                                                    else {
                                                        Write-Debug -Message 'break'
                                                        break
                                                    }
                                                }
                                                Write-Debug -Message 'if (-not $VMMigrationJob)'
                                                if (-not $VMMigrationJob) {
                                                    Write-Debug -Message 'Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)'
                                                    Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)
                                                }
                                                Write-Debug -Message '$LastVMWasPoweredDown = $false'
                                                $LastVMWasPoweredDown = $false
                                                Write-Debug -Message ('$LastVMWasPoweredDown: ''{0}''' -f [string]$LastVMWasPoweredDown)

                                                Write-Debug -Message ('$BackingUpVMs.VM: ''{0}''' -f [string]$BackingUpVMs.VM.Name)
                                                Write-Debug -Message 'if ($BackingUpVMs.VM -contains $SCVM)'
                                                if ($BackingUpVMs.VM -contains $SCVM) {
                                                    Write-Debug -Message '$BackingUpVMDescription = $BackingUpVMs | Where-Object -FilterScript {$_.VM -eq $SCVM}'
                                                    $BackingUpVMDescription = $BackingUpVMs | Where-Object -FilterScript {$_.VM -eq $SCVM}
                                                    Write-Debug -Message ('$BackingUpVMDescription: ''{0}''' -f [string]$BackingUpVMDescription)
                                                    Write-Debug -Message '$null = $BackingUpVMs.Remove($BackingUpVMDescription)'
                                                    $null = $BackingUpVMs.Remove($BackingUpVMDescription)
                                                    Write-Debug -Message ('$BackingUpVMs.VM: ''{0}''' -f [string]$BackingUpVMs.VM.Name)
                                                }
                                            }
                                        }
                                        else {
                                            Write-Verbose -Message ('Skipping VM {0} this time because: {1}' -f $SCVM.Name, $SCVirtualMachineLiveMigrationEligibility.Reason)
                                            switch ($SCVirtualMachineLiveMigrationEligibility.Reason) {
                                                'BackingUp' {
                                                    Write-Debug -Message ('$BackingUpVMs.VM: ''{0}''' -f [string]$BackingUpVMs.VM.Name)
                                                    Write-Debug -Message 'if ($BackingUpVMs.VM -contains $SCVM)'
                                                    if ($BackingUpVMs.VM -contains $SCVM) {
                                                        Write-Debug -Message '$BackingUpVMDescription = $BackingUpVMs | Where-Object -FilterScript {$_.VM -eq $SCVM}'
                                                        $BackingUpVMDescription = $BackingUpVMs | Where-Object -FilterScript {$_.VM -eq $SCVM}
                                                        Write-Debug -Message '$BackupAddDateTime = $BackingUpVMDescription.DateTime'
                                                        $BackupAddDateTime = $BackingUpVMDescription.DateTime
                                                        Write-Debug -Message ('$BackupAddDateTime: ''{0}''' -f [string]$BackupAddDateTime)
                                                        Write-Debug -Message '$CurrentDateTime = Get-Date'
                                                        $CurrentDateTime = Get-Date
                                                        Write-Debug -Message ('$CurrentDateTime: ''{0}''' -f [string]$CurrentDateTime)
                                                        Write-Debug -Message '$BackupDateTimeThreshold = $BackupAddDateTime + $BackupThreshold'
                                                        $BackupDateTimeThreshold = $BackupAddDateTime + $BackupThreshold
                                                        Write-Debug -Message ('$BackupDateTimeThreshold: ''{0}''' -f [string]$BackupDateTimeThreshold)
                                                        Write-Debug -Message 'if ($CurrentDateTime -gt $BackupDateTimeThreshold)'
                                                        if ($CurrentDateTime -gt $BackupDateTimeThreshold) {
                                                            Write-Verbose -Message ('VM {0} is unmigratable' -f $SCVM.Name)
                                                            if ($CrashOnUnmigratable) {
                                                                $Message = ('VM {0} is in backing up state for more than {1} already' -f $SCVM.Name, [string]$BackupThreshold)
                                                                $PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.TimeoutException' -ArgumentList $Message), 'TimeoutException', [System.Management.Automation.ErrorCategory]::OperationTimeout, $SCVM)))
                                                            }
                                                            else {
                                                                Write-Debug -Message '$null = $UnmigratableVMs.Add($SCVM)'
                                                                $null = $UnmigratableVMs.Add($SCVM)
                                                                Write-Debug -Message ('$UnmigratableVMs: ''{0}''' -f [string]$UnmigratableVMs.Name)
                                                                Write-Debug -Message '$null = $BackingUpVMs.Remove($BackingUpVMDescription)'
                                                                $null = $BackingUpVMs.Remove($BackingUpVMDescription)
                                                                Write-Debug -Message ('$BackingUpVMs.VM: ''{0}''' -f [string]$BackingUpVMs.VM.Name)
                                                            }
                                                        }
                                                    }
                                                    else {
                                                        Write-Debug -Message '$CurrentDateTime = Get-Date'
                                                        $CurrentDateTime = Get-Date
                                                        Write-Debug -Message ('$CurrentDateTime: ''{0}''' -f [string]$CurrentDateTime)
                                                        Write-Debug -Message '$null = $BackingUpVMs.Add(@{VM = $SCVM; DateTime = $CurrentDateTime})'
                                                        $null = $BackingUpVMs.Add(
                                                            @{
                                                                VM       = $SCVM
                                                                DateTime = $CurrentDateTime
                                                            }
                                                        )
                                                        Write-Debug -Message ('$BackingUpVMs.VM: ''{0}''' -f [string]$BackingUpVMs.VM.Name)
                                                    }
                                                }
                                                'NotRunning' {
                                                    Write-Debug -Message ('$Bulletproof: ''{0}''' -f [string]$Bulletproof)
                                                    Write-Debug -Message 'if ($Bulletproof)'
                                                    if ($Bulletproof) {
                                                        Write-Debug -Message 'Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)'
                                                        Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)
                                                    }

                                                    Write-Debug -Message ('$SourceSCVMs.Status: ''{0}''' -f [string]$SourceSCVMs.Status)
                                                    Write-Debug -Message '$SourceSCVMsRunning = $SourceSCVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesRunning}'
                                                    $SourceSCVMsRunning = $SourceSCVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesRunning}
                                                    Write-Debug -Message ('$SourceSCVMsRunning: ''{0}''' -f [string]$SourceSCVMsRunning.Name)
                                                    Write-Debug -Message '$SourceSCVMsMigrating = $SourceSCVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}'
                                                    $SourceSCVMsMigrating = $SourceSCVMs | Where-Object -FilterScript {$_.Status -in $SCVMStatesMigrating}
                                                    Write-Debug -Message ('$SourceSCVMsMigrating: ''{0}''' -f [string]$SourceSCVMsMigrating.Name)
                                                    Write-Debug -Message 'if (-not ($SourceSCVMsRunning -or $SourceSCVMsMigrating))'
                                                    if (-not ($SourceSCVMsRunning -or $SourceSCVMsMigrating)) {
                                                        # We do not want to move PoweredDown VMs while there's anything in the queue. We also want to migrate them as last as possible.
                                                        Write-Debug -Message '$null = $VMMigrationRetryInfo.Add($SCVM)'
                                                        $null = $VMMigrationRetryInfo.Add($SCVM)
                                                        Write-Debug -Message ('$VMMigrationRetryInfo: ''{0}''' -f [string]$VMMigrationRetryInfo.Name)
                                                        Write-Verbose -Message ('Trying to migrate a powered-down VM {0}' -f $SCVM.Name)
                                                        Write-Debug -Message ('Move-SCPoweredDownVirtualMachine -VM $SCVM -VMHost $DestinationVMHost -Path ''{0}''' -f $Path)
                                                        Move-SCPoweredDownVirtualMachine -VM $SCVM -VMHost $DestinationVMHost -Path $Path
                                                        Write-Debug -Message '$LastVMWasPoweredDown = $true'
                                                        $LastVMWasPoweredDown = $true
                                                        Write-Debug -Message ('$LastVMWasPoweredDown: ''{0}''' -f [string]$LastVMWasPoweredDown)
                                                    }
                                                }
                                                'Failed' {
                                                    Write-Debug -Message '$null = Repair-SCVirtualMachine -VM $SCVM -Dismiss -Force'
                                                    $null = Repair-SCVirtualMachine -VM $SCVM -Dismiss -Force
                                                }
                                                'OK' {
                                                    Write-Debug -Message '$null = $UnmigratableVMs.Add($SCVM)'
                                                    $null = $UnmigratableVMs.Add($SCVM)
                                                    Write-Debug -Message ('$UnmigratableVMs: ''{0}''' -f [string]$UnmigratableVMs.Name)
                                                    Write-Debug -Message ('$BackingUpVMs.VM: ''{0}''' -f [string]$BackingUpVMs.VM.Name)
                                                    Write-Debug -Message 'if ($BackingUpVMs.VM -contains $SCVM)'
                                                    if ($BackingUpVMs.VM -contains $SCVM) {
                                                        Write-Debug -Message '$BackingUpVMDescription = $BackingUpVMs | Where-Object -FilterScript {$_.VM -eq $SCVM}'
                                                        $BackingUpVMDescription = $BackingUpVMs | Where-Object -FilterScript {$_.VM -eq $SCVM}
                                                        Write-Debug -Message ('$BackingUpVMDescription: ''{0}''' -f [string]$BackingUpVMDescription)
                                                        Write-Debug -Message '$null = $BackingUpVMs.Remove($BackingUpVMDescription)'
                                                        $null = $BackingUpVMs.Remove($BackingUpVMDescription)
                                                        Write-Debug -Message ('$BackingUpVMs.VM: ''{0}''' -f [string]$BackingUpVMs.VM.Name)
                                                    }
                                                    Write-Verbose -Message ('VM {0} is unmigratable because of an unsupported status: {1}' -f $SCVM.Name, $SCVirtualMachineLiveMigrationEligibility.Status)
                                                }
                                            }
                                        }
                                    }
                                }
                                else {
                                    Write-Debug -Message ('$Bulletproof: ''{0}''' -f [string]$Bulletproof)
                                    Write-Debug -Message 'if (-not $Bulletproof)'
                                    if (-not $Bulletproof) {
                                        Write-Debug -Message 'Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)'
                                        Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)
                                    }
                                }
                            }

                            Write-Debug -Message 'if (-not $LastVMWasPoweredDown)' # No need to wait if the last job was synchronous
                            if (-not $LastVMWasPoweredDown) {
                                Write-Debug -Message ('Start-Sleep -Seconds {0}' -f $Timeout)
                                Start-Sleep -Seconds $Timeout
                            }
                        }

                        Write-Debug -Message ('$SourceSCVMs: ''{0}''' -f [string]$SourceSCVMs.Name)
                        Write-Debug -Message 'while ($SourceSCVMs)'
                    }
                    while ($SourceSCVMs)

                    Write-Debug -Message 'Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost)'
                    Read-SCVMHosts -VMHost ($SourceVMHost, $DestinationVMHost) # Make sure our VM objects are fresh

                    Write-Debug -Message ('$UnmigratableVMs: ''{0}''' -f [string]$UnmigratableVMs.Name)
                    Write-Debug -Message 'if ($UnmigratableVMs)'
                    if ($UnmigratableVMs) {
                        foreach ($SCVM in $UnmigratableVMs) {
                            Write-Debug -Message ('$SCVM: ''{0}''' -f $SCVM.Name)
                            Write-Debug -Message ('$SCVM.VMHost: ''{0}''' -f [string]$SCVM.VMHost)
                            Write-Debug -Message 'if ($SCVM.VMHost -eq $DestinationVMHost)'
                            if ($SCVM.VMHost -eq $DestinationVMHost) {
                                Write-Debug -Message '$null = $UnmigratableVMs.Remove($SCVM)'
                                $null = $UnmigratableVMs.Remove($SCVM) # If a VM somehow migrated - we do not care how did it do that.
                                Write-Debug -Message ('$UnmigratableVMs: ''{0}''' -f [string]$UnmigratableVMs.Name)
                                Write-Verbose -Message ('VM {0} is not unmigratable anymore - it migrated somehow' -f $SCVM.Name)
                            }
                        }

                        Write-Debug -Message ('$UnmigratableVMs.Count: {0}' -f $UnmigratableVMs.Count)
                        Write-Debug -Message 'if ($UnmigratableVMs.Count -gt 0)'
                        if ($UnmigratableVMs.Count -gt 0) {
                            Write-Debug -Message ('$UnmigratableVMs: ''{0}''' -f [string]$UnmigratableVMs.Name)
                            Write-Debug -Message '$UnmigratableVMs'
                            $UnmigratableVMs
                        }
                    }
                }
            }
            else {
                $Message = 'Source ({0}, ID: {1}) and destination ({2}, ID: {3}) VMM servers are different' -f $SourceVMHost.ServerConnection.FullyQualifiedDomainName, $SourceVMHost.ServerConnection.ManagedComputer.ID, $DestinationVMHost.ServerConnection.FullyQualifiedDomainName, $DestinationVMHost.ServerConnection.ManagedComputer.ID
                $PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.ArgumentException' -ArgumentList $Message), 'ArgumentException', [System.Management.Automation.ErrorCategory]::InvalidArgument, $null)))
            }
        }
        else {
            $Message = 'Source ({0}) and destination ({1}) servers are the same' -f $SourceVMHost, $DestinationVMHost
            $PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.ArgumentException' -ArgumentList $Message), 'ArgumentException', [System.Management.Automation.ErrorCategory]::InvalidArgument, $null)))
        }

        Write-Debug -Message ('EXIT TRY {0}' -f $MyInvocation.MyCommand.Name)
    }
    catch {
        Write-Debug -Message ('ENTER CATCH {0}' -f $MyInvocation.MyCommand.Name)

        Write-Debug -Message ('{0}: $PSCmdlet.ThrowTerminatingError($_)' -f $MyInvocation.MyCommand.Name)
        $PSCmdlet.ThrowTerminatingError($_)

        Write-Debug -Message ('EXIT CATCH {0}' -f $MyInvocation.MyCommand.Name)
    }

    Write-Debug -Message ('EXIT {0}' -f $MyInvocation.MyCommand.Name)
}