Copying Exported Azure Managed Disks

Of the many benefits of Azure Managed Disks, one of the more important ones is the move away from addressing virtual disks as regular blob URIs in standard and premium storage accounts. Azure storage accounts rely on the use of primary and secondary access keys to secure access to the contents of a storage account and there’s no ability to assign granular access permissions at the blob level. If someone has either the primary or secondary access key for a storage account, they essentially have full access to the contents of that storage account including any virtual disks (VHDs) contained within it. The only way to secure the contents of these VHDs is by implementing Azure Disk Encryption (ADE) so that the virtual disks are encrypted at the OS level and copying them through the use of an access key and the Azure Storage REST APIs won’t allow someone to access the contents unless they also have the encryption key used to secure the data.

Enter Azure Managed Disks. With this feature, there is no longer a way to access the VHD blobs via a URI and access to a storage account. Placement of the VHD blobs on Azure storage is managed by the service and Azure Role-Based Access Control can be used to assign specific permissions for a managed disk to one or more users. Managed Disks exposes a variety of operations including read, write, delete and retrieval (export) of a disk via a shared access signature (SAS) URI. For this last part, an administrator can export the managed disk via the Azure Management Portal and they’re receive a URL to use for copying the managed disk to either an Azure storage account or to a location on their local PC.

Copying the VHD via the SAS URI is a bit tricky if you’re trying to use standard tools like Azcopy. To download the VHD to a local PC you can use the hyperlink provided directly in the portal. To copy the VHD to a storage account, though, you can’t use Azcopy. Azcopy requires both READ and LIST SAS permissions to copy a blob. However, the Azure REST SAS API currently only supports read URIs (https://docs.microsoft.com/en-us/rest/api/manageddisks/disks/disks-grant-access). The easiest way I’ve found to complete the copy is to use the Start-AzureStorageBlobCopy cmdlet. This cmdlet starts an asynchronous copy job using the storage REST APIs and works just fine with the read only SAS URI provided by the Azure Portal. Here’s how to accomplish the task:

First, make sure you’re authenticated to Azure and have selected the proper subscription containing the destination storage account for the VHD:

Login-AzureRmAccount

Select-AzureRmSubscription -SubscriptionName <Subscription Name>

Then, get the access key for the destination storage group and create a context to use for the copy job:

$dstkey = Get-AzureRmStorageAccountKey -StorageAccountName <Destination Storage Account Name> -ResourceGroupName <Destination Storage Account Resource Group>

$dstcontext = New-AzureStorageContext -StorageAccountName <Destination Storage Account Name> -StorageAccountKey $dstkey.Value[0]

Lastly, create the copy job and then run the Get-AzureStorageBlobCopyState with the “WaitforComplete” parameter to provide a visual indication of the progress of the copy:

$copyblob = Start-AzureStorageBlobCopy -AbsoluteUri <Export managed disk URL from Portal – Full URL> -DestContext $dstcontext -DestContainer <Destination Container Name> -DestBlob <Destination VHD Filename>

$copyblob | Get-AzureStorageBlobCopyState –WaitForComplete

Azure VM Recreation Script

There are many tasks associated with virtual machines in Azure which can only be performed by deleting the VM configuration while preserving the underlying virtual hard disks and then recreating the VM configuration with the proper settings. Some of the things that come to mind are:

  • Renaming a VM resource
  • Moving a VM from one VNET to another
  • Moving a VM from one Azure region to another
  • Moving a VM into or out of an availability set
  • Changing a single NIC VM to a multi NIC VM and vice versa

There are likely others, I’m sure. However, these are actually pretty basic needs for most folks and it’s a shame it isn’t easier to do it. I’ve gotten these types of requests so many times that I finally decided to build a script as a basis for enabling them in a semi-automated fashion. Attached is sample script which includes the methodology and was designed to move a VM in one region to another region assuming that someone copied the underlying VHDs to the destination using either AzCopy or Start-AzureStorageBlobCopy.

At a high-level, here’s what the script does:

  1. Authenticates to the specified Azure subscription if necessary
  2. Reads a list of disk names and VHD URIs for the underlying disks
  3. Gets the source VM definition using Get-AzureRmVM
  4. Creates a new VM definition in the destination location
  5. Adds OS Disks and Data Disks using the provided disk mappings
  6. Creates NIC(s) for destination VM – prompts for new IPs for static addresses
  7. Deploys new VM

It goes without saying that the source VM should be stopped when the VHDs are copied and the script is run. There’s no need to delete the source VM until the new one is successfully built. This handles availability sets if the original VM was in one and works for VMs with either managed disks or unmanaged disks. There are some limitations as of now:

  • Doesn’t work with VMs encrypted using Azure Disk Encryption (ADE)
  • Doesn’t move Public IP resources
  • Doesn’t move NSGs if those were assigned to the VM’s NIC(s) directly
  • Will only move the first IP configuration for each NIC
  • Doesn’t recreate any tags associated with the source VM
  • Doesn’t redeploy any extensions present on the source VM

The real idea behind this was that it would serve as a starting point. This same script could easily be used to migrate a VM from standard to premium storage with a few tweaks or to redeploy a VM to a different VNET or availability set if desired. Hopefully folks find it useful as a starting point for their own adventures.

Create VM from VHD v3 (PowerShell script)