設定虛擬網路之間的 VM 容錯移轉Configure VM failover between virtual networks

重要

已不再支援此版本的 Virtual Machine Manager (VMM),建議升級至 VMM 2019This version of Virtual Machine Manager (VMM) has reached the end of support, we recommend you to upgrade to VMM 2019.

本文說明在不使用 Azure Site Recovery 服務來管理災害復原的情況下,如何處理虛擬網路之間 System Center - Virtual Machine Manager (VMM) 中的 VM 複寫和容錯移轉。This article describes how to handle replication and failover of VMs in System Center - Virtual Machine Manager (VMM), between virtual networks when you're not using the Azure Site Recovery service to manage disaster recovery.

  • 建議您使用 Azure Site Recovery 來複寫 VM。We recommend that you use Azure Site Recovery for replicating VMs. VMM 必須透過 Site Recovery 才能管理 Hyper-V 複本,而且您需要使用 Hyper-V 複本 PowerShell Cmdlet 才能自動化 Hyper-V 複本作業。VMM doesn't manage Hyper-V Replica without Site Recovery, and you need to use Hyper-V Replica PowerShell cmdlets to automate Hyper-V Replica operations.
  • 針對災害復原,建議您使用個別的主要和次要虛擬網路。For disaster recovery, we recommend that you use separate primary and secondary virtual networks. 主要 VM 連線到主要網路,複本 VM 則連線到次要網路。Primary VMs connect to the primary network, and replica VMs to the secondary network. 這樣可以確保兩部 VM 能同時連線到網路。This ensures that both VMs can be connected to a network at the same time.
  • 如果您使用單一虛擬網路,請使用 Site Recovery 的網路對應功能以自動化網路管理。If you have a single virtual network, use Site Recovery to automate network management using the network mapping feature. 如果您不使用 Site Recovery,您必須仔細地檢查必要條件,以及 VM 附加至網路的順序。If you don't use Site Recovery, you need to carefully check prerequisites, and the order in which VMs are attached to the network. 特別是複本 VM 和主要 VM 絕不能同時連線到單一虛擬網路。In particular, the replica VM and primary VM mustn't be connected to the single virtual network at the same time. 否則,可能會刪除 VMM 中的 CA-PA 記錄,並造成網路失去連線。Otherwise, CA-PA records might get deleted in VMM, and cause network connectivity loss.

範例解決方案Sample solution

此範例解決方案描述以下環境:This sample solution describes the following environment:

  • 單一 VMM 伺服器同時管理主要站台和次要站台。A single VMM server manages both primary and secondary sites.
  • 主要和複本 VM 裝載於單一 Hyper-V 虛擬網路上。Primary and replica VMs are hosted on a single Hyper-V virtual network.
  • 您想要執行計劃的容錯移轉,並在容錯移轉之後保留 VM 的 IP 位址。You want to run a planned failover, and retain the IP address of the VM after the failover.
  • VM 使用 IPv4 位址。VMs have IPv4 addresses.

在您開始使用 Intune 之前Before you start

  • 確定虛擬交換器和邏輯交換器的設定正確,並與 VMM 網狀架構相符。Make sure that the virtual switch and logical switch settings are valid and match in the VMM fabric. 如果它們的設定不正確或不相符,則在容錯移轉後可能無法進行網路附加作業。If they don't, network attach operations might not succeed after failover.

  • 主要 VM 應該連線到虛擬網路The primary VM should be connected to a virtual network

  • 複本 VM 不應連線到網路The replica VM shouldn't be connected to a network

  • 針對主要 VM 的每個網路介面卡,應該僅指派單一 IP 位址。Only one IP address should be assigned to each network adapter of the primary VM. 執行以下命令以確實達成此目的。Run this command to ensure this. 如果 VM 上有多個已連線的網路介面卡,請變更陣列索引來針對介面卡執行命令。If there's more than one connected network adapter on the VM, run it for adapter by changing the array index.

    $VMOnPD = Get-SCVirtualMachine -Name "VM Name" | where {$_.IsPrimaryVM -eq $true}
    Get-SCIPAddress –GrantToObjectId $VMOnPD.VirtualNetworkAdapters[0].ID``
    
  • 確定作業系統指派給 VM 的 IP 位址與上方顯示的 IP 位址相同。Make sure that the IP address assigned to the VM by the operating system is the same as the IP address shown above. 登入 VM 並執行 ipconfig 來確認此項目。Log onto the VM and run ipconfig to check this.

  • 確認查閱資料表已在主要 VM 和複本 VM 上正確設定。Check that lookup tables are correctly set on the primary and replica . 若要這麼做,請在每部伺服器上執行下列命令,並確定有和上方傳回的 IP 位址相對應的項目:Get-NetVirtualizationLookupRecordTo do this, run the following command on each server, and ensure that there is an entry that corresponds to the IP address returned above: Get-NetVirtualizationLookupRecord

  • 檢查 IP 位址是否為 IPv4,而非 IPv6Check that the IP address is IPv4, and not IPv6

  • 在執行指令碼之前,請確定兩部 VM 都已經關閉。Make sure both VMs are turned off before you run the scripts.

  • 確定兩部 VM 上的複寫狀態都已啟用。Make sure that the replication state is enabled on both VMs.

執行計劃的容錯移轉指令碼Run the planned failover script

以下是指令碼會執行的項目:Here's what this script does:

  1. 針對主要 VM 上的每個網路介面卡,指令碼會儲存 IP 位址、VM 網路和 IP 集區。For each network adapter on a primary VM, it stores the IP address, VM network, and IP pool.
  2. 撤銷主要和次要 VM 上每個網路介面卡的所有 IP 位址。Revokes all of the IP addresses for each network adapter on the primary and secondary VMs.
  3. 中斷所有網路介面卡連線。Disconnects all network adapters.
  4. 容錯移轉主要和次要 VM。Fails over the primary and secondary VMs.
  5. 選擇性啟動反向複寫。Optionally starts reverse replication.
  6. 為複本 VM (針對給每個網路介面卡) 提供相同的 IP 位址。Gives the same IP address (for each network adapter) to the replica VM.
  7. 將複本 VM 上的每個網路介面卡附加至於步驟 1 儲存的 VM 網路。Attaches each network adapter on the replicate VM to the VM networks that were stored in step 1.

執行指令碼Run the script

此指令碼接受兩個引數:The script takes two arguments:

  • $VMName:虛擬機器的名稱$VMName – Name of the virtual machine
  • $ReverseRep:布林值引數,可以指定是否要執行反向複寫。$ReverseRep – Boolean argument to specify whether reverse replication should be performed or not.
    • 如果傳遞 $true,則會立即開始反向複寫,而且您稍後無法取消容錯移轉。If $true is passed then the reverse replication is started immediately, and you can't cancel failover later.
    • 在 $ReverseRep 為 $true 的情況下成功完成此指令碼之後:After this script is completed successfully with $ReverseRep as $true:
      • 主要 VM 應該處於「已準備進行計劃的容錯移轉」 複寫狀態。The primary VM should be in a Prepared for planned failover replication state.
      • 複本 VM 應該處於「容錯移轉完成」 複寫狀態The replica VM should be in a Failover complete replication state
    • 如果傳遞 $false,則不會執行反向複寫。If $false is passed, then reverse replication won't be performed. ReverseRepORCancelFO.ps1 可用於執行反向複寫或取消容錯移轉ReverseRepORCancelFO.ps1 can be used to either perform reverse replication or cancel the failover
    • 在 $ReverseRep 為 $false 的情況下成功完成指令碼之後:After the script is completed successfully with $ReverseRep as $false:
      • 主要 VM 應該處於「已準備進行計劃的容錯移轉」 複寫狀態The primary VM should be in a Prepared for planned failover replication state
      • 複本 VM 應該處於「容錯移轉完成」 複寫狀態。The replica VM should be in a Failover complete replication state.

如果指令碼未完成任何步驟,您需要手動完成失敗的步驟,然後返回 PowerShell 視窗。If the script doesn't complete any of the steps, you need to manually complete the failed steps, and then return to the PowerShell window. 指令碼步驟包含主要 VM 的容錯移轉、複本 VM 的容錯移轉,以及選擇性的反向複寫。The script steps include failover of the primary VM, failover of the replica VM, and optionally, reverse replication.

執行指令碼:Run the script:

 Param(
 [Parameter(Mandatory=$True)]
   [string]$VMName,
 [Parameter(Mandatory=$true)]
   [boolean]$ReverseRep
)

# the script running on system with SCVMM Console/PowerShell installed. Also, requires Hyper-V powershell module.``

Import-Module hyper-v

## Refresh VM configuration and initialize
Write-Host -ForegroundColor Green (Get-Date) ".....Refreshing the VMs..."
Get-SCVirtualMachine -Name $VMName | Read-SCVirtualMachine

$VMOnPD = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $true}
$VMOnDR = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $false}

if ($VMOnPD.StatusString -ne "Stopped")
{
    write-host -ForegroundColor Red (Get-Date) "....VM is not in stopped state. Actual State " $VMOnPD.StatusString
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$error.Clear()
$VMRepConfig = Get-VMReplication -ComputerName $VMOnPD.HostName -VMName $VMOnPD.Name
$VMRepConfig = Get-VMReplication -ComputerName $VMOnDR.HostName -VMName $VMOnPD.Name

if ($error -ne 0)
{
    $temp = $VMOnPD.HostName.Split(".")
    $primaryHostName = $temp[0]

    $temp = $VMOnDR.HostName.Split(".")
    $recoveryHostName = $temp[0]

    write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using FQDN, switching to Hostname"
    write-host -ForegroundColor Yellow (Get-Date) "....Primary Hostname: " $primaryHostName " Replica Hostname: " $recoveryHostName

    $error.Clear()
    $VMRepConfig = Get-VMReplication -ComputerName $primaryHostName -VMName $VMOnPD.Name
    $VMRepConfig = Get-VMReplication -ComputerName $recoveryHostName -VMName $VMOnPD.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using Hostname"
        write-host -ForegroundColor Red (Get-Date) "....Exiting"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) "....Successful in getting VM Replication state using Hostname"
}
else
{
    $primaryHostName = $VMOnPD.HostName
    $recoveryHostName = $VMOnDR.HostName
}

$VMOnPDAdapter = Get-SCVirtualNetworkAdapter -VM $VMonPD
$VMOnDRAdapter = Get-SCVirtualNetworkAdapter -VM $VMonDR

$fileName = $VMName + (Get-Date).ToString() + ".txt"
$fileName = $fileName.Replace("/","_")
$fileName = $fileName.Replace(":","_")

Write-Host -ForegroundColor Yellow (Get-Date) "....Dumping network information for $VMName to file $fileName"
Write-Host -ForegroundColor Yellow (Get-Date) "....Number of Network adapters found: " $VMOnPDAdapter.count

$VMNetwork = @()
$VMSubnet = @()
$Pools = @()

$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{
    if ($vmAdapter.VMNetwork -eq $null)
    {
        $VMNetwork = $VMNetwork + $null
        $VMSubnet = $VMSubnet + $null
        $Pools = $Pools + $null
        $counter = $counter + 1
        continue
    }

    $VMNetwork = $VMNetwork + (Get-SCVMNetwork -Name $vmAdapter.VMNetwork.Name -ID $vmAdapter.VMNetwork.ID)
    $VMSubnet = $VMSubnet + (Get-SCVMSubnet -Name $vmAdapter.VMSubnet.Name | where {$_.VMNetwork.ID -eq $vmAdapter.VMNetwork.ID})
    #$PortClassification = Get-SCPortClassification | where {$_.Name -eq "Guest Dynamic IP"}
    $Pools = $Pools + (Get-SCStaticIPAddressPool -IPv4 | where {$_.VMsubnet.name -eq $vmAdapter.VMSubnet.Name})

    Out-File -FilePath $fileName -InputObject $VMNetwork[$counter] -Append
    Out-File -FilePath $fileName -InputObject $VMSubnet[$counter] -Append
    Out-File -FilePath $fileName -InputObject $Pools[$counter] -Append

    $counter = $counter + 1
}

if ($error.Count -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) "....Error is gathering information for $VMName. No changes made"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$IP = @()
$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $IP = $IP + $null
        $counter = $counter + 1
        continue
    }

    ## Revoke IP
    $error.Clear()
    $IP = $IP +(Get-SCIPAddress –GrantToObjectId $VMOnPD.VirtualNetworkAdapters[$counter].ID)
    Write-Host -ForegroundColor Yellow (Get-Date) "....Revoking IP " $IP[$counter] "from Primary VM"
    Revoke-SCIPAddress $IP[$counter]
    if ($error.count -eq 0)
    {
        Write-Host -ForegroundColor Green (Get-Date) "....." $IP[$counter] "revoke completed"
    }

    ## Disconnect Primary VM
    Write-Host -ForegroundColor Yellow (Get-Date) "....Disconnecting Primary VM from Network " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnPD.VirtualNetworkAdapters[$counter] -NoLogicalNetwork -NoConnection -NoPortClassification
    Write-Host -ForegroundColor Green (Get-Date) "....Network Adapter '" $counter "' of Primary VM Disconnected"

    $counter = $counter + 1
}

## Start failover
Write-Host -ForegroundColor Yellow (Get-Date) ".....We are going to Failover " $VMName " from " $primaryHostName " to " $recoveryHostName

$error.Clear()
Start-VMFailover -ComputerName $primaryHostName -VMName $VMOnPD.Name -Prepare -Confirm:$false

start-sleep 5

Write-Host -ForegroundColor Yellow (Get-Date) ".....Completing Failover on Replica site..."
Start-VMFailover -ComputerName $recoveryHostName -VMName $VMOnDR.Name -Confirm:$false
if ($ReverseRep)
{
    write-host -ForegroundColor Green (Get-Date) ".....Starting Reverse Replication..."
    Set-VMReplication -ComputerName $recoveryHostName -reverse -VMName $VMOnDR.Name
}

if ($error -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) ".....Error occured during Planned Failover for VM $VMName"
    write-host -ForegroundColor Red (Get-Date) ".....Please manually complete Failover before continuing"
    Write-Host -ForegroundColor Red (Get-Date) ".....Press any key to continue..."
    $ignoreKey = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

Write-Host -ForegroundColor Green (Get-Date) ".....Connecting Network(s) to Failed-over VM"

$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $counter = $counter + 1
        continue
    }

    Write-Host -ForegroundColor Yellow (Get-Date) "Granting " $IP[$counter] "to Failed-over VM"
    Grant-SCIPAddress -GrantToObjectType "VirtualNetworkAdapter" -GrantToObjectID $VMOnDRAdapter[$counter].ID -StaticIPAddressPool $Pools[$counter] –IPAddress $IP[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Granting IP completed"

    Write-Host -ForegroundColor Yellow (Get-Date) "Connecting Replica VM to " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnDRAdapter[$counter] -IPv4AddressType static -VMNetwork $VMNetwork[$counter] -VMSubnet $VMSubnet[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Network Adapter '" $counter "' of Failed-over VM connected to " $VMNetwork[$counter]

    $counter = $counter + 1
}

執行反向複寫/取消指令碼Run the reverse replication/cancel script

以下是指令碼會執行的項目:Here's what this script does:

  1. 如果您並未在容錯移轉指令碼中執行反向複寫,您可以使用此指令碼進行反向複寫,或是取消容錯移轉。If you didn't run reverse replication in the failover script, you can use this script for reverse replication, or to cancel the failover.
  2. 如果您取消,則指令碼在中斷複本 VM 網路後,會反轉網路步驟,並還原主要 VM 的連線。If you cancel, the script reverses the networking steps, and restores the primary VM connections, after disconnecting the replica VM networks.

執行指令碼Run the script

這個指令碼應該在容錯移轉指令碼搭配 $ReverseRep 設定為 $false 的情況下執行。此指令碼接受三個引數:This script should be run for the failover script with $ReverseRep set to $false .This script takes three arguments:

  • $VMName:VM 名稱$VMName: VM name
  • $ReverseRep:布林值引數,可以指定是否要執行反向複寫。$ReverseRep: Boolean argument to specify whether reverse replication should be performed. $true 表示會執行反向複寫。$true indicates that reverse replication runs.
  • $CancelFO:布林值引數,可以指定是否要取消容錯移轉。$CancelFO - Boolean argument to specify whether the failover is cancelled. $true 表示會取消主要站台和復原站台上的容錯移轉。$true indicates cancellation on primary and recovery sites.

同時只能針對 $ReverseRep 和 $CancelFO 傳遞 $true。One and only one of $ReverseRep and $CancelFO can be passed $true at a time. 指令碼成功執行之後,兩部 VM 的狀態都應該是「已啟用複寫」 。After the script runs successfully, the state on both VMs should be Replication enabled’.

執行指令碼:Run the script:

Param(
 [Parameter(Mandatory=$True)]
   [string]$VMName,
 [Parameter(Mandatory=$true)]
   [boolean]$ReverseRep,
 [Parameter(Mandatory=$true)]
   [boolean]$CancelFO
)

# the script running on system with SCVMM Console/PowerShell installed. Also, requires Hyper-V powershell module.

Import-Module hyper-v

if ($ReverseRep -eq $CancelFO)
{
    write-host -ForegroundColor Red (Get-Date) "....Please ensure that one and only one of the parameters -ReverseRep and -CancelFO is passed as $True"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

## Refresh VM configuration and initialize
Write-Host -ForegroundColor Green (Get-Date) ".....Refreshing the VMs..."
Get-SCVirtualMachine -Name $VMName | Read-SCVirtualMachine

$VMOnPD = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $true}
$VMOnDR = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $false}

$error.Clear()
$VMRepConfig = Get-VMReplication -ComputerName $VMOnPD.HostName -VMName $VMOnPD.Name
$VMRepConfig = Get-VMReplication -ComputerName $VMOnDR.HostName -VMName $VMOnPD.Name

if ($error -ne 0)
{
    $temp = $VMOnPD.HostName.Split(".")
    $primaryHostName = $temp[0]

    $temp = $VMOnDR.HostName.Split(".")
    $recoveryHostName = $temp[0]

    write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using FQDN, switching to Hostname"
    write-host -ForegroundColor Yellow (Get-Date) "....Primary Hostname: " $primaryHostName " Replica Hostname: " $recoveryHostName

    $error.Clear()
    $VMRepConfig = Get-VMReplication -ComputerName $primaryHostName -VMName $VMOnPD.Name
    $VMRepConfig = Get-VMReplication -ComputerName $recoveryHostName -VMName $VMOnPD.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using Hostname"
        write-host -ForegroundColor Red (Get-Date) "....Exiting"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) "....Successful in getting VM Replication state using Hostname"
}
else
{
    $primaryHostName = $VMOnPD.HostName
    $recoveryHostName = $VMOnDR.HostName
}

if ($VMOnDR.ReplicationStatus.ReplicationState -ne "Recovered")
{
    write-host -ForegroundColor Red (Get-Date) "....Replica VM is not in Failed over state. Actual State " $VMOnDR.ReplicationStatus.ReplicationState
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$error.Clear()

if ($ReverseRep -eq $true)
{
    write-host -ForegroundColor Green (Get-Date) ".....Starting Reverse Replication..."
    Set-VMReplication -ComputerName $recoveryHostName -reverse -VMName $VMOnDR.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) ".....Error occured during Reverse Replication for VM $VMName"
        write-host -ForegroundColor Red (Get-Date) ".....Please manually complete Reverse replication"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) ".....Reverse Replication completed..."
    exit 0
}

if ($VMOnDR.StatusString -ne "Stopped")
{
    write-host -ForegroundColor Red (Get-Date) "....VM is not in stopped state. Actual State " $VMOnDR.StatusString
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$VMOnPDAdapter = Get-SCVirtualNetworkAdapter -VM $VMonPD
$VMOnDRAdapter = Get-SCVirtualNetworkAdapter -VM $VMonDR

$fileName = $VMName + (Get-Date).ToString() + ".txt"
$fileName = $fileName.Replace("/","_")
$fileName = $fileName.Replace(":","_")

Write-Host -ForegroundColor Yellow (Get-Date) "....Dumping network information for $VMName to file $fileName"
Write-Host -ForegroundColor Yellow (Get-Date) "....Number of Network adapters found on Failed-over VM: " $VMOnDRAdapter.count

$VMNetwork = @()
$VMSubnet = @()
$Pools = @()

$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{
    if ($vmAdapter.VMNetwork -eq $null)
    {
        $VMNetwork = $VMNetwork + $null
        $VMSubnet = $VMSubnet + $null
        $Pools = $Pools + $null
        $counter = $counter + 1
        continue
    }

    $VMNetwork = $VMNetwork + (Get-SCVMNetwork -Name $vmAdapter.VMNetwork.Name -ID $vmAdapter.VMNetwork.ID)
    $VMSubnet = $VMSubnet + (Get-SCVMSubnet -Name $vmAdapter.VMSubnet.Name | where {$_.VMNetwork.ID -eq $vmAdapter.VMNetwork.ID})
    #$PortClassification = Get-SCPortClassification | where {$_.Name -eq "Guest Dynamic IP"}
    $Pools = $Pools + (Get-SCStaticIPAddressPool -IPv4 | where {$_.VMsubnet.name -eq $vmAdapter.VMSubnet.Name})

    Out-File -FilePath $fileName -InputObject $VMNetwork[$counter] -Append
    Out-File -FilePath $fileName -InputObject $VMSubnet[$counter] -Append
    Out-File -FilePath $fileName -InputObject $Pools[$counter] -Append

    $counter = $counter + 1
}

if ($error.Count -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) "....Error is gathering information for $VMName. No changes made"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$IP = @()
$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $IP = $IP + $null
        $counter = $counter + 1
        continue
    }

    ## Revoke IP
    $error.Clear()
    $IP = $IP +(Get-SCIPAddress –GrantToObjectId $VMOnDR.VirtualNetworkAdapters[$counter].ID)
    Write-Host -ForegroundColor Yellow (Get-Date) "....Revoking IP " $IP[$counter] "from Replica VM"
    Revoke-SCIPAddress $IP[$counter]
    if ($error.count -eq 0)
    {
        Write-Host -ForegroundColor Green (Get-Date) "....." $IP[$counter] "revoke completed"
    }

    ## Disconnect Replica VM
    Write-Host -ForegroundColor Yellow (Get-Date) "....Disconnecting Replica VM from Network " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnDR.VirtualNetworkAdapters[$counter] -NoLogicalNetwork -NoConnection -NoPortClassification
    Write-Host -ForegroundColor Green (Get-Date) "....Network Adapter '" $counter "' of Replica VM Disconnected"

    $counter = $counter + 1
}

## Cancel failover
Write-Host -ForegroundColor Yellow (Get-Date) ".....We are going to Cancel Failover " $VMName " on both " $primaryHostName " and " $recoveryHostName

$error.Clear()
Stop-VMFailover -ComputerName $recoveryHostName -VMName $VMName
Start-Sleep -Seconds 10
Stop-VMFailover -ComputerName $primaryHostName -VMName $VMName

if ($error -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) ".....Error occured during Cancel Failover for VM $VMName"
    write-host -ForegroundColor Red (Get-Date) ".....Please manually Cancel Failover on both Primary and Recovery Server"
    Write-Host -ForegroundColor Red (Get-Date) ".....Press any key to continue..."
    $ignoreKey = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

Write-Host -ForegroundColor Yellow (Get-Date) ".....Connecting Network(s) back to the Primary VM"

$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $counter = $counter + 1
        continue
    }

    Write-Host -ForegroundColor Yellow (Get-Date) "Granting " $IP[$counter] "to Primary VM"
    Grant-SCIPAddress -GrantToObjectType "VirtualNetworkAdapter" -GrantToObjectID $VMOnPDAdapter[$counter].ID -StaticIPAddressPool $Pools[$counter] –IPAddress $IP[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Granting IP completed"

    Write-Host -ForegroundColor Yellow (Get-Date) "Connecting Primary VM to " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnPDAdapter[$counter] -IPv4AddressType static -VMNetwork $VMNetwork[$counter] -VMSubnet $VMSubnet[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Network Adapter '" $counter "' of Primary VM connected to " $VMNetwork[$counter]

    $counter = $counter + 1
}