Azure: Moving VHD to Azure with PowerShell and create VM image



Original Article:


This is a guide for moving a VHD to Azure using PowerShell with Azure Resource Manager 5.1 module. Then in the Azure portal, we will create an image from the VHD, this will help us to be able to provision VMs from that image.


This guide is provided as an alternative solution for provisioning a KEMP LoadMaster virtual machine on an Azure subscription with the Enterprise Agreement in case there are limitations for the current region to provision the KEMP LoadMaster directly from the Azure marketplace.

Note: You can move an exported VHD from a local virtualization tool or from another cloud.

The use of managed disks for the new virtual machine simplifies the administration of the virtual machine and provides better availability when the virtual machine is placed in an availability set.


Move VHD to Azure using PowerShell and create an image object from the OS Disk


Steps to move VHD to Azure using PowerShell

1. We will open Powershell on our local computer and access our Azure subscription through the command:

"AzureRM.Profile \ Login-AzureRmAccount"


2. To simplify the process we will use two variables: The name of the Azure subscription and the ID of the Azure subscription by executing the following commands (replace the name and ID of your subscription):

$Subscription = "Your Subscription Name"
$SubscriptionId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

3. We will select the current subscription as Default, for that we execute the following command:

"Get-AzureRmSubscription -SubscriptionId" 4d278909-xxxx-xxxx-xxxx-xxxxxxxx "-TenantId" 683bfafa-xxxx-xxxx-a787-xxxxxxxxx "| Set-AzureRmContext"

4. Proceed to create a Storage Account / Storage Account through the following command:

$storageAccount = New-AzureRmStorageAccount -ResourceGroupName "DRendon" `
-Name "kempvdhtestingdr" `
-SkuName Standard_LRS `
-Location "eastus"
$ctx = $ storageAccount.Context

5. Now we are going to generate the container for our blob storage:

$containerName = "kempvdhcontainerdr"
new-azurestoragecontainer -Name $ containerName -Context $ctx -Permission blob

6. Store in a variable the location of the VHD:

$localPath = 'C:\Users\daver\Downloads\VLM-trial\LoadMaster-VLM-Microsoft-HyperV\vlm2.vhd'

7. Assign a variable to name the VHD:

$vhdName = 'kempVhdtest.vhd'

8. Proceed to move the VHD to the blob storage:

$urlOfUploadedImageVhd = ('' + '/' + $vhdName)
Add-AzureRmVhd -ResourceGroupName "DRendon" -Destination $ urlOfUploadedImageVhd `
-LocalFilePath $localPath -OverWrite

"Add-AzureRmVhd cmdlet uploads on-premises virtual hard disks, in .vhd file format, to a blob storage account as fixed virtual hard disks"

There are other optional parameters that we can make use of: 

  • AsJob: Run cmdlet in the background and return a Job to track progress.
  • BaseImageUriToPatch: Specifies the URI to a base image blob in Azure Blob Storage
  • DefaultProfile: The credentials, account, tenant, and subscription used for communication with Azure.
  • NumberOfUploaderThreads: Specifies the number of uploader threads to be used when uploading the .vhd file.
  • OverWrite: Indicates that this cmdlet overwrites an existing blob in the specified destination URI, if one exists.

The VHD migration process will start right away and depending on the size it could take a couple of minutes:


Once the upload has been successful you should see something like this:


Now there are 2 options to create the image: from PowerShell or from the Azure Portal. 

Creating the image of the Virtual Machine

Option 1. Creating the image of the Virtual Machine using PowerShell

1. Let's create an image object and then store it in the variable $ imageConfig

$location = 'EastUS'
$imageConfig = New-AzureRmImageConfig -Location $ location


2. Then we are going to set the properties of the operating system disk in the object of the image using the Set-AzureRmImageOsDisk command:

$imageConfig = Set-AzureRmImageOsDisk -Image $imageConfig -OsType Windows -OsState Generalized `
-BlobUri $urlOfUploadedImageVhd

3. Now proceed to generate the image: 

$image = New-AzureRmImage -ImageName $imageName -ResourceGroupName $resourceGroupName -Image $imageConfig

Option 2. Creating the image of the Virtual Machine in Azure

 Once the migration process is completed, go to the storage account to verify that the VHD is correctly listed:


2. Now go create a new resource,  search for "image" then click on the image as shown below:


3. Provide the parameters required to provision the image from the VHD as follows:


You will see a notification if the image was created correctly:


Now we can create the virtual machines from this image when we need it:



Script Download

Here is the complete script on VHD migration with Azure Powershell 5.x, you can also download it from here:

$subscriptionName = "Your Subscription Name"
$subscriptionId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$tenantId ="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$resourceGroupName = 'DRendon'
$location = 'EastUS'
$vhdName = 'kemp360central-v1.25.vhd'
$imageName = 'kempcentral25'
$containerName = "kempcentraldrtestcontainer"
$storageAccountName = "kempcentraldrtest"
Select-AzureRmSubscription -SubscriptionId $subscriptionId
#Select your default subscription on ARM
Get-AzureRmSubscription -SubscriptionId $subscriptionId -TenantId $tenantId | Set-AzureRmContext
#create new storage account
$storageAccount = New-AzureRmStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName -SkuName Standard_LRS -Location $location
$ctx = $storageAccount.Context
#create container
new-azurestoragecontainer -Name $containerName -Context $ctx -Permission blob
#set the local path from the vhd
$localPath = 'C:\Users\daver\Downloads\kemp360central\kemp360central-v1.25.vhd'
# set the url of the image and move the vhd, also use the -overwrite option since process might fail sporadically
# -overwrite solves the error Add-AzureRmVhd : The pipeline was not run because a pipeline is already running.
# Pipelines cannot be run concurrently
$urlOfUploadedImageVhd = ('https://' + $storageAccountName + '' + $containerName  + '/' + $vhdName)
Add-AzureRmVhd -ResourceGroupName $resourceGroupName -Destination $urlOfUploadedImageVhd `
-LocalFilePath $localPath -OverWrite
# Create a managed image from the uploaded VHD
$imageConfig = New-AzureRmImageConfig -Location $location
#set the managed disk from the image
$imageConfig = Set-AzureRmImageOsDisk -Image $imageConfig -OsType Windows -OsState Generalized `
    -BlobUri $urlOfUploadedImageVhd
$image = New-AzureRmImage -ImageName $imageName -ResourceGroupName $resourceGroupName -Image $imageConfig


Discover questions

  1. Was the VHD uploaded to Azure Cloud or the OS on the VHD is from Azure Marketplace?
  2. What Operation System/version is installed on the VHD?
  3.  Did you install Azure Guest Agent on the OS? 
  4. Was the VHD OS generalized before shutdown? 

You should be able to identify what Linux OS type/version is running on your PC, install Guest Agent, generalize the OS before importing it to Azure Cloud.

Steps you may apply in order to fix the issue

  1. Identify OS type/version, and install on it Guest Agent. Please review an article: Linux distributions endorsed on Azure
  2. Generalize the OS: How to create an image of a virtual machine or VHD
  3. Review the Azure documentation in order to prepare the OS. Following article contains steps how to prepare the OS and install required applications:       Understanding and using the Azure Linux Agent
  4.  Fix the size of the vhd, that is, the VHD size integer, i.e. 200GB, it cannot be 200.5GB or 200.05GB. 
  5.  Ensure that blob is formatted as VHD

See Also

Other Languages:

 Spanish (es-MX):

0 Replies