
Cloning Windows Virtual Machine in Azure without having to stop it
Zero downtime cloning of Virtual Machine in Azure using PowerShell
Creating a copy of an existing Virtual Machine on Azure is not really that much straight forward as you might think. The proper way as described in the documentation is to "generalize" Virtual Machine which puts it into a state where it can be used as a template for creating new Virtual Machines.
Unfortunately this way of creating a clone of VM on Azure makes the source VM inaccessible. This can be a big problem especially if you wan to clone an existing VM for the purpose of scaling them behind the load balancer or you just want to make an availability set to avoid any downtime during deployment.
You can always make a Snapshot of OS disk of the running VM and then create a boot-able Managed Disk, but unfortunately, Azure UI still does not support creating a VM and attaching and existing Managed Disk as a bootable one. Luckily all these options are available from the Cloud Shell directly from the Azure portal as an in-browser terminal.
Azure Cloud Shell supports both bash and PowerShell, so you can use the one you prefer. For this case I will stick to PowerShell.
The idea behind this approach of cloning an existing VM is to:
- Create a Snapshot of OS Managed disk of an existing VM
- Create instance of Managed Disk from previously created Snapshot
- Allocate static IP and Network Adapter instances for the new VM
- Configure new VM by attaching Managed Disk, static IP and Network Adapter resources to the VM configuration instance
- Create the VM using the configuration
- Delete the Snapshot of the disk
These are the exact steps we are going to follow in the PowerShell script we are going to use to clone the existing VM instance.
Since we are technically cloning only the disk of the existing VM, other elements of the VM (Static IP, Network Adapter, Virtual Machine Size, Geo Location...) are something we will create in the fly and are not exactly clones of the elements of existing VM, but once started you will have a clone of the Operating System running on the source VM you cloned from
In order to create resources for the new VM, you will need few values which you can find in the properties of the existing VM from Azure UI, but they might be a bit different than the ones you use in the script. Simple reason is that values shown in Azure UI are display names of these elements, while the actual name has a bit different value. You can search Azure documentation for this, but you can always use PowerShell to pull out these values.
Two main values you are going to need for creating a new VM are Azure data center geo Location where VM will be created and Size of a new VM
Azure geo location values
Microsoft keeps adding new data centers and therefore new locations keep getting onto the data center locations list. You might want to create a cloned VM in a different region to achieve geo redundancy and lower down the latency.
Here is how to get the list of data center geo locations currently available in Azure using PowerShell in Azure Cloud Shell in-browser console
Get-AzureRmLocation |Format-Table
This will produce the output as the following
-------- ----------- ---------
eastasia East Asia {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
southeastasia Southeast Asia {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.AppConfiguration…}
centralus Central US {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.AppConfiguration…}
eastus East US {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.AppConfiguration…}
eastus2 East US 2 {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
westus West US {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.AppConfiguration…}
northcentralus North Central US {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
southcentralus South Central US {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
northeurope North Europe {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
westeurope West Europe {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.AppConfiguration…}
japanwest Japan West {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
japaneast Japan East {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
brazilsouth Brazil South {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
australiaeast Australia East {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.AppConfiguration…}
australiasoutheast Australia Southeast {Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql, Microsoft.ContainerRegistry…}
southindia South India {microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql…}
centralindia Central India {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
westindia West India {microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql…}
canadacentral Canada Central {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
canadaeast Canada East {Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql, Microsoft.ContainerRegistry…}
uksouth UK South {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
ukwest UK West {Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql, Microsoft.ContainerRegistry…}
westcentralus West Central US {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.AppConfiguration…}
westus2 West US 2 {Microsoft.CognitiveServices, microsoft.visualstudio, Microsoft.ServiceBus, Microsoft.Storage…}
koreacentral Korea Central {Microsoft.CognitiveServices, Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql…}
koreasouth Korea South {Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql, Microsoft.Web…}
francecentral France Central {Microsoft.CognitiveServices, Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql…}
francesouth France South {Microsoft.ClassicInfrastructureMigrate, Microsoft.DeploymentManager, Microsoft.DevTestLab, Microsoft.Kusto…
australiacentral Australia Central {Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql, Microsoft.Web…}
australiacentral2 Australia Central 2 {Microsoft.ServiceBus, Microsoft.Sql, microsoft.insights, Microsoft.ClassicInfrastructureMigrate…}
uaecentral UAE Central {Microsoft.Security, Microsoft.SqlVirtualMachine, Sendgrid.Email}
uaenorth UAE North {Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql, Microsoft.ContainerRegistry…}
southafricanorth South Africa North {Microsoft.CognitiveServices, Microsoft.ServiceBus, Microsoft.Storage, Microsoft.Sql…}
southafricawest South Africa West {Microsoft.Databricks, Microsoft.DBforMariaDB, Microsoft.DBforMySQL, Microsoft.DBforPostgreSQL…}
germanynorth Germany North {Microsoft.ServiceBus}
germanywestcentral Germany West Central {Microsoft.ServiceBus, microsoft.insights, Microsoft.EventHub, Microsoft.ManagedIdentity}
Azure VM size values
Another value you will need for the new VM is the Size. Virtual Machine sizes are basically short names for the predefined configurations. Each size name is associated with the configuration parameters which involve number of VCPUs and RAM size among others.
VM size directly affects the computing price, so it is important to pick the size that will work for you performance wise and at the same time cost effective. For Windows VMs, you can find list of VM Sizes on page Sizes for Windows virtual machines in Azure or just run the PoweShell command in Azure Cloud Shell console.
Get-AzureRmVmSize -location "eastus2"
You notice that Location parameter is required in order to list the VM sizes. This is because not all VM sizes are available in all regions, so when creating you new VM, once you pick the location, you need to use the VM size available in that location.
The output of the VM sizes fetch PowerShell script looks as following.
---- ------------- ---------- ---------------- -------------- --------------------
Standard_A0 1 768 1 1047552 20480
Standard_A1 1 1792 2 1047552 71680
Standard_A2 2 3584 4 1047552 138240
Standard_A3 4 7168 8 1047552 291840
Standard_A5 2 14336 4 1047552 138240
Standard_A4 8 14336 16 1047552 619520
Standard_A6 4 28672 8 1047552 291840
Standard_A7 8 57344 16 1047552 619520
Basic_A0 1 768 1 1047552 20480
Basic_A1 1 1792 2 1047552 40960
Basic_A2 2 3584 4 1047552 61440
Basic_A3 4 7168 8 1047552 122880
Basic_A4 8 14336 16 1047552 245760
Standard_D1_v2 1 3584 4 1047552 51200
Standard_D2_v2 2 7168 8 1047552 102400
Standard_D3_v2 4 14336 16 1047552 204800
Standard_D4_v2 8 28672 32 1047552 409600
Standard_D5_v2 16 57344 64 1047552 819200
Standard_D11_v2 2 14336 8 1047552 102400
Standard_D12_v2 4 28672 16 1047552 204800
Standard_D13_v2 8 57344 32 1047552 409600
VM sizes availability across location is changing over time, so make sure you get the proper VM size name with the PowerShell command from above to avoid VM clone script failure. This is especially important for promo VM sizes which are not available at all time.
Microsoft keeps updating Azure and some of the output values for regions and VM sizes might change or be expanded over time. To make sure you have the correct value for the PowerShell script to run, make sure you run the value fetching script in Azure Cloud Shell to have the latest values
Azure VM clone PowerShell script
As I mentioned above, we are going to follow few basic steps to achieve new VM that boots up the same copy of the OS from the point of time when the OS disk Snapshot was made. This approach will allow us to maintain 100% uptime of the source VM without having to "generalize" it.
Here is the actual script that will perform the VM clone.
#Existing virtual network where new virtual machine will be created $virtualNetworkName = '<Virtual Network Name>' #Resource group of the VM to be clonned from $resourceGroupName = '<Resource Group Name>' #Region where managed disk will be created $location = '<Geo Location>' #Names of source and target (new) VMs $sourceVirtualMachineName = '<Source VM Name>' $targetVirtualMachineName = '<Target VM Name>' #Name of snapshot which will be created from the Managed Disk $snapshotName = $sourceVirtualMachineName + '_OsDisk-snapshot' #Name of the new Managed Disk $diskName = $targetVirtualMachineName + '_OsDisk' #Size of new Managed Disk in GB $diskSize = 128 #Storage type for the new Managed Disk (Standard_LRS / Premium_LRS / StandardSSD_LRS) $storageType = '<Storage Type>' #Size of the Virtual Machine (https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sizes) $targetVirtualMachineSize = '<Target VM Size>' #Set the subscription for the current session where the commands wil execute Select-AzureRmSubscription -SubscriptionId '<Subscription Id>' #Get the existing VM from which to clone from $sourceVirtualMachine = Get-AzVM -ResourceGroupName $resourceGroupName -Name $sourceVirtualMachineName #Create new VM Disk Snapshot $snapshot = New-AzSnapshotConfig -SourceUri $sourceVirtualMachine.StorageProfile.OsDisk.ManagedDisk.Id -Location $location -CreateOption copy $snapshot = New-AzSnapshot -Snapshot $snapshot -SnapshotName $snapshotName -ResourceGroupName $resourceGroupName #Create a new Managed Disk from the Snapshot $disk = New-AzureRmDiskConfig -AccountType $storageType -DiskSizeGB $diskSize -Location $location -CreateOption Copy -SourceResourceId $snapshot.Id $disk = New-AzureRmDisk -Disk $disk -ResourceGroupName $resourceGroupName -DiskName $diskName #Initialize virtual machine configuration $targetVirtualMachine = New-AzureRmVMConfig -VMName $targetVirtualMachineName -VMSize $targetVirtualMachineSize #Attach Managed Disk to target virtual machine. OS type depends OS present in the disk (Windows/Linux) $targetVirtualMachine = Set-AzureRmVMOSDisk -VM $targetVirtualMachine -ManagedDiskId $disk.Id -CreateOption Attach -Windows #Create a public IP for the VM $publicIp = New-AzureRmPublicIpAddress -Name ($targetVirtualMachineName.ToLower() + '_ip') -ResourceGroupName $resourceGroupName -Location $location -AllocationMethod Dynamic #Get Virtual Network information $vnet = Get-AzureRmVirtualNetwork -Name $virtualNetworkName -ResourceGroupName $resourceGroupName # Create Network Interface for the VM $nic = New-AzureRmNetworkInterface -Name ($targetVirtualMachineName.ToLower() + '_nic') -ResourceGroupName $resourceGroupName -Location $location -SubnetId $vnet.Subnets[0].Id -PublicIpAddressId $publicIp.Id $targetVirtualMachine = Add-AzureRmVMNetworkInterface -VM $targetVirtualMachine -Id $nic.Id #Create the virtual machine with Managed Disk attached New-AzureRmVM -VM $targetVirtualMachine -ResourceGroupName $resourceGroupName -Location $location #Remove the snapshot Remove-AzureRmSnapshot -ResourceGroupName $resourceGroupName -SnapshotName $snapshotName -Force
You can safely copy the whole script and paste it into Azure Cloud Shell console. The lines will be executed one by one and in the end you will get a new VM instance which runs a copy of the OS state from the source VM.
Alternatively, you can upload the script file from the Cloud Shell toolbar and execute it
Executing the script may take some time as it is initiating the resource provisioning in Azure. Creating of a VM especially take most of the time of script run.
References
- Windows Virtual Machines Documentation
- Remove-AzureRmSnapshot
- Select-AzureRmSubscription
- Get-AzVM
- New-AzSnapshotConfig
- New-AzSnapshot
- New-AzureRmDiskConfig
- New-AzureRmDisk
- New-AzureRmVMConfig
- Set-AzureRmVMOSDisk
- New-AzureRmVM
Disclaimer
Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.
Comments for this article