If you to want to upload an on-premises Hyper-V VM to the cloud to create a custom Azure image containing all your business applications in a preconfigured state, this post will help you.
Latest posts by Surender Kumar (see all)

Azure baseline images can be of two types: specialized images and generalized images. A specialized image is used to create only one VM, whereas a generalized image can be used to create multiple clone VMs. In this post, we will focus on creating a generalized Azure image by removing machine-specific information from it, which is suitable for cloning.

For demonstration purposes, I will use a Hyper-V VM running Windows Server 2022 with a preconfigured IIS server, SFTP server, and a business web app. You can have any application running on your VM that you want to include in your custom Azure image. Be aware that the system preparation tool (sysprep), which we will use later to generalize the OS, does not support all server roles. Check the supported roles here.


You will need the following prerequisites to follow along with this guide:

  • Active Azure subscription
  • Azure Az PowerShell module installed
  • Windows machine with Hyper-V installed
  • A generation 1 VM running with the required OS and set of applications (in our case, Server 2022, IIS, SFTP server, and business web app). Azure offers generation 2 VMs, but to avoid any issues, I recommend using a generation 1 VM.

Prepare your virtual machine

First, make sure you have configured your VM the way you like. For this demo, I've made sure that the IIS role is installed, an SFTP server is configured, and a business app is working on the VM as expected.

In addition, you also need to adjust a few Windows settings. These settings are not required, but are recommended to ensure you can connect the new VM that you will provision later in your Azure account. Log in to your VM, and follow these steps:

The Azure platform mounts an ISO file to a DVD drive when creating a VM from a generalized image. So, we will set the SAN policy to OnlineAll to ensure that the system brings all newly discovered disks online and makes them available for read/write. To do so, launch diskpart in your VM, and type the following command:

san policy=OnlineAll
(iView and change the SAN policy using diskpartg)
View and change the SAN policy using diskpart

View and change the SAN policy using diskpart

Run the following PowerShell commands to enable RDP and make sure it is set to use the default port number and network-level authentication:

Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -Value 0 -Type DWord -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" -Name fDenyTSConnections -Value 0 -Type DWord -Force
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp" -Name PortNumber -Value 3389 -Type DWord -Force
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name UserAuthentication -Value 1 -Type DWord -Force

You can also enable PowerShell remoting using the following PowerShell command:

Enable-PSRemoting -Force

Enable Windows firewall and necessary rules:

Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled True
Set-NetFirewallRule -Name FPS-ICMP4-ERQ-In, RemoteDesktop-UserMode-In-TCP, WINRM-HTTP-In-TCP, WINRM-HTTP-In-TCP-PUBLIC, IIS-WebServerRole-QUIC-In-UDP, IIS-WebServerRole-HTTPS-In-TCP, IIS-WebServerRole-HTTP-In-TCP, OpenSSH-Server-In-TCP -Enabled True

Create new rules to allow bidirectional communication to the virtual public IP address of the Azure platform:

New-NetFirewallRule -DisplayName AzurePlatform -Direction Inbound -RemoteAddress -Profile Any -Action Allow -EdgeTraversalPolicy Allow
New-NetFirewallRule -DisplayName AzurePlatform -Direction Outbound -RemoteAddress -Profile Any -Action Allow

Make sure all Windows updates are installed, and the VM is not pending a restart. You can restart it one more time to be sure.

Finally, you need to run sysprep on your VM. To do so, run the following PS commands:

Remove-Item -Path "C:\Windows\Panther" -Recurse
C:\Windows\System32\Sysprep\sysprep.exe /generalize /shutdown /oobe

Running sysprep with the generalize and oobe options on the VM

Running sysprep with the generalize and oobe options on the VM

Sysprep now automatically turns off the VM. You shouldn't turn it back on until you've uploaded the VHD to Azure.

Prepare your virtual hard disk

Now you need to prepare your VM's virtual hard disk so that it can be uploaded to Azure. Before you can upload, the disk needs to meet the following prerequisites:

  • The disk format must be VHD.
  • The disk type must be Fixed.
  • The virtual size must be a multiple of one (1) MB.
  • The minimum virtual size must be at least 20 MB.

The Azure Az PowerShell module offers an Add-AzVHD cmdlet, which can handle all the conversion, resizing, optimization, and upload operations, but it requires Hyper-V to be installed. If Hyper-V isn't available, you need to manually convert and optimize the virtual disk with the following PowerShell commands:

Get-VHD "D:\VM\SRV301.vhdx"
Convert-VHD -Path "D:\VM\SRV301.vhdx" -DestinationPath "D:\VM\SRV301.vhd" -VHDType Fixed
The Get-VHD command gives you information about the current virtual hard disk. If you notice that the disk is not properly aligned, use the Resize-VHD command, as shown below:
Resize-VHD -Path D:\VM\SRV301.vhd -SizeBytes 30GB

You need to take care while specifying the value of the -SizeBytes parameter so that the resultant size is a multiple of 1 MB, as required by Azure. Assuming that the current size of my disk is 27.5 GB, I extended it to 30 GB.

Since we already have Hyper-V installed, we will just use the Add-AzVHD cmdlet to automatically prepare the virtual hard disk and upload it from our on-premises machine to Azure.

Prepare to upload VHD

If Azure Az PowerShell is not installed on your machine, you can install it with the following command:

Install-Module -Name Az -Repository PSGallery -Force
Installing the Az PowerShell module in Windows

Installing the Az PowerShell module in Windows

Now I'll use the Connect-AzAccount cmdlet to connect my Hyper-V host to Azure and type the credentials in the popup window to log in to my Azure account.

Connecting the Azure account using the Connect AzAccount cmdlet

Connecting the Azure account using the Connect AzAccount cmdlet

Connecting the Azure account using the Connect-AzAccount cmdlet

Next, run the following command to create a new resource group:

New-AzResourceGroup -Name awesomeResourceGroup01 -Location eastus
Creating a resource group in Azure with PowerShell

Creating a resource group in Azure with PowerShell

Skip this step if you want to use one of your existing resource groups.

Upload VHD and create a generalized disk

Finally, use the Add-AzVHD cmdlet, as shown below, to upload the virtual hard disk to Azure:

Add-AzVhd -LocalFilePath "D:\VM\SRV301.vhdx" -ResourceGroupName awesomeResourceGroup01 -Location eastus -DiskName myServerDisk -NumberOfUploaderThreads 32

This command uses Hyper-V functionality to convert an existing VHDX to a new VHD disk in the same directory, optimize it by detecting empty blocks, calculate the MD5 hash of a new disk, and then upload it directly to a managed disk named myServerDisk in Azure. The command could take a while to finish, so you just have to be patient.

When the disk is uploaded, you can use the Get-AzDisk cmdlet to view the uploaded VM's information:

Get-AzDisk -ResourceGroupName "awesomeResourceGroup01" -DiskName "myServerDisk"
Viewing the newly uploaded generalized disk

Viewing the newly uploaded generalized disk

Create a custom Azure image from disk

Now that you have created a managed disk in Azure, we will use it to create a generalized OS image. To do so, run the following PowerShell commands:

$locationName = "eastus"
$imageName = "imgServer2022"
$resourceGroup = "awesomeResourceGroup01"
$managedDisk = Get-AzDisk -ResourceGroupName "awesomeResourceGroup01" -DiskName "myServerDisk"
$imageCfg = New-AzImageConfig -Location $locationName
$imageCfg = Set-AzImageOsDisk -Image $imageCfg -OsState Generalized -OsType Windows -ManagedDiskId $managedDisk.Id
New-AzImage -ImageName $imageName -ResourceGroupName $resourceGroup -Image $imageCfg 
Creating a generalized OS image from a managed disk

Creating a generalized OS image from a managed disk

The above commands will create a generalized OS image with the name imgServer2022 using the virtual disk you uploaded to Azure. You can view the managed image with the following command:

Get-AzImage -ResourceGroupName "awesomeResourceGroup01" -ImageName "imgServer2022"
Viewing the generalized OS image

Viewing the generalized OS image

Launch a new VM using a custom Azure image

We can now create a new VM using the managed OS image we created in the previous step. When creating a new VM, click the See all images link under the Image field, click My Images in the left section, and then select the imgServer2022 image. See the screenshots below for reference:

Launching a new VM using a generalized OS image

Launching a new VM using a generalized OS image

Selecting a custom image from My Images

Selecting a custom image from My Images

Set all other fields as required, and click the Review + Create button. Now, wait while the new VM is provisioned for you.

Connect to the new VM

Once the VM is successfully provisioned and started, click the VM name and select the Networking option. Make sure the necessary inbound port rules are configured. In my case, I created a few rules to allow the RDP, SFTP server (SSH), and HTTP(S) ports.

Configuring inbound port rules to allow the necessary traffic

Configuring inbound port rules to allow the necessary traffic

When you connected to the VM via RDP, we can verify that IIS, the SFTP server, and the web app are already running on the new VM.

Subscribe to 4sysops newsletter!

Verifying apps and services on a new VM

Verifying apps and services on a new VM

You just created a custom Azure image. The image is generalized, so you can use it as a baseline image to provision any number of VMs, saving you a lot of time and effort.

  1. Chris 2 months ago

    how would i take a production on-prem server vm and move it to azure

    • Author

      You can use the same procedure as mentioned in this post. If you’re going to be creating only a single VM in Azure from imported disk, you could skip the Sysprep step.

Leave a reply

Please enclose code in pre tags

Your email address will not be published.


© 4sysops 2006 - 2023


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account