For various reasons, it might be useful to move or copy an Azure VM to a different region within Microsoft Azure. One might think this is a big thing (which might become even bigger if the source VM uses „Managed Disks“), but in fact it is just a „Copypaste“ – if you know how to do it.

The whole process consists of only a few steps (use this as a checklist):

  1. Create Disk Snapshots of the Source VM
  2. Copy the Snapshots to a Destination Region-based Storage Account Container
  3. Add a New Virtual Network in the Destination Region
  4. Create a New VM within the New Resource Group
  5. Attach the Snapshots as Disks to the New VM
  6. Convert the Disks to „Managed Disks“ (if required)
  7. Bonus: Copy a Network Security Group to the Destination Region

Create Disk Snapshots Of The Source VM

Step Zero: perform a cleanup and remove temporary files and folders, old backups etc. …every
byte counts when copying between zones! So if you plan to copy to a different zone (visit https://azure.microsoft.com/en-us/pricing/details/bandwidth/ for details) this might cost some coins per GB.

After the cleanup, creating snapshots is a simple trick: from the Azure Portal select the source VM, select „Disks“, select one of the disks and click „+ Create Snapshot“, and do this for every disk – name them „Snapshot-[VMName]-[DiskDescription]“, for example: „Snapshot-myVM1-OS“ for the System Disk and „Snapshot-myVM1-Data1“ for the first Data Disk. The VM may remain online during this process, but some applications might require the VM to be turned off. Make sure you select „Account type: Premium (SSD)“ if you use Managed Disks / Premium Storage.

Copy the Snapshots to a Destination Region-based Storage Account

Step Zero again: use the Azure Portal to create a new blob container within a Storage Account in the destination region (used in $DestStorageAccountName and $DestStorageContainerName). All additional steps can be done via Power Shell. Keep in mind that the speed between the regions is limited (our tests between US and WestEU were ~500MB / minute, which equals to ~65Mbit/sec) so the Copy Job might take some time. And: once the Snapshot Access expires (Parameter “DurationInSecond”) the Copy Job is cancelled. However, the timeout limit can be set to astronomic values (tried 3.000.000 seconds [~34 days] once), so you should keep the true filesize and speed limit in mind when defining this parameter value. Replace the variables’ values respectively:

# Source Data
$SrcResourceGroupName ="SourceResourceGroupName"
$SnapshotName1 = "Snapshot-myVM1-OS"
$SnapshotName2 = "Snapshot-myVM1-Data1"


# Destination Data
$DestStorageAccountName = "DestinationStorageAccount”
$DestStorageContainerName = "DestinationContainer"
$DestStorageAccountKey = "DestinationStorageAccountAccessKey"
$DestFileName1 = "myNewVM1-OS-VHD.vhd"
$DestFileName2 = "myNewVM1-Data1.vhd"


# Grant Access To The Snapshots
# Might be necessary if your tenant has multiple subscriptions:
$SubscriptionId = "12345-abcdefgh-1234567-abcde"
Select-AzureRmSubscription -SubscriptionId $SubscriptionId

$sas1 = Grant-AzureRmSnapshotAccess -ResourceGroupName $SrcResourceGroupName -SnapshotName $SnapshotName1 -DurationInSecond 3600 -Access Read
$sas2 = Grant-AzureRmSnapshotAccess -ResourceGroupName $SrcResourceGroupName -SnapshotName $SnapshotName2 -DurationInSecond 3600 -Access Read


#Create the context for the storage account which will be used to copy snapshot to the storage account
$DestStorageContext = New-AzureStorageContext –StorageAccountName $DestStorageAccountName -StorageAccountKey $DestStorageAccountKey 


#Copy the snapshot to the storage account -> creates background copy jobs, therefore: quick command execution!
Start-AzureStorageBlobCopy -AbsoluteUri $sas1.AccessSAS -DestContainer $DestStorageContainerName -DestContext $DestStorageContext -DestBlob $DestFileName1
Start-AzureStorageBlobCopy -AbsoluteUri $sas2.AccessSAS -DestContainer $DestStorageContainerName -DestContext $DestStorageContext -DestBlob $DestFileName2


#Use this for Copy Job Monitoring - replace X accordingly - use the largest disk for reference
Get-AzureStorageBlobCopyState -Context $DestStorageContext -Blob $DestFileNameX -Container $DestStorageContainerName


#Use this to cancel the Copy Jobs, if necessary
Get-AzureStorageBlob -Context $DestStorageContext -Container $DestStorageContainerName | Stop-AzureStorageBlobCopy -Force

Add a new virtual network in the Destination Region

You might want to skip this, if you already have one. But keep in mind that, if source and destination network are connected to each other, you might experience DNS problems when starting the cloned VM. It is safer to start the cloned VM in a closed environment.

 

Create a new VM within the new Resource Group

Creating a new VM is, in this specific case, only possible via PowerShell, as you cannot edit the OS disk from the Azure Portal. Make sure you know the VmSize of the source VM:

Get-AzureRmVM -ResourceGroupName $SrcResourceGroupName

Make note of the VmSize – this is later used as “SrcVmSize” – in addition to this, check the “Name” of the Destination Region, which defines the variable “$DestLocationName”:

Get-AzureLocation | Out-GridView

And with this, create a new Public IP, a new Network Interface, define the OS Disk VHD File and define a new VM, adding the required size and the Destination Location. Make sure that the naming of Public IP etc. complies with your naming convention (in this example, it is defined as “MyNewVM1-ip”, “MyNewVM1-nic” etc.):

$DestResourceGroupName = "DestinationResourceGroupName"
$DestVirtualNetworkName = "DestinationVirtualNetworkName"
$DestLocationName = "DestinationName"
$DestVirtualNetwork = Get-AzureRmVirtualNetwork -ResourceGroupName $DestResourceGroupName -Name $DestVirtualNetworkName
$MyNewVM1Name = "MyNewVM1"

$PublicIp = New-AzureRmPublicIpAddress -Name $MyNewVM1Name+"-ip" -ResourceGroupName $DestResourceGroupName -Location $DestLocationName -AllocationMethod Dynamic
$NetworkInterface = New-AzureRmNetworkInterface -ResourceGroupName $DestResourceGroupName  -Name $MyNewVM1Name+"-nic1" -Location $DestLocationName -SubnetId $DestvirtualNetwork.Subnets[0].Id -PublicIpAddressId $PublicIp.Id
$DestVMOSVhd = "https://"+$DestStorageAccountName+".blob.core.windows.net/"+$DestStorageContainerName+"/"+$MyNewVM1Name+"-OS.vhd"

$DestVmConfig = New-AzureRmVMConfig -VMName $MyNewVM1Name -VMSize "<<<<SrcVmSize>>>>"
$DestVmConfig = Set-AzureRmVMOSDisk -VM $DestVmConfig -Name $MyNewVM1Name -VhdUri $DestVMOSVhd -CreateOption Attach -Windows # or -Linux
$DestVmConfig = Add-AzureRmVMNetworkInterface -VM $DestVmConfig -Id $NetworkInterface.Id

$NewVm = New-AzureRmVM -VM $DestVMConfig -Location $DestLocationName -ResourceGroupName $resourceGroupName

In some regions, not all VmSizes are supported – make sure that the required VmSize is available in the required destination region:

Get-AzureRmVMSize $DestLocationName | Out-GridView

If the Source VM Size is not available, make sure that the substituted VMSize supports equal CPU Cores and memory, and, most importantly, the “MaxDataDiskCount” should be equal or larger than the data disk count of your source VM (the OS disk does not count here!). If you want to use managed disks, have a look at the VMSize details web page to see if managed disks are supported.

 

Attach the Data Disks to the new VM

This can be done with the Azure Portal – select the new VM from the Destination Resource Group, select “Disks” and click on “+ Add data disk”. Assign a name according to your naming conventions and select “Storage Blob” as “Source Type”. Browse to the $DestStorageAccountName, open the $DestStorageContainerName and select the appropriate vhd file. Select the proper “OS type” and “Account type”. Do so for every Data Disk that you plan to attach and save your changes.

 

Convert the disks to „Managed Disks“ (if required)

If you plan to use managed disks, use this simple command to convert all attached disks to Managed Disks:

#Stop the VM if running
#Stop-AzureRmVM -ResourceGroupName $DestResourceGroupName -Name $MyNewVM1Name -Force
ConvertTo-AzureRmVMManagedDisk -ResourceGroupName $DestResourceGroupName -VMName $MyNewVM1Name

After this, you’re done with the basics! You can now start the VM, perform a generalization, change hostnames, adapt the internal IP address etc.

 

Bonus: Copy a Network Security Group to the Destination Region

If you have protected your Source VMs with a Network Security Group (NSG) you may want to copy this too to the Destination Region – do it with PowerShell:

$SrcNSGRules =  Get-AzureRmNetworkSecurityGroup -Name „SourceNSGName“ -ResourceGroupName $SrcResourceGroupName | Get-AzureRmNetworkSecurityRuleConfig
$DestNSG = New-AzureRmNetworkSecurityGroup -ResourceGroupName $DestResourceGroupName -Location $DestLocationName -Name „DestinationNSGName“

foreach ($rule in $SrcNSGRules) {

    $DestNSG | Add-AzureRmNetworkSecurityRuleConfig -Name $rule.Name -Direction $rule.Direction -Priority $rule.Priority -Access $rule.Access -SourceAddressPrefix $rule.SourceAddressPrefix -SourcePortRange $rule.SourcePortRange -DestinationAddressPrefix $rule.DestinationAddressPrefix -DestinationPortRange $rule.DestinationPortRange -Protocol $rule.Protocol # -Description $rule.Description
    $DestNSG | Set-AzureRmNetworkSecurityGroup

}

 

After this, assign the NSG to the various VMs via the Azure Portal.

CONGRATULATIONS, YOU’RE DONE!