Recently, I had to migrate a VM from vCenter to Azure. Unlike the migration from vCenter to AWS, this migration is a little bit trickier and involves many steps. There is probably a better way with Azure Site Recovery, but you have to pay for that. See my other post for that.
We’ll use PowerShell for the migration. There are several prerequisites for the migration and they are listed in the comments section.
I have a resource group called rgTest, a virtual network called vnetTest and a subnet that spans all of the virtual network address space called subTest.
In addition, I’ve created a storage account called sablogtest and a container called conttest.
On my local machine, I have the VMDK files downloaded from vCenter in a folder called VM and I have another folder called VHD where the converted files will be stored. For the script, use the names of the VMDK files that are 1K in size.
The script works if the OS has one or two drives. If you have more drives, you’ll have to modify the script. It should be very straightforward.
I’ve noticed that if I run the script in PowerShell cmd, then the script waits sometimes for the user to hit any key. In Windows ISE PowerShell, I didn’t have these problems. Make sure you are local admin on the machine. It’s needed because of the VMDK to VHD conversion.
The VM will power up automatically. When you RDP to it, the data drive won’t be accessible. Make sure you run diskmgmt.msc and then online the drive.
################################################################################ ## Kliment Andreev 2017 - (c) BSD License ################################################################################ ## 1. Converts VMDK file(s) to VHD ## 2. Uploads the VHD(s) to an existing container ## 3. Creates a bootable disk and/or data disk ## 4. Creates a VM with a public and private IP ################################################################################ # Pre-req: # Microsoft CLI 2.0 for Azure # Microsoft Virtual Machine Converter 3.0 # VMDK file(s) and enough storage to convert it/them to VHD # Existing subscription in azure # Known location of your subscription e.g East US # Existing resource group # Existing storage account and a container inside # Existing vNet where server will reside # Existing subnet where the server will reside # Existing security group that allows RDP # Make sure the OS is set for DHCP # Make sure the OS uses E1000, don't use vmxnet NIC # Make sure you know your local admin password # Make sure you have RDP enabled ################################################################################ # If for some reason the script fails # and you are starting from scratch, # make sure you delete the VHD files. # Otherwise the converter will create a new # VHD file with (1) as suffix and the VM # won't convert/upload properly ################################################################################ # Define your resources/variables here # None of these can be empty except: # $myDataVMDKFile and $myDataVHDFile ################################################################################ # Define where is your OS VMWare VMDK file, e.g. c:\users\name\desktop\server.vmdk $myOSVMDKFile = "C:\Users\klimenta\Desktop\VM\testvm.vmdk" # Define where is your DATA VMWare VMDK file, e.g. c:\users\name\desktop\server.vmdk # If you have only C: drive, the string below can be empty e.g. "" $myDataVMDKFile = "C:\users\klimenta\desktop\VM\testvm_1.vmdk" #$myDataVMDKFile = "" # Where do you want your OS VHD file created locally, e.g. c:\users\name\desktop\server.vhd $myOSVHDFile = "C:\Users\klimenta\Desktop\VHD\testvm.vhd" # Where do you want your Data VHD file created locally, e.g. c:\users\name\desktop\server.vhd $myDataVHDFile = "C:\Users\klimenta\Desktop\VHD\testvm_1.vhd" #$myDataVHDFile="" # Virtual Machine Converter PS module $ConverterFile = "C:\Program Files\Microsoft Virtual Machine Converter\MvmcCmdlet.psd1" # Existing subscription name $mySubscription = "Pay-As-You-Go" # Existing location $myLocation = "East US" # Existing resource group $myResourceGroup = "rgTest" # Name of the existing storage account (it shoould be unique) $myStorageAcct = "sablogtest" # Name of the existing container $myContainer ="conttest" # Name of the existing VNET $myVNET = "vnettest" # Name of the existing subnet $mySubnet = "subtest" # Name of the existing security group $mySecurityGroup = "sgAllowRDP" # VM size $myVMSize = "Standard_A2" ################################################################################ # If Standard_A2 # doesn't suit your needs # check other sizes in a region with: # Get-AzureRmVMSize -Location "East US" | Sort-Object Name ################################################################################ # Start the counter ################################################################################ $StartDate = (Get-Date) # Check if any of the variables are blank if (!(($myOSVMDKFile) -and ($myOSVHDFile) -and ($ConverterFile) -and ($mySubscription) -and ` ($myLocation) -and ($myResourceGroup) -and ($myStorageAcct) -and ($myContainer) -and ($myVNET) -and ` ($mySubnet) -and ($mySecurityGroup) -and ($myVMSize))) { Write-Host "Some of the required variables are empty. Exiting..." -ForegroundCOlor Red Exit } # Check if the OS VMDK exists if (-not(Test-Path -Path $myOSVMDKFile)) { Write-Host "Error: VMDK file does not exist. Exiting..." -ForegroundColor Red Exit } # Check if the Data VMDK exists but only if defined if ($myDataVMDKFile -ne "") { if (-not(Test-Path -Path $myDataVMDKFile)) { Write-Host "Error: VMDK file does not exist. Exiting..." -ForegroundColor Red Exit } } ################################################################################ # Check the VHD ################################################################################ $extension = ([IO.Path]::GetExtension($myOSVHDFile)).ToUpper() if ($extension -ne ".VHD") { Write-Host "Error: Not a VHD file. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Import Virtual Machine Converter ################################################################################ if (-not(Test-Path -Path $ConverterFile)) { Write-Host "Error: Microsoft Virtual Machine Converter not installed. Exiting..." -ForegroundColor Red Exit } Import-Module $ConverterFile # Get the VHD file name only and the URL of the container $VHDOSFileName = [io.path]::GetFileName($myOSVHDFile) $VHDDataFileName = [io.path]::GetFileName($myDataVHDFile) $urlOS = "https://" + $myStorageAcct + ".blob.core.windows.net/" + $myContainer + "/" + $VHDOSFileName $urlData = "https://" + $myStorageAcct + ".blob.core.windows.net/" + $myContainer + "/" + $VHDDataFileName ################################################################################ # Log in to your Azure account ################################################################################ Write-Host "Waiting...Login prompt coming up..." -ForegroundColor Yellow try { Login-AzureRmAccount } catch { Write-Host $_.Exception.Message -ForegroundColor Red Exit } ################################################################################ # Check if you can log to the specified subscription ################################################################################ try { if(Get-AzureRmSubscription -SubscriptionName $mySubscription -ErrorAction Stop) {} } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: The name of the subscription is incorrect or you don't have rights. Exiting..." -ForegroundColor Red Exit } Get-AzureRmSubscription -SubscriptionName $mySubscription | Select-AzureRmSubscription Write-Host "Your current subscription is set to" $mySubscription -ForegroundColor Yellow ################################################################################ # Check if the resource group exists ################################################################################ try { if(Get-AzureRmResourceGroup -ResourceGroupName $myResourceGroup -ErrorAction Stop) {} } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: The name of the resource group is incorrect or you don't have rights. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Converting from VMDK to VHD (OS) # If you get an error from Azure that the blob is corrupted, use # -VhdType FixedHardDisk ################################################################################ try { Write-Host "Converting VMDK OS Disk to VHD in progress... Hit any key when no progress on top." -ForegroundColor Yellow if(ConvertTo-MvmcVirtualHardDisk -SourceLiteralPath $myOSVMDKFile -DestinationLiteralPath $myOSVHDFile ` -VhdType DynamicHardDisk -VhdFormat Vhd -ErrorAction Stop) {} } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: The VMDK file is most likely corrupt, access violation or incorrect VMDK format. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Converting from VMDK to VHD (Data) # If you get an error from Azure that the blob is corrupted, use # -VhdType FixedHardDisk ################################################################################ if ($myDataVMDKFile -ne "") { try { Write-Host "Converting VMDK Data Disk to VHD in progress... Hit any key when no progress on top." -ForegroundColor Yellow if(ConvertTo-MvmcVirtualHardDisk -SourceLiteralPath $myDataVMDKFile -DestinationLiteralPath $myDataVHDFile ` -VhdType DynamicHardDisk -VhdFormat Vhd -ErrorAction Stop) {} } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: The VMDK file is most likely corrupt, access violation or incorrect VMDK format. Exiting..." -ForegroundColor Red Exit } } ################################################################################ # Upload the OS VHD to the existing blob/container ################################################################################ Write-Host "Uploading OS VHD to" $urlOS -Foreground Yellow Add-AzureRmVhd -ResourceGroupName $myResourceGroup -Destination $urlOS -LocalFilePath $myOSVHDFile -OverWrite # Upload the Data VHD to the existing blob/container if ($myDataVMDKFile -ne "") { Write-Host "Uploading Data VHD to" $urlData -Foreground Yellow Add-AzureRmVhd -ResourceGroupName $myResourceGroup -Destination $urlData -LocalFilePath $myDataVHDFile -OverWrite } ################################################################################ # Create the OS disk ################################################################################ Write-Host "Creating OS disk from VHD..." -Foreground Yellow $VHDOSFileNameOnly = [io.path]::GetFileNameWithoutExtension($VHDOSFileName) try { $osDisk = New-AzureRmDisk -DiskName $VHDOSFileName -Disk ` (New-AzureRmDiskConfig -AccountType StandardLRS -Location $myLocation -CreateOption Import ` -SourceUri $urlOS) ` -ResourceGroupName $myResourceGroup } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong when creating the OS disk. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Create the Data disk ################################################################################ if ($myDataVMDKFile -ne "") { Write-Host "Creating data disk from VHD..." -Foreground Yellow $VHDDataFileNameOnly = [io.path]::GetFileNameWithoutExtension($VHDDataFileName) try { $dataDisk = New-AzureRmDisk -DiskName $VHDDataFileName -Disk ` (New-AzureRmDiskConfig -AccountType StandardLRS -Location $myLocation -CreateOption Import ` -SourceUri $urlData) ` -ResourceGroupName $myResourceGroup } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong when creating the data disk. Exiting..." -ForegroundColor Red Exit } } ################################################################################ # Create and assign a public IP ################################################################################ $ipName = "ip" + $VHDOSFileNameOnly Write-Host "Creating and assigning a public IP" $ipName -Foreground Yellow try { $pip = New-AzureRmPublicIpAddress -Name $ipName -ResourceGroupName $myResourceGroup -Location $myLocation ` -AllocationMethod Dynamic } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong with the IP. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Create the NIC ################################################################################ $nicName = "nic" + $VHDOSFileNameOnly Write-Host "Creating the NIC" $nicName -Foreground Yellow try { $VNET = Get-AzureRmVirtualNetwork -Name $myVNET -ResourceGroupName $myResourceGroup -ErrorAction Stop } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong with the NIC. Check the vNet name. Exiting..." -ForegroundColor Red Exit } try { $nic = New-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $myResourceGroup ` -Location $myLocation -SubnetId $VNET.Subnets[0].Id -PublicIpAddressId $pip.Id -NetworkSecurityGroupId $mySecurityGroup.Id } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong with the NIC. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Create the VM config ################################################################################ $vmName = $VHDOSFileNameOnly Write-Host "Creating the VM config for" $VHDOSFileNameOnly -Foreground Yellow try { $vmConfig = New-AzureRmVMConfig -VMName $vmName -VMSize $myVMSize -ErrorAction Stop } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong with the VM Config. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Attach the NIC ################################################################################ Write-Host "Attaching the NIC to" $VHDOSFileNameOnly -Foreground Yellow $vm = Add-AzureRmVMNetworkInterface -VM $vmConfig -Id $nic.Id ################################################################################ # Add the OS disk ################################################################################ Write-Host "Adding the OS disk to" $VHDOSFileNameOnly -Foreground Yellow try { $vm = Set-AzureRmVMOSDisk -VM $vm -ManagedDiskId $osDisk.Id -StorageAccountType StandardLRS ` -CreateOption Attach -Windows } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong with the OS disk. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Create the VM ################################################################################ Write-Host "Creating the VM. This will take some time...Hit any key." $VHDOSFileNameOnly -Foreground Yellow try { New-AzureRmVM -ResourceGroupName $myResourceGroup -Location $myLocation -VM $vm } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong with the VM creation. Exiting..." -ForegroundColor Red Exit } ################################################################################ # Add the Data disk ################################################################################ if ($myDataVMDKFile -ne "") { Write-Host "Adding the Data disk to" $VHDDataFileNameOnly -Foreground Yellow try { $vm = Get-AzureRmVM -Name $VHDOSFileNameOnly -ResourceGroupName $myResourceGroup $vm = Add-AzureRmVMDataDisk -VM $vm -ManagedDiskId $DataDisk.Id -StorageAccountType StandardLRS ` -CreateOption Attach -Lun 1 Update-AzureRmVM -VM $vm -ResourceGroupName $myResourceGroup } catch { Write-Host $_.Exception.Message -ForegroundColor Red Write-Host "Error: Something went wrong with the Data disk. Exiting..." -ForegroundColor Red Exit } } ################################################################################ # Finalize ################################################################################ $publicIP = Get-AzureRmPublicIpAddress -Name $ipName -ResourceGroupName $myResourceGroup $EndDate = (Get-Date) Write-Host "========================================================================" -ForegroundColor Yellow Write-Host "CREATION OF THE VM COMPLETED. The IP address is" $publicIP.ipaddress -ForegroundColor Yellow Write-Host "========================================================================" -ForegroundColor Yellow Write-Host "=> TOTAL TIME:"($EndDate - $StartDate).Days "days," ($EndDate - $StartDate).Hours "hours," ($EndDate - $StartDate).Minutes "minutes."