Cloning an Azure virtual machine (long)

I needed to move my virtual machine from one MSDN subscription to another. There is a “Move” option in Azure portal, but it works only if you can access both subscriptions from the same account. Alas, I was not so lucky, so I had to do it “the old way”.

The general idea is to copy the virtual disk (VHD) of the old virtual machine to a blob managed by the new subscription and create a VM from it. Unfortunately, there is no “create from custom VHD” option in Azure portal. It can only create virtual machines from predefined images like Ubuntu or Windows Server. So, one has to use Powershell. I followed this guide, but had to do some modifications.

  1. Stop the source virtual machine.
  2. Use AzCopy to copy VHD blob to a container under the new subscription.
  3. Create Subnet, Virtual Network, IP address, Network Security Group, and NIC as described in the guide. You will need an SSH rule instead of the RDP rule, but you can configure it later using the Azure Portal.
  4. Create the OS disk using the command below. DO NOT use the one in the guide.
  5. Continue to create the VM as in the guide.

OS Disk

Azure Virtual Machines have tons of properties, but the most difficult part is the OS Disk. I’ve got a lot of weird error messages before I was done. The magic command is

$vm = Set-AzureRmVMOSDisk -VM $vm -DiskSizeInGB 128 -CreateOption Attach -Linux -Caching ReadWrite -VhdUri $mydiskUri -Name $mydiskname

Strange Errors

It is critical to specify -CreateOption Attach and give it a name. My first attempt actually created a Virtual Machine in a “failed” state, with a message, I quote

OS Provisioning for VM 'vmname' did not finish in the allotted time. However, the VM guest agent was detected running. This suggests the guest OS has not been properly prepared to be used as a VM image (with CreateOption=FromImage). To resolve this issue, either use the VHD as is with CreateOption=Attach or prepare it properly for use as an image.

I also saw a

Cannot attach an existing OS disk if the VM is created from a platform or user image.

and

New-AzureRmVM : The entity name 'osDisk.name' is invalid according to its validation rule:
^[^_\W][\w-._]{0,79}(?<![-.])$.

The root cause of the last one was that the OSDisk name was empty, but hey, why not show a cool regular expression instead? Microsoft is usually pretty good with error messages, but these ones were not really helpful.

Getting to see both subscriptions under same account

Microsoft account system is highly confusing.  The set of resources you see is determined by your login (naturally), whether it’s a work or personal account, and by “directory”. “Work” account does not mean much, somehow my “work” account is still authenticated on the Microsoft server, but that’s another story. There are all kinds of limitations what subscriptions can be seen/administered by whom. I ended up with quite a few permutations:

  • Email = my_personal_email, Work=false, Directory=org1; sees subscription 1
  • Email = my_personal_email, Work=true, Directory=ikriv.com; sees nothing
  • Email = my_personal_email, Work=true, Directory=org2; sees subscription 2
  • Email = my_org1_email, Work=false, Directory=org1; sees subscription 1
  • Email = my_org2_email, Work=false, Directory=org2; sees subscription 2

The only permutation I was unable to get is to have one account that would see both subscription 1 and subscription 2, so I could not use “move” functionality of Azure portal.

Copying your VHD

Azure VM disks are stored as simple blobs in Azure storage, so anyone can copy them as long as they have the blob container access keys. No account information is required.

You cannot copy the VHD while the source VM is running. VHDs tend to be tens of Gigabytes in size. To avoid paying for traffic, you want to keep the destination in the same data center as the source, and you want to use AzCopy utility as opposed to just downloading the data to your machine and uploading it back to Azure. NB: AzCopy is not part of the Azure SDK, it is, at the time of writing, a part of “Azure Storage Tools” download. I am not adding the link here, because it will probably be obsolete in a year or two. Things move fast in Azureland.

Creating the destination VM

Since creating my actual VM took several tries, I am just pasting the commands together. I haven’t tested this script, so use it as a starting point. Be careful to double check all names before running. It is almost impossible to rename anything in Azure once created. E.g. I now have a security group literally named “myNsg” 🙂

$rgpName = 'myResourceGroup' 
$vmName = 'myVM'
$location = 'eastus' 
$vhdUri = 'https://...'
$subnetName = 'mySubNet'
$vnetName = "myVnetName"
$nsgName = "myNsg"
$ipName = "myIP"
$nicName = "myNicName"
$vmName = "myVM"
$vmSize = "Standard_D1_v2"

$singleSubnet = New-AzureRmVirtualNetworkSubnetConfig -Name $subnetName -AddressPrefix 10.0.0.0/24

$vnet = New-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgName -Location $location `
    -AddressPrefix 10.0.0.0/16 -Subnet $singleSubnet

$rdpRule = New-AzureRmNetworkSecurityRuleConfig -Name myRdpRule -Description "Allow RDP" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 110 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationAddressPrefix * -DestinationPortRange 3389

$nsg = New-AzureRmNetworkSecurityGroup -ResourceGroupName $rgName -Location $location `
    -Name $nsgName -SecurityRules $rdpRule

$pip = New-AzureRmPublicIpAddress -Name $ipName -ResourceGroupName $rgName -Location $location -AllocationMethod Static

$nic = New-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $Name `
    -Location $location -SubnetId $vnet.Subnets[0].Id -PublicIpAddressId $pip.Id -NetworkSecurityGroupId $nsg.Id

$vmConfig = New-AzureRmVMConfig -VMName $vmName -VMSize $vmSize

$vm = Set-AzureRmVMOSDisk -VM $vm -DiskSizeInGB 128 -CreateOption Attach -Linux -Caching ReadWrite -VhdUri $mydisk -Name "ikrivblog3"

$vm = New-AzureRmVMConfig -VMName $vmName -VMSize $vmSize
$vm = Add-AzureRmVMNetworkInterface -VM $vm -Id $nic.Id
$vm = Set-AzureRmVMOSDisk -VM $vm -DiskSizeInGB 128 -CreateOption Attach -Linux -Caching ReadWrite -VhdUri $vhdUri
New-AzureRmVM -ResourceGroupName $rgName -Location $location -VM $vm

Conclusion

The world of Azure continues to be confusing and moving fast. You routinely find obsolete screenshots of web pages that no longer exist or look quite different now, and references to commands that no longer work. In a way, Microsoft has adopted the Linux-y development model of “do something now, make it look good later”, which does not always yields the best results. In the end of the day I was able to accomplish the task, but it took me a few hours to get there. Perhaps in the future they will add an option to simply create a VM from VHD and be done with it.

I also wonder what is the way to create N identical virtual machines that perform some kind of  network distributed task, but that’s a matter for another conversation.

Leave a Reply

Your email address will not be published. Required fields are marked *