Moving Azure Virtual Machines to a different region


There are various scenarios in which you would want to move your existing Azure IaaS virtual machines (VMs) from one region to another. For example, if you already deployed in one region, and a new region support was added which is closer to the end users of your application or service. In this scenario, you’d want to move your VMs as is to the new region to reduce latency. Additional reasons would be to improve manageability, or to move for governance reasons.


You could move your VMs using Azure Site Recovery. Though it is the current recommended approach, it still has several downsides:

  1. It has a long list of prerequisites and preparations to complete before you start.
  2. You can move VMs only between any two regions within the same geographic cluster. For example, you cannot move a VM from US to Europe, or from Europe to Australia (check the compatibility matrix for more information on this).
  3. It can create the virtual network for you, but not the NSG, nor the public IP.
  4. It’s free just for the first 31 days


Using an ARM template sounds like an obvious solution. Just export the template from the VM’s resource group, replace some strings and re-deploy the whole thing, right? No.
Exporting the resource group to an ARM template is exactly that. It exports the whole resource group, and nothing but the resource group.
So what happens if the VNET is in a different resource group? Or the resource group contains more than one resource except the one VM we want to move? We might end up creating too many resources than we intended..

Manual Clicking?

Of course, you could go through the whole manual process of:

  1. Stop the virtual machine
  2. Enable the Export function to generate a single URL containing the VHD of the managed disk to migrate
  3. Create a storage account in the target region
  4. Copy the disk (VHD file) from the managed disk to the storage account created in the target region
  5. From the VHD in the storage account, create a managed disk
    Recreate the virtual machine from the managed disk

That’s way too much manual work, and doesn’t include all the prerequisites. For example, do the target vNET and Subnet already exist in the target region? What about IP configuration? Maybe the VM has a Public IP, or an NSG attached to it’s NIC? What if the VM has several datadisks?


This is why I wrote the Move-AzVmToRegion.ps1 PowerShell script. It takes care most of the prerequisites and dependencies mentioned above, and simplifies the move for your VMs.

You need to pass it the VM you wish to move, the target location and the target resource group:

$vm = Get-AzVM -ResourceGroupName 'rg-old-home' -Name 'vm-test'
$vm | .\Move-AzVmToRegion.ps1 -Location 'West Europe' -ResourceGroup 'rg-new-home' -Verbose

If the target region cannot host the VM size (not all sizes exist in all regions), the script will stop the process and return a relevant error message. If the resource group doesn’t exist in the target region, the script will create it.

From that point forward, the script stops the VM if it’s running, reads all the VM’s configuration, creates a temporary storage account and container in the target region (and target resource group), exports and copies the managed disks to vhds, creates new managed disks from the copied vhds, creates the vNET and subnet(s), creates the NIC, IP configuration, and the Public IP and NSG (if needed). After all those are ready, it creates the VM and starts it.

The Move-AzVmToRegion.ps1 script can be downloaded from here:

Please note the requirements at the top of the script (PowerShell 5.1 or above, and some of the Az modules):

#Requires -Version 5.1
#Requires -Module Az.Accounts, Az.Compute, Az.Network, Az.Storage, Az.Resources

Note #1: The IP addresses range in each Azure region is different. This is why moving a resource from region to region will cause the Public IP address to change.

Note #2: There are some things the current version of the script doesn’t completely handle. Like the diagnostics settings, the FQDN of the public IP (if even used), or any extensions attached to the VM.

I’ll be updating the script regularly with the missing capabilities or in case any bugs are found. So be sure to check it out every once in a while.



Leave a Reply