This past week I needed to move a client’s Hyper-V machine to a Windows Azure VM.  If you haven’t figured it out already, the process isn’t as straightforward as one may hope – especially, since there’s a rather large chasm between the new and old Azure management portals as neither offer an entire end-to-end solution.  As with most things Microsoft nowadays, PowerShell seems to be the best solution for truly accomplishing your needs.

Below, you’ll find a resource for tackling all of the different components of moving a Hyper-V VM to Azure.  There are some ways to do parts of this through the various GUI’s, but, again, none of the GUI’s offer a complete solution.

Before You Begin

Before you begin, you will need to download/have access to a few PowerShell cmdlet libraries.

  • Azure (via Microsoft Web Platform Installer, when prompted click Run)
  • Hyper-V (some cmdlets are installed with Hyper-V host). You can also download a helper library from CodePlex.

Also, Windows Azure currently only supports Hyper-V migrations of:

  • Windows Server 2008 R2 SP1
  • Windows Server 2012 Datacenter
  • Windows Server 2012 R2 Datacenter

NOTE: I would recommend that you backup your hard drive images before proceeding.

Preparing the Hyper-V VM

Login to your virtual machine and open a command prompt as an Administrator.  At the command prompt type in:

c:\windows\system32\sysprep\sysprep

This will display a dialog like the following:

sysprep-azure

Make sure you have the following options set:

  • Enter System Out-of-Box Experience (OOBE)
  • Check Generalize
  • Shutdown Options should be “Shutdown”
Then click Ok.  This will start the process of prepping your machine for Azure import.  Once this process is complete, your machine will shutdown.
NOTE: Sysprep will remove all machine-specific information, including licensing and active directory domain settings.  Therefore, should there be any application on the machine requiring user-specific/administrator permissions, I advise that you add permissions to the application for the local administrators group as when you import the VM, Azure will create a new local administrator for you.  Once the machine has been imported, you can then add the machine back to the domain and/or add specific user permissions.
For more information on sysprep, check out TechNet.

Preparing the Hard Drive Images

Windows Azure requires the images to be in .vdk format rather than the .vhdk format.  If your hard drive images are already in .vhd format, you may skip this step.  Also, you can complete this step using the Hyper-V GUI should you choose by editing the attached disk and choosing convert.  However, PowerShell is a little more straightforward.

Open a PowerShell window and type the following:

Convert-VHD –Path c:\path_to_original\harddrive.vhdx –DestinationPath c:\path_to_destination\harddrive.vhd

You’ll need to do this for each hard drive attached to the Hyper-V virtual machine.  Also, Convert-VHD is smart enough to know the original and destination formats determined by the hard drive file extensions (e.g. vhd or vhdx).  For more information on Convert-VHD, you can take a look at TechNet.

Side Note: Reattaching Hyper-V Hard Drives

In case you would like to reattached a backed-up version of the hard drive (for instance, the one you created before this process), you may need to reassign permissions to the hard drive for the Hyper-V machine.

First, you’ll need to get the Hyper-V machine’s GUID.  There’s a few ways to do this, but PowerShell helper libraries, come in really handy:

Get-VM | ft ElementName,name

Or, if you prefer, you can also query the WMI repository.  With elevated credentials in PowerShell, execute:

Get-WmiObject -Namespace root\virtualization -class msvm_computersystem | select elementname, operationalstatus, processid, name| ft -auto

While the WMI repository comes in handy for determining a few other characteristics such as processid, both of the above commands will return the GUID of the Hyper-V machine.  Once you have the GUID of the Hyper-V machine, you’ll need, from a command prompt opened as an Administrator, to execute the following command (as seen on Microsoft Knowledgebase) on each of the hard drive images you’d like to attach to the Hyper-V virtual machine.

icacls <Path of .vhd or .vhdx file> /grant "NT VIRTUAL MACHINE\<Virtual Machine GUID from previous step>":(F)

This will grant the Hyper-V machine access to the hard drive images and you can add these images (or replace the currently attached images) in the Hyper-V management console.

Connect to Azure

Once you have the Azure PowerShell libraries installed, connecting to Azure is quite simple. Open Azure Powershell from the Start Menu and type:

$cred = Get-Credential
Add-AzureAccount -Credential $cred

The above commands will display a dialog box allowing you to enter your Azure credentials.  Then, your current PowerShell context will be connected to Azure using the supplied credentials (assuming that the credentials you entered were correct).

Set the CurrentStorageAccountName

Before you can upload an image, you’ll need to have a storage account.  This can very easily be done from either of the Azure portals, but to be true to form, if you’d like to create a storage account via PowerShell, execute the following command:

$storage = New-AzureStorageAcount -StorageAccountName "mystorageaccountname" -Location "East US"

Once, you have a storage account created (via previously created or just now), you’ll need to set your CurrentStorageAccountName for the PowerShell context in which you are working.  To check which storage account you are currently using, you can enter:

Get-AzureSubscription

currentStorageAccountName
You may notice that your CurrentStorageAccountName is blank.  If not, skip ahead.  If so, that’s okay.  Let’s set it now.  If you executed the above command for creating a new storage account, simply type in the following:

Set-AzureSubscription -SubscriptionName "Free Trial" ` -CurrentStorageAccountName $storage.Label -PassThru

NOTE: The name of your subscription may be different.  If so, type that in the place of “Free Trial” (e.g. mine in the example above is “DesertedRoad”).

If you don’t know storage account’s name, or, like me, have multiple storage accounts, then you can type the following to list your available storage accounts.

Get-AzureStorageAccount

This is filterable if you know who to query returned objects in PowerShell.  But, the command simply returns an 0-based index array of storage account objects.  So, counting from 0, choose which storage account you’d like to use and issue a slightly modified version of the command above and replace “0” with the index of the storage account you’d like to use:

Set-AzureSubscription -SubscriptionName "Free Trial" ` -CurrentStorageAccountName (Get-AzureStorageAccount)[0].Label -PassThru

Now, issuing Get-AzureSubscription will return your current PowerShell context with the CurrentStorageAccountName set.

Now that you have your storage account set, you’ll also need a storage container.  I typically call mine “vhds”, but you can call it whatever you’d like.  However, for the examples below, I will be using “vhds” as the path.

New-AzureStorageContainer "vhds" -Permission Off

The Off value for the -Permission parameter sets the blobs in the “vhds” container to private allowing only an administrator to  view them.

Uploading the Hard Drive Images

Now that you have .vhd formatted images of your hard drives, you are ready to upload them to Azure.  Depending on the size of the hard drive image, this could take anywhere from 30 minutes to 2 hours.  A very rough estimation is approximately, 1 minute per GB as the process is not simply uploading a file, but also creating MD5 hashes and verifying the success of the upload.

Before uploading a quick recap:

  1. Login to Azure via Powershell
  2. Create/Set the CurrentStorageAccountName for the current context
  3. Create a storage container

To upload your virtual machine hard drive images, execute the following for each hard drive image:

Add-AzureVhd -Destination "http://mystorageaccount.blob.core.windows.net/vhds/harddrive0.vhd" -LocalPath "c:\hyper-v\hard drives\harddrive0.vhd"

Obviously, use your blob storage account and the container you created above.

Converting Your Disk Images to Azure Disks and Azure Virtual Machine Images

There are two types of disks in Azure: 1) OS disks; and, 2) data disks.  The primary difference between the two is that the OS disk contains your operating system and you can only attach 1 OS disk to each VM while you can attach unlimited data disks to a single VM.

Let’s assume that my VM has two hard drives (C: – harddrive0.vhd; and, D: – harddrive1.vhd) and they both have been uploaded to my storage container.

First, let’s attach the OS disk.

Add-AzureDisk allows you to create the OS disk (see MSDN):

Add-AzureDisk -DiskName "Machine0-Disk0" -MediaLocation "http://mystorageaccount.blob.core.windows.net/vhds/harddrive0.vhd" -Label "OS" -OS "Windows"

By adding the -OS parameter, I’ve made this a bootable OS disk.

Second, to add a data disk, you can use Add-AzureDisk without the “OS” parameter, or use Add-AzureDataDisk.  Add-AzureDataDisk does give you a few more options, however, to create an OS bootable disk, you MUST use Add-AzureDisk.  (If you created a bootable disk, you’ll need to see Option 1 below and use the second command to import the disk into your VM.) Below is a basic example of each in order to create my “D” drive Azure disk image.

// OPTION 1:
// First, create an Azure data disk, then add the newly created data disk to an existing VM
Add-AzureDisk -DiskName "Machine-Disk1" -MediaLocation "http://mystorageaccount.blob.core.windows.net/vhds/harddrive1.vhd" -Label "Data"
 
Get-AzureVM "MyService" -Name "MyVM" | Add-AzureDataDisk -Import -DiskName "Machine-Disk1" -LUN 1 | Update-AzureVM
 
// OPTION 2:
// Add a data disk directly from a blob location and attach it to a VM
Get-AzureVM "MyService" -Name "MyVM" | Add-AzureDataDisk -ImportFrom -MediaLocation "https://mystorageaccount.blob.core.windows.net/vhds/harddrive1.vhd"   -DiskLabel "Data" -LUN 1 | Update-AzureVM

Now, sometimes, it’s convenient to create a VM image so that you don’t have to keep doing the above every time you’d like to create a virtual machine with the above disks.  To create a VM image in your library (again, this will allow you to create a new VM from a custom image rather than choosing one of Azure’s built-in images).

Again, you’ll need to start off with your bootable OS disk and you can then add data disks to your image later.  To create the VM image:

Add-AzureVMImage -ImageName imageName -MediaLocation http://mystorageaccount.blob.core.windows.net/vhds/harddrive0.vhd -Label imageLabel -OS "Windows"

Create a Reserved IP

This step is optional and should only be used if you’d like to attach a static reserved IP to your virtual machine.  At the writing of this post, Azure only allows a maximum of 5 reserved IPs per subscription.  If you need additional reserved IPs, then you’ll need to contact Azure Support.

To create a reserved IP labeled MyTestIP:

New-AzureReservedIP -ReservedIPName "MyTestIP" -Location "East US"

NOTE: Unfortunately, Azure doesn’t give us the ability to put a reserved IP into a specified resource group yet.  Hopefully, that will come as I like to keep things organized.  This will create the IP in your general availability resources of Azure (you’ll need to look in your new management portal for this: Browse -> Reserved IP addresses or Browse -> Resource Groups -> Default-Networking).

Choose Azure Virtual Machine Size

Azure offers a ton of options for virtual machine sizes.  However, be careful when choosing a machine size as some are being deprecated and will no longer function in the future.  Currently, it is best to choose an A-type or D-type machine.  To list all available virtual machine sizes:

Get-CloudVMRoleSizeProfile | ft Name, Description, CpuCount, MemoryInMB -AutoSize

Once you determine the size of the virtual machine you’ll want to deploy, remember the profile’s Name.

Create the Virtual Machine

Whoohoo! We’ve made it. Are you still with me?  If you look back, there’s really more explanation of the steps than there are steps to complete this.  So, this process maybe slightly time-consuming, but it’s not very difficult.

Now that all of the preliminaries are complete.  Let’s create the VM based on our image (“Machine0”) from above.

First, we’ll need to create an Azure machine configuration.

$vm1 = New-AzureVMConfig -Name "myVMName" -InstanceSize "instanceSizeName" -ImageName "imageName" | `
       Add-AzureProvisioningConfig -Windows  -AdminUsername "myAdmin" -Password "password"

Some things to notice about the above:

  1. myVMName – the name of your new virtual machine
  2. instanceSizeName – the name of the Azure virtual machine size (see previous section)
  3. imageName – the name of the image your created above with Add-AzureVMImage
  4. myAdmin – the new admin username for the machine (NOTE: “admin” and “administrator” are reserved accounts and cannot be used)
  5. password – the password for the new admin account

Now, create the Azure VM based on the machine configuration.

New-AzureVM -ServiceName "serviceName" -Location "East US" -VMs $vm1 -ReservedIPName "MyTestIP"

Notes about the command:

  1. serviceName – the name of the resource group
  2. East US – the location for the VM, but, of course, feel free to choose whatever location you desire
  3. $vm1 – the VM config we created in the previous step
  4. MyTestIP – the name of the reserved IP we created in the section above.

Like What You See?

Subscribe to receive new posts in your inbox.

Privacy Preference Center