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
Comments
Anonymous
May 29, 2014
The comment has been removedAnonymous
June 11, 2014
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.Anonymous
July 17, 2014
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.Anonymous
July 18, 2014
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... :-(Anonymous
July 23, 2014
$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 -1Anonymous
October 06, 2014
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?Anonymous
November 23, 2014
The comment has been removedAnonymous
December 17, 2014
The comment has been removedAnonymous
December 19, 2014
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.aspxAnonymous
January 07, 2015
You can use DC Migration Tool, an open source tool to copy azure resources from one subscription to other subscription across data centers as well. Azure Data Center Migration Tool Blog blog.persistentsys.com/.../persistent-systems-releases-azure-data-center-migration-solutionAnonymous
March 24, 2015
The comment has been removedAnonymous
June 25, 2015
The comment has been removedAnonymous
July 09, 2015
Very good guide. A little issue is that the script can't work with a source VM with VNet configuration. I revised and generate a script file. tombwu.wordpress.com/.../migrate-vms-between-azure-subscriptionsAnonymous
August 04, 2015
I'm getting the certificate error on step 14 as wellAnonymous
August 16, 2015
Don smith did provide a solution to fix step 14 error . See below command he suggested Set-AzureSubscription -SubscriptionName "DESTINATION_SUBSCRIPTION_NAME" -CurrentStorageAccountName $destStorageNameAnonymous
August 28, 2015
Great post! It worked fine for me. But I had this issue, I changed the VM size on the destination subscription and now I can not login in anymore on the machine (I am getting Access Denied by SSL). Do you know why?Anonymous
September 21, 2015
The comment has been removedAnonymous
November 18, 2015
Problem with #14 will be resolved if you follow these steps.
- Download publishsettings file (login, then save to "%temp%AllMySubscriptions.publishsettings"): > Get-AzurePublishSettingsFile
- Import all certs to your private cert store: > Import-AzurePublishSettingsFile "%temp%AllMySubscriptions.publishsettings"
- Load store (run certmgr.msc, look under Personal) and find your cert by subscription's name in Friendly Name column.
- Open cert and copy its Thumbprint into variable: > $thumb = "00 55 ff ...."
- Create Cert object: > $cert = Get-Item cert:\CurrentUserMy$thumb
- Now just add -Certificate $cert to the end of command, like this: > Get-AzureSubscription -Current | Set-AzureSubscription -CurrentStorageAccountName $destStorageName -Certificate $cert
Anonymous
November 28, 2015
Hi, I wish to move my VM from a existing subscription1-RegionA to a existing Subscription2-RegionB, but retaining its original name. This script seems to be unaware of what I need, and gives me the error "Could not read properties for virtual machine: (myMachineName). It may still be provisioning.", Is there a way to sort this?.. please help.Anonymous
January 06, 2016
Killer post. I used this as a foundation for creating a module specific to our organization with validations and checkpoints along the way. Also, the script can easily handle VMs with VNets, you just need to modify the exported XML file as appropriate for your situation. Many thanks!Anonymous
February 16, 2016
Thanks, this was useful. I wonder, now when the new Azure portal is running, are there any better ways to move a VM between subscriptions?Anonymous
February 17, 2016
Thanks. But how to do this with new Resource Manager VMs?Anonymous
March 14, 2016
The comment has been removedAnonymous
June 01, 2017
Link for "http://sdrv.ms/1aSXfXD" is not working now anymore. Can you post updated link please?