A very common request regarding Azure is to bring your own local VM to Azure for a direct cloud use. For this purpose, it is recommended to use appropriate tools, such as Azure Site Recovery. If you already have the VHD files, you can manually move them to a storage account and set up a new VM in Azure. However, there are usually 2 problems: the VHD file needs a fixed size and the VHD file must be stored in a PageBlob

Generate VM

In the following script, a new VM is created and an OS disk (line 15) and a data disk (line 16) from a storage account (line 13-14) are refered to generate a managed disk from these VHD files. The VHD files must have been previously uploaded to the storage account. The script can be downloaded at: https://github.com/tzuehlke/scripts/blob/master/PS_with_az/CreateVMwithOSDiskAndDataDiskFromExistingVHDs.ps1

# Connect-AzAccount
# Select-AzSubscription <SUBSCRIPTIONNAME>

$rgName = "vhd4vm2"
$location = "westeurope"
$nicname = "vm1-nic"
$subnet1Name = "vm1-subnet"
$vnetName = "vm1-vnet"
$vnetAddressPrefix = ""
$vnetSubnetAddressPrefix = ""
$vmName = "vm1"
$vmSize = "Standard_D2s_v3"
$vhdStorageAccount = "<STORAGEACCOUNTNAME>"
$rgVhdStorageAccount = "<RG-STORAGEACCOUNTNAME>"
$disk1src = "https://<STORAGEACCOUNTNAME>.blob.core.windows.net/<CONTAINER>/disk1-fixed.vhd"
$disk2src = "https://<STORAGEACCOUNTNAME>.blob.core.windows.net/<CONTAINER>/disk2-fixed.vhd"

$pip = New-AzPublicIpAddress -Name $nicname -ResourceGroupName $rgName -Location $location -AllocationMethod Dynamic
$subnetconfig = New-AzVirtualNetworkSubnetConfig -Name $subnet1Name -AddressPrefix $vnetSubnetAddressPrefix
$vnet = New-AzVirtualNetwork -Name $vnetName -ResourceGroupName $rgName -Location $location -AddressPrefix $vnetAddressPrefix -Subnet $subnetconfig
$nic = New-AzNetworkInterface -Name $nicname -ResourceGroupName $rgName -Location $location -SubnetId $vnet.Subnets[0].Id -PublicIpAddressId $pip.Id
$vm = New-AzVMConfig -VMName $vmName -VMSize $vmSize
$vm = Add-AzVMNetworkInterface -VM $vm -Id $nic.Id
$discStorageAcc = Get-AzStorageAccount -ResourceGroupName $rgVhdStorageAccount -Name $vhdStorageAccount

$diskConfig1 = New-AzDiskConfig -AccountType 'Premium_LRS' -Location $location -CreateOption Import -StorageAccountId ($discStorageAcc.Id) -SourceUri $disk1src
$disk1 = New-AzDisk -Disk $diskConfig1 -ResourceGroupName $rgName -DiskName "disk1"
$vm = Set-AzVMOSDisk -VM $vm -ManagedDiskId $disk1.Id -CreateOption Attach -Linux
$vm = Set-AzVMOSDisk -VM $vm -ManagedDiskId $disk1.Id -CreateOption Attach -Linux

$diskConfig2 = New-AzDiskConfig -AccountType 'Premium_LRS' -Location $location -CreateOption Import -StorageAccountId ($discStorageAcc.Id) -SourceUri $disk2src
$disk2 = New-AzDisk -Disk $diskConfig2 -ResourceGroupName $rgName -DiskName "disk2"
$vm = Add-AzVMDataDisk -VM $vm -ManagedDiskId $disk2.Id -CreateOption Attach -Lun 0

New-AzVM -ResourceGroupName $rgName -Location $location -VM $vm -Verbose

If the script succeeded, a new VM and 2 managed Disks are created:

The two problems mentioned above occur in lines 26+27 and 31+32 respectively. In the following the corresponding solutions are given.

Problem 1: “Only blobs formatted as VHDs can be imported.”

The VHD needs to be in the VHD Format (not VHDX) and needs a fixed size. You can can convert your VHD to the correct format und fixed size with Hyper-V:

Afterwards, you can upload the VHD file to an Azure Storage Account.

But the best approach is the alternative upload directly with PowerShell! This has the advantage that the VHD file is converted directly into the Disk Type “Fixed size” and the upload is done directly into a PageBlob, which also bypasses the second problem

Add-AzVhd -Destination "https://<STORAGEACCOUNTNAME>.blob.core.windows.net/<CONTAINER>/disk1.vhd" -LocalFilePath "C:\VHDs\vm1-disk1.vhd"

Problem 2: “The source blob https://<STORAGEACCOUNTNAME> .blob.core.windows.net/ <CONTAINER>/disk1.vhd is not a page blob.”

The uploaded VHD files must be stored in a storage account of type “PageBlob”. If this is not the case, you will see the above error message. It does not matter whether the storage account was created with child “Generation 1” or “Generation 2”. The blob type “PageBlob” does not have to be specified explicitly for Add-AzVhd (see problem 1). However, if AzCopy is used, this parameter should not be forgotten. The calls will look like the following.

For AzCopy v8 with Access Key of Storage Account:

AzCopy /Source:C:\VHDs\vm1-disk1.vhd /Dest:https://<STORAGEACCOUNTNAME>.blob.core.windows.net/<CONTAINER>/disk1.vhd /DestKey:onn...fA== /BlobType:page

For AzCopy v10 wit Shared Access Signature:

AzCopy copy "c:\VHDs\vm1-disk1.vhd" "https://<STORAGEACCOUNTNAME>.blob.core.windows.net/<CONTAINE>?sv=2018-03-28&s...ivWdA%3D" --blob-type=PageBlob

This post is related to: