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


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

  • Comments 9

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: http://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: http://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: http://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 NAME with 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 NAME with 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: http://sdrv.ms/1aSXfXD

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

  • Thanks Ido

    Great post, this is the only article I have found that actually worked for us.

    Successfully moved two VMs across subscriptions and locations.

    Thanks again

    Mark Harby

    Nottingham. UK

  • Look, Azure is pretty cool.  And Microsoft gets a lot of things right.  But this procedure is supposed to be simpler than just re-creating the vm?  Really?  What genius decided adding a 'publish' button to wrap this logic was a bad idea?  It's always been like this with Microsoft, they have technologies that get you 95% there, but the last 5% borders on incomprehensible, and when you finally get that last 5%, they deprecate the technology in favor of their new 'best' way of doing things.  Oye Veh.

  • I created a VM on my Free Trial subscription that has now expired. I'm trying to move it to my new Pay-As-You-Go subscription using your script. I note that the VM shows on the Azure Portal as being in state "Stopped (Deallocated)".

    When I run the command: $sourceVm | Export-AzureVM -Path $vmConfigurationPath

    I get this error:

    Export-AzureVM : Could not read properties for virtual machine: MyVM. It may still be provisioning.

    Is this error because the VM is deallocated? I really don't want to have to create my VM again from scratch.

  • Further to my last comment, it turned out that it was because the Azure Powershell console had the root of C:\ as its working directory. There was a permissions issue in writing the file to there. Changing the working directory solved the problem!

    The error message was misleading... :-(

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

    Gets following error

    WARNING: No deployment found in service: 'http://xyspqr.cloudapp.net '

    Powershell version

    Major  Minor  Build  Revision

    -----  -----  -----  --------

    0      8      3      -1

  • On step 7,

    is NEW SUBSCRIPTION NAME the original subscription or the new subscription where I am going to create the copy?

    Otherwise, how will this process find the subscription to create the new VM?

  • PS C:\> $sourceContext = New-AzureStorageContext -StorageAccountName $sourceStorageName -StorageAccountKey $sourceStorag

    eKey

    New-AzureStorageContext : Cannot validate argument on parameter 'StorageAccountKey'. The argument is null or empty.

    Supply an argument that is not null or empty and then try the command again.

    At line:1 char:100

    + ... rageAccountKey $sourceStorageKey

    +                    ~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidData: (:) [New-AzureStorageContext], ParameterBindingValidationException

       + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.WindowsAzure.Commands.Storage.Common.Cmdlet.N

      ewAzureStorageContext

  • I think this is it. Thanks for the walkthrough, so far I´m good. Just a weird detail during blob copy, counter reaches only 23% but checking the coptState it shows copied and total bytes the same, so no problem.

    However, Igot this error in step 14

    PS C:\exportAzureVm> Get-AzureSubscription -Current | Set-AzureSubscription -CurrentStorageAccountName $desStorageName

    Set-AzureSubscription : The input object cannot be bound because it did not contain the information required to bind

    all mandatory parameters:  Certificate

    At line:1 char:34

    + Get-AzureSubscription -Current | Set-AzureSubscription -CurrentStorageAccountNam ...

    +                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidArgument: (Microsoft.Windo...ureSubscription:PSObject) [Set-AzureSubscription], P

      arameterBindingException

       + FullyQualifiedErrorId : InputObjectMissingMandatory,Microsoft.WindowsAzure.Commands.Profile.SetAzureSubscription

      Command

    Any clue. I´m really n00b in Azure powershell and have no idea what ttit can be.

    I realized also that my new subscriptio, yet active, is not shown in my available bscription when creating a new VM on the management portal. But On Azure Power Shell, I see both subscription Ids. (free trial and thenew paay-as-you-go)

    Thanks

  • Many thanks for this guide!  Here's my notes after having just worked through it.

    blogs.msdn.com/.../move-an-azure-virtual-machine-between-subscriptions-or-data-centers-geos.aspx

Page 1 of 1 (9 items)
Leave a Comment
  • Please add 3 and 8 and type the answer here:
  • Post