
I’ll show how to upload a .vhd to Azure as a blob and how to create a Virtual Machine from it using the Powershell AzureRM Cmdlets.
Recently I needed to move a couple of Azure virtual machines from one Azure subscription to another, and to my surprise I found that neither uploading a virtual harddisk nor creating a new virtual machine from it can be accomplished through the ‘new’ Azure portal. Knowing that Powershell support for Azure is excellent, I took a stab at doing it this way, and it isn’t too hard – once you know which of the 990(!) Cmdlets you need…
Note that Azure has 2 sets of Cmdlets: The Azure module, which corresponds to the old manage.windowsazure.com
portal, and the Azure Resource Manager modules that correspond to portal.azure.com
(i.e. the current way of doing things) – I’m using the latter, in combination with Powershell 5.0.
By the way, note that the .vhd I’m using was already created in Azure, so I don’t know what issues you’ll run into if you try to upload .vhd’s that were created outside of Azure.
Setting up the Azure cmdlets
Powershell 5.0 ships with the PowerShellGet module, which makes installing the AzureRM module as easy as:
Install-Module AzureRM
Because the AzureRM module is basically the root of a whole tree of dependencies, installing it can take a couple of minutes.
Once installed, use the following to log into Azure in an interactive fashion:
Login-AzureRmAccount
The AzureRM cmdlets only operate on the resources in the subscription that is currently marked as active. In case you have access to more than one subscription, make sure you select the correct one, for example by subscription name (see also here)
Get-AzureRmSubscription -SubscriptionName "Visual Studio Enterprise" | Select-AzureRmSubscription
Uploading a .vhd as a blob
Next, determine the resource group and storage account url where the .vhd should be uploaded to. I find it easiest to just use the Azure portal to find an existing VM’s .vhd and base the url on that:
To perform the upload of the .vhd, use Add-AzureRmVhd
. Prior to uploading, it determines where the zero-padded blocks are in the file and only transfers the parts with actual data, most likely saving you some bandwith:
$resourceGroupName = "seventh-resource" $sourceVhd = "E:\Azure backups\AcmeSrv-AcmeSrv-2014-11-23.vhd" $destinationVhd = "https://seventh.blob.core.windows.net/vhds/AcmeSrv-AcmeSrv-2014-11-23.vhd" Add-AzureRmVhd -LocalFilePath $sourceVHD -Destination $destinationVHD ` -ResourceGroupName $resourceGroupName -NumberOfUploaderThreads 5
When the uploading has completed, you should see the .vhd as part of the blobs in the Blob service.
Creating a virtual machine based on a .vhd
Defining a virtual machine consists of piecing together a couple of resources. I’ll be referring to an existing Virtual network, which can be created through the Azure portal if you don’t have one already. (N.B. To list the available networks, run Get-AzureRmVirtualNetwork
without parameters):
$virtualNetworkName = "SeventhNetwork" $locationName = "westeurope" $virtualNetwork = Get-AzureRmVirtualNetwork -ResourceGroupName $resourceGroupName ` -Name $virtualNetworkName
We first need to define a network interface with a public IP address through which our new VM can be accessed:
$publicIp = New-AzureRmPublicIpAddress -Name "AcmeSrv" -ResourceGroupName $ResourceGroupName ` -Location $locationName -AllocationMethod Dynamic $networkInterface = New-AzureRmNetworkInterface -ResourceGroupName $resourceGroupName ` -Name "AcmeSrv123" -Location $locationName -SubnetId $virtualNetwork.Subnets[0].Id -PublicIpAddressId $publicIp.Id
Now we can start preparing the VM configuration. To list the available VMSizes for your location, use:
Get-AzureRmVMSize $locationName | Out-GridView
I’ll attach the .vhd we already uploaded, tell Azure it should be treated as a Windows machine, and attach the network interface we just created:
$vmConfig = New-AzureRmVMConfig -VMName "AcmeSrv" -VMSize "Standard_DS1" $vmConfig = Set-AzureRmVMOSDisk -VM $vmConfig -Name "AcmeSrv" -VhdUri $destinationVhd ` -CreateOption Attach -Windows $vmConfig = Add-AzureRmVMNetworkInterface -VM $vmConfig -Id $networkInterface.Id
Now we have all the parts ready to create the actual virtual machine. This call may take a while, but you should be able to follow progress through the Azure portal’s virtual machine overview.
$vm = New-AzureRmVM -VM $vmConfig -Location $locationName -ResourceGroupName $resourceGroupName
And that’s it; your new virtual machine is good to go 🙂
21 comments
Thank you for your awesome post! It solves my problem!
Junle Li
Man,
I was struggling to move resources from Free Trial (It was a production environment & about to expire) to Azure in Open subscription. Even the MS support was awful. Then I have decided to create the environment from scratch. I have created all the resources but VMs with Live Data 😐
Luckily I have found this.
You have saved my life. Thanks a million.
Harsha Perera
Thanks a lot guys, glad to be of help 🙂
Léon Bouquiet
Thanks a lot, worked perfectly 😉
Andi
Worked a treat 🙂
Existing VM on wrong VNet, deleted VM, grabed URL of VHD and used instead of uploading. Followed your script steps = success.
Steve
AWESOME POST!
I’m new to Azure and I’ve been working with MS Support and the litany of confusing information that I’ve receive left me really frustrated. Your post is clear and concise.
Thank you, thank you!
Darrell
I still don’t understand why this can’t be done via GUI.
claudio
So many blog posts readen and this one saved my life.
Thank you for you post. No nonsense. Just works.
thibault
[…] big thank you to Leon which this article allowed me to move forward on this point: https://blogs.infosupport.com/creating-a-vm-in-azure-based-on-an-uploaded-vhd/ . […]
Creating a VM to Azure in PowerShell from a VHD existing [Exclusive Guide] | Tech Revwz
this is exactly what i needed, did a migration from asm to arm, trying to move subscriptions to CSP, issues with switching key vaults cross subscription.
glad i found this, worked perfect.
thank you
johnson
Thank you so much. Life saver. MS support were awful in dealing with our migration from one subscription to the other. You scripts were easy to use and understandable
Blue
Is there a way to add the VMs to an availability set during creation?
It was possible to add Vms into an availability set in Classic mode, however it doesn’t seem possible in Resource Manager!
Blue
I haven’t tried it myself, but it looks like Set-AzureAvailabilitySet is what you’re looking for.
Léon Bouquiet
Very easy to follow steps! Thanks!
Lee
Been struggling for ages to be able to do this with little to no MS official support, combined with the Azure Storage Explorer this guide got the ball rolling with cloning Azure VMs for testing purposes. Thanks!
Doc
Excellent and straight forward solution. We’re migrating VMs from our outdated servers on perm to Azure. I looked everywhere and this was by far the best tutorial. I should mention though that AzurePS kept asking for a logon to the subscription despite I already did several times. it turned out that the installation of AzurePS should be from the msi installer https://github.com/Azure/azure-powershell/releases/download/v4.4.0-September2017/azure-powershell.4.4.0.msi
Good luck.
M. Taha
Mohammad Taha
good blog!!
I want to do the same thing in Azure Stack what all changes do I need to make?
Basharat Tamboli
Hi Leon,
Is this applicable for Linux VM?
Can I replace the script to Linux Redhat?
-CreateOption Attach -Windows
Thanks
JM
Leon this is a great guide, could you please advise what needs to be adjusted to fire up a Linux VM in the same manner?
Darren Smith
Amazing! Fixed an issue that I had been worried about digging into as it looked like a daunting task; your guide saved the day.
Mike Morris
If your Network and VM is in different RG, Then you can use following Powershell. (Linux VM)
Connect-AzureRmAccount
Select-AzureRmSubscription -Subscription “”
$RGvirtualNetworkName = “”
$VMresourceGroupName = “”
$virtualNetworkName = “”
$VMName = “”
$PIPName = “”
$NICName = “”
$VmSize = “”
$destinationVhd = “”
$locationName = “westeurope”
$virtualNetwork = Get-AzureRmVirtualNetwork -ResourceGroupName $RGvirtualNetworkName -Name $virtualNetworkName
$publicIp = New-AzureRmPublicIpAddress -Name $PIPName -ResourceGroupName $VMResourceGroupName -Location $locationName -AllocationMethod Dynamic
$networkInterface = New-AzureRmNetworkInterface -ResourceGroupName $VMResourceGroupName -Name $NICName -Location $locationName -SubnetId $virtualNetwork.Subnets[1].Id -PublicIpAddressId $publicIp.Id
$vmConfig = New-AzureRmVMConfig -VMName $VMName -VMSize $VMSize
$vmConfig = Set-AzureRmVMOSDisk -VM $vmConfig -Name $VMName -VhdUri $destinationVhd -CreateOption Attach -Linux
$vmConfig = Add-AzureRmVMNetworkInterface -VM $vmConfig -Id $networkInterface.Id
$vm = New-AzureRmVM -VM $vmConfig -Location $locationName -ResourceGroupName $VMresourceGroupName
Ghanshyam Dusane