There are probably multiple ways to do this, both right and wrong, but, here’s a process that I’ve been using for a while that I’ve recently tweaked to take advantage of new Azure Managed Disks.
Sidebar – standard managed disk warning
Before I go on though, I wanted to issue a quick warning about the differences between standard unmanaged and managed disks. Microsoft will be pushing you to you Managed Disks more and more. Yes, it's a great feature that makes the management of VM disks simpler. The key bit of information though is as follows:
- If you provision an unmanaged disk that is 1Tb in size, but, only uses 100Gb, you are paying for 100Gb of storage costs. So you’re only paying for what you use. [1. Unmanaged disk cost – Azure Documentation]
- If you provision a managed disk that is 1Tb in size, but, only you 10Mb, you will be paying for the privilege of the whole 1Tb disk [2. Managed disk cost – Azure Documentation]
- Additionally, Premium disks, you’re paying for what you provision no matter if its managed or unmanaged
That aside, Managed Disks are a pretty good feature that makes disk and storage account management considerably simpler. If you’re frugal with your VM allocation and have the process to manage people and technology correctly, Managed Disks are great.
The Process
tl;dr
- Create a snapshot in Azure
- Copy the snapshot from snapshot storage location to Blob storage
- Create a new VM instance based on the blob.vhd file
- This blob post outlines the use of managed disks
- However, mounting direct from Blob can also be done
The actual process
I’ve gone through this recently and updated it so that it’s as streamlined, for me, as possible. Again, this is skewed towards managed disk usage, but, can easily be extended to be used with unmanaged disks as well. Let's begin:
Step 0 ?
If you’re wanting to do this to create copies of your VM instances, to scale out your workload, remember to generalise or sysprep your VM instance prior to Step 1. In the example I go into below, my use case was to create a copy of a server from a production environment (VNET and subscription) and move it to different and separate non-production environment (separate VNET and subscription).
Step 1 – Create a snapshot of your VM disk(s)
The first thing we need to do is actually power off your virtual machine instance. I’ve seen that snapshots can happen while the VM instance is running, but, I guess you can call me a little bit more old school, a little bit more on the cautious side when it comes to these sorts of things. I’ve been bitten by this particular bug in the past, unpleasant it was; so I’m inclined to err on the side of caution.
Once the VM instance is offline, go to the Azure Portal and search for “Snapshots”. Create a new snapshot.
- NOTE: snapshots in Azure are done per DISK and not per VM INSTANCE
- Name the snapshot
- Select the subscription where the VM instance is located
- Select the resource group you want to save the snapshot to
- Or create a new one
- Select the snapshot location
- Select the source disk
- If you earlier selected the same resource group where your VM instance is contained, the disk selection will display the resource group member VM instance disks first in the list
- Select the storage type- standard or premium for your snapshot
- I usually just use standard as I’ve not had the need for faster speed premium as yet (that will change one day for sure)
- Create the snapshot
One the snapshot is created, complete this quick next step to generate an export access URL (we’ll need this in step 2):
- Select the snapshot
- From the top menu, select Export
- You’ll be presented with a menu item with a time interval (based in seconds)
- The default is 3600 or 1 hour
- That is fine, but, I like to make that 36000 (add another 0) so that I have a whole day to do this again and again if need be
- Save the generated URL to notepad for later!
Step 2 – Copy the snapshot to Blob
The next part relies on PowerShell. Update the following PowerShell script with your parameters to copy the snapshot to Blob:
$storageAccountName = "
$storageAccountKey = “
$absoluteUri = “https://blahblahblah.blob.core.windows.net/blahblahblah/........”
$destContainer = “
$blobName = “server.vhd”
$destContext = New-AzureStorageContext –StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey
Start-AzureStorageBlobCopy -AbsoluteUri $absoluteUri -DestContainer $destContainer -DestContext $destContext -DestBlob $blobName
Just for your info, here's a quick explanation of the above:
- Storage account name = there storage account where you want to store the VHD
- The storage account key = either the primary or secondary which is used for authentication for accessing the storage account
- The absolute URI = this is the snapshot URI we generated at the end of step 1
- The destination container = where you want to store the VHD. Usually this is either “vhds”, or maybe create one called “snapshots”
- The blob name = the file name of the VHD itself (remember to only use lowercase)
Step 2.5 – Moving around the blob if need be
Before we actually create a new VM instance based on this snapshot blob, there is an additional option we could take. That is, perhaps it would make sense to move the blob to a different subscription. This is particularly handy when you would have a development environment that you would want to move to production. Other use cases might be the inverse- making a replica of a production system for development purposes.
The absolute fastest way to do this, as I don’t like being inefficient here is with the Azure Storage Explorer (ASE) tool. It's an application that provides a quick GUI for completing storage actions. If you add in both the storage accounts in the ASE, you can as easily as this:
- Select the blob from storage account A (in subscription A)
- Select copy from the top menu
- Go to your second storage account (storage account B in perhaps subscription B)
- Go to the relevant container
- Select paste from the top menu
- Wait for the blob to copy
- DONE
It can’t get any simpler or faster than that. I’m sure if you’re command line inclined, you have a quick go-to PowerShell cmdlet for that, but, for me, I’ve found that to be pretty damn quick. So it isn’t broken, why fix it.
Step 3 – Create a new VM with a managed disk based on the snapshot we’ve put into Azure Blob
The final piece of the puzzle, as the cliche would go, is to create a new virtual machine instance. Again, as the wonderfully elusive and vague title of this blog post states, we’ll use PowerShell to do this. Sure, ARM templates would work and likely the Azure Portal can get you pretty far as well. However, again I like to be efficient and I’ve found that the following PowerShell script does this the best.
Additionally, you can change this up to mount the VHD from blob, vs create a new managed disk as well. So, for the purpose of creating a new machine, PowerShell is as flexible as it is fast and convenient.
Here’s the script you’ll need to create the new VM instance:
#Prepare the VM parameters
$rgName = "
$location = "australiaEast"
$vnet = "
$subnet = "/subscriptions/xxxxxxxxx/resourceGroups/
$nicName = "VM01-Nic-01"
$vmName = "VM01"
$osDiskName = "VM01-OSDisk"
$osDiskUri = "https://
$VMSize = "Standard_A1"
$storageAccountType = "StandardLRS"
$IPaddress = "10.10.10.10"
#Create the VM resources
$IPconfig = New-AzureRmNetworkInterfaceIpConfig -Name "IPConfig1" -PrivateIpAddressVersion IPv4 -PrivateIpAddress $IPaddress -SubnetId $subnet
$nic = New-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $rgName -Location $location -IpConfiguration $IPconfig
$vmConfig = New-AzureRmVMConfig -VMName $vmName -VMSize $VMSize
$vm = Add-AzureRmVMNetworkInterface -VM $vmConfig -Id $nic.Id
$osDisk = New-AzureRmDisk -DiskName $osDiskName -Disk (New-AzureRmDiskConfig -AccountType $storageAccountType -Location $location -CreateOption Import -SourceUri $osDiskUri) -ResourceGroupName $rgName
$vm = Set-AzureRmVMOSDisk -VM $vm -ManagedDiskId $osDisk.Id -StorageAccountType $storageAccountType -DiskSizeInGB 128 -CreateOption Attach -Windows
$vm = Set-AzureRmVMBootDiagnostics -VM $vm -disable
#Create the new VM
New-AzureRmVM -ResourceGroupName $rgName -Location $location -VM $vm
Again, let me explain a little the parameters we’ve set that the start of the script:
- $rgName = the resource group where you want to deploy the VM instance
- $location = the Azure region
- $vnet = the virtual network where you want to deploy the VM instance
- $subnet = the subnet where you want to deploy the VM instance
- $nicName = the name of the NIC of the server
- $vmName = the name of the VM instance, the server name
- $osDiskName = the OS disk name
- $osDiskUri = the direct URI/URL to the VHD in your storage account
- $VMSize = the VM size or the service plan for the VM
- $storageAccountType = what type of storage you would like to have
- $IPaddress = the static IP address of the server as I like to do this in Azure, rather than use dynamic IP’s
And that is pretty much that!
Conclusion
It’s Friday in Sydney. It's the pre-kend and it’s a gloomy, cold 9th day of Winter 2017. I hope that the above content helps you out of a jam or gives you the insight you need to run through this process quickly and efficiently. That feeling of giving back, helping. That's that feeling that should warm me up and get me to lunch time! Counting down!
Cheers!