From the MVPs: Copying a virtual machine from one Windows Azure subscription to another with PowerShell

This is the 36th in our series of guest posts by Microsoft Most Valued Professionals (MVPs). You can click the “MVPs” tag in the right column of our blog to see all the articles.

Since the early 1990s, Microsoft has recognized technology champions around the world with the MVP Award . MVPs freely share their knowledge, real-world experience, and impartial and objective feedback to help people enhance the way they use technology. Of the millions of individuals who participate in technology communities, around 4,000 are recognized as Microsoft MVPs. You can read more original MVP-authored content on the Microsoft MVP Award Program Blog .

This post is by ASP.Net MVP Ido Flatow Thanks, Ido!

And don’t forget: a week of free online Windows Azure training is in progress. Information and registration links are here. It’s not too late to attend the Wednesday, Thursday, and/or Friday sessions.

Why?

During the early stages of development and proof-of-concept (POC) steps, it is common to find developers and IT Pros using their own Windows Azure trial subscription to “experiment” with Windows Azure Virtual Machines (VMs). These experiments include creating a VM, installing your environment on it, and testing it out to conclude how easy it will be to start off a new project or migrate your existing application to Windows Azure.

Note: You can use the Windows Azure Cmdlets to automate your VM creation. Check here to see how: https://msdn.microsoft.com/en-us/library/windowsazure/jj835085.aspx.

After the initial POC succeeds (assuming it succeeded, there’s no reason to think otherwiseJ), your company creates a Windows Azure subscription, and you are asked to create your VM in the new subscription, so the bill can be charged to the company.

Now comes the part of migrating your VM to the new subscription. You actually don’t need to re-create the VM in the new subscription. You can simply move your current VM to the new subscription by using PowerShell and the Windows Azure PowerShell Cmdlets.

What?

So what do you need before starting this task?

1. Access to the existing and new Windows Azure subscriptions, either as admin or co-admin.

2. The Windows Azure PowerShell module, which you can install from here: https://go.microsoft.com/fwlink/p/?linkid=320376&clcid=0x409

How?

Connect your PowerShell environment to your Windows Azure subscription

You cannot use the Windows Azure Cmdlets until you configure PowerShell to use your subscription. To do so, follow the steps detailed in this article: https://www.windowsazure.com/en-us/documentation/articles/install-configure-powershell/#Connect

Now we can open a Windows PowerShell window and start typing.

Get the required information from the source VM

To copy the VM from the original subscription, we need to know where the VHDs (virtual hard disks) of the VM are stored. If the VM has both an OS disk and data disks, we will need to get the location of each of the disks.

1. Type the following command to use your original subscription, where you have the existing VM. Replace EXISTING SUBSCRIPTION NAMEwith the name of your original subscription.

Select-AzureSubscription -SubscriptionName "EXISTING SUBSCRIPTION NAME"

2. Type the following command to get the list of VMs and verify you see your VM in the list.

Get-AzureVM

3. Locate your VM in the shown table, and type the following commands to store the VM name and the Cloud Service where the VM is hosted. Replace YOUR_VM_NAME with the value from the Name column and CLOUD_SERVICE_NAME with the value from the ServiceName column. Finally, replace NEW_CLOUD_SERVICE_NAME with the unique name you want to give to the new VM in the target subscription.

$vmName = "YOUR_VM_NAME"

$serviceName = "CLOUD_SERVICE_NAME"

$destServiceName = "NEW_CLOUD_SERVICE_NAME"

$workingDir = (Get-Location).Path

4. Type the following commands to get the information of your existing VM. The commands will retrieve information about the VM disks and export the VM configuration to an XML file.

$sourceVm = Get-AzureVM –ServiceName $serviceName –Name $vmName

$vmConfigurationPath = $workingDir + "\exportedVM.xml"

$sourceVm | Export-AzureVM -Path $vmConfigurationPath

$sourceOSDisk = $sourceVm.VM.OSVirtualHardDisk

$sourceDataDisks = $sourceVm.VM. DataVirtualHardDisks

5. Type the following commands to get the Windows Azure storage account name containing the original VM VHDs, and its access key.

$sourceStorageName = $sourceOSDisk.MediaLink.Host -split "\." | select -First 1

$sourceStorageAccount = Get-AzureStorageAccount –StorageAccountName $sourceStorageName

$sourceStorageKey = (Get-AzureStorageKey -StorageAccountName $sourceStorageName).Primary

6. If you have not done so yet, turn off the original VM. You can either do it manually through the Windows Azure Management Portal, or by typing the following command:

Stop-AzureVM –ServiceName $serviceName –Name $vmName -Force

Now we have all the information we need from the original VM, it’s time to move to the new subscription and create the VM there. Keep the PowerShell window opened and continue to the next part.

Verify you have everything you need to create the VM in the new subscription

7. First, we need to switch the current subscription to the new subscription. Type the following command to do so, and replace NEW SUBSCRIPTION NAMEwith the name of your original subscription.

Select-AzureSubscription -SubscriptionName "NEW SUBSCRIPTION NAME"

8. Next, we need to set where we want to create the new VM – which region – by type the next command.

$location = $sourceStorageAccount.Location

Note: We’re assuming the new VM is going to be created in the same region as the original VM. If you are planning on creating the new VM in a different region, or in an affinity group, set the $location variable accordingly.

9. Type the following commands to verify you have a storage account in the selected region. If the storage account is not found, one will be created, which may take a couple of minutes. Make sure you replace NEW_STORAGE_NAME with a unique name for the new storage.

$destStorageAccount = Get-AzureStorageAccount | ? {$_.Location -eq $location} | select -first 1

if ($destStorageAccount -eq $null)

{

    $destStorageName = "NEW_STORAGE_NAME"

    New-AzureStorageAccount -StorageAccountName $destStorageName -Location $location

    $destStorageAccount = Get-AzureStorageAccount -StorageAccountName $destStorageName

}

$destStorageName = $destStorageAccount.StorageAccountName

$destStorageKey = (Get-AzureStorageKey -StorageAccountName $destStorageName).Primary

Note: Make sure the name is you use for the new storage account unique and only contains lowercase letters, otherwise the storage account creation may fail.

10. Type the following commands to create the required storage context variables.

$sourceContext = New-AzureStorageContext –StorageAccountName $sourceStorageName `

        -StorageAccountKey $sourceStorageKey

$destContext = New-AzureStorageContext –StorageAccountName $destStorageName `

        -StorageAccountKey $destStorageKey

11. Type the following commands to verify the target storage account has a container for the VHDs.

if ((Get-AzureStorageContainer -Context $destContext -Name vhds -ErrorAction SilentlyContinue) -eq $null)

{

    New-AzureStorageContainer -Context $destContext -Name vhds

}

Copy the VHDs from the source storage to the destination storage

Now that we have the information on both the source and destination storage accounts, it’s time to copy file blobs.

12. Type the following commands to copy the blobs from the original storage account to the destination.

$allDisks = @($sourceOSDisk) + $sourceDataDisks

$destDataDisks = @()

foreach($disk in $allDisks)

{

    $blobName = $disk.MediaLink.Segments[2]

    $targetBlob = Start-CopyAzureStorageBlob -SrcContainer vhds -SrcBlob $blobName `

            -DestContainer vhds -DestBlob $blobName `

            -Context $sourceContext -DestContext $destContext -Force

    Write-Host "Copying blob $blobName"

    $copyState = $targetBlob | Get-AzureStorageBlobCopyState

    while ($copyState.Status -ne "Success")

    {

        $percent = ($copyState.BytesCopied / $copyState.TotalBytes) * 100

        Write-Host "Completed $('{0:N2}' -f $percent)%"

        sleep -Seconds 5

        $copyState = $targetBlob | Get-AzureStorageBlobCopyState

    }

    If ($disk -eq $sourceOSDisk)

    {

        $destOSDisk = $targetBlob

    }

    Else

    {

        $destDataDisks += $targetBlob

    }

}

Note: If you changed the $location variable to point to a different data center, the copy process can take several minutes or even hours. Copying blobs within the same data center should several seconds to minutes.

13. Type the following commands to register the new disks as data/OS disks.

Add-AzureDisk -OS $sourceOSDisk.OS -DiskName $sourceOSDisk.DiskName -MediaLocation $destOSDisk.ICloudBlob.Uri

foreach($currenDataDisk in $destDataDisks)

{

    $diskName = ($sourceDataDisks | ? {$_.MediaLink.Segments[2] -eq $currenDataDisk.Name}).DiskName

    Add-AzureDisk -DiskName $diskName -MediaLocation $currenDataDisk.ICloudBlob.Uri

}

Create the new VM in the new subscription

We now have the VM disks in our new subscription, in the destination storage account. Now all we need to do is create the new VM with the existing disks.

14. Type the following commands to create a new VM configuration, based on the original VM configuration.

Get-AzureSubscription -Current | Set-AzureSubscription -CurrentStorageAccountName $destStorageName

$vmConfig = Import-AzureVM -Path $vmConfigurationPath

Note: The above command will copy all the settings of your original VM. If that VM was deployed to a virtual network, make sure your new subscription has a virtual network with the same subnet name. You will also need to add the –VNetName parameter to the next command, and set it to the name of the new virtual network.

15. Lastly, type the following commands to create the new VM according to the configuration.

New-AzureVM -ServiceName $destServiceName -Location $location -VMs $vmConfig -WaitForBoot

Note: if you want to create the new VM in an affinity group, and you have already set the $location variable to the group’s name, change the –Location parameter to –AffinityGroup.

And that is it. Since the new VM is based on the same disk as the original VM, we do not need to add any information regarding the admin username and password – you can connect to the new VM with your original username and password.

If you want to download an RDP file for the new VM, just type the following command:

Get-AzureRemoteDesktopFile -ServiceName $destServiceName -Name $vmConfig.RoleName -LocalPath ($workingDir+"\newVM.rdp")

You can find the complete script file here: https://sdrv.ms/1aSXfXD

For more Windows Azure automation tips, check my blog at https://blogs.microsoft.co.il/blogs/idof