Last updated 1 year ago by Sander Rozemuller
This article is part one of a serie posts about WVD image management automated. In this first part I will describe how to create and connect a new disk (based on a snapshot) to a new Azure VM based on the existing sessionhost configuration. This will save a lot of extra parameters like VMsize, network settings and type. After the VM is started you will get the information how to connect to the VM by RDP (3389) with specific credentials specially created for this VM.
Updated 14-03-2021
This post is a part of the series WVD Image Management Automated.
- Create WVD image version based on existing config with PowerShell – Part 1
- Save WVD image with Sysprep as Image Gallery version – Part 2
- Create WVD Session hosts based on Shared Image Gallery version – Part 3
- WVD housekeeping, removing all unused sessionhosts, disks and images – Part 4
- Monitor Image Versions with Azure Monitor – Part 5
Table of contents
- WVD in a nutshell
- Requirements
- Used Azure components
- Finding hostpool and session hosts
- Disk configuration
- Networking
- Create VM
- Generate credentials
- How to use
- Results
WVD in a nutshell
Windows Virtual Desktop is a desktop and app virtualization service that runs on the cloud. For a full description click here
In the basic WVD exists on three big parts: clients, WVD and the Azure VM’s & Services. As the picture below says only WVD is Microsoft managed. For that part you can use ARM templates and you are out of control at that part.

The real fun starts at the dynamic environment like clients and VM’s. In this article we will focus how to deal with disk- and image management automatically.
Since WVD has no image provisioning like Citrix, disk management goes a bit different. Before knowing how to automate things it is good to know which elements we need and how the tasks need to be done when an image needs to be updated.
Requirements
PowerShell Modules
Microsoft has enrolled a new PowerShell module for Windows Virtual Desktop. For executing commands in this article you need this module. How to setup this module please check https://docs.microsoft.com/en-us/azure/virtual-desktop/powershell-module
We also need the az.network and az.compute PowerShell Modules
Used Azure components
- Windows Virtual Desktop Hostpool
- Virtual Machines (sessions hosts in a hostpool)
- Disks & Snapshosts
- Network
- Shared Image Gallery
Finding hostpool and session hosts
In this case I assume you already have a WVD environment with a hostpool, and sessionhosts. I will talk about WVD environment deployment later.
First we need to now in which hostpool you want to create a new disk. So the script will ask you for that. After all actually these are the only variables you really need :). Since the hostpool is the main WVD part we know every other component.
I’ve added an extra variable $localPublicIp. I will talk about this later in this topic. I’m also importing the needed modules.
param(
[parameter(mandatory = $true)][string]$hostpoolName,
[parameter(mandatory = $true)][string]$snapshotName,
[parameter(mandatory = $true)][string]$localPublicIp
)
import-module az.desktopvirtualization
import-module az.network
import-module az.compute
# Get WVD hostpool information
$hostpool = Get-AzWvdHostPool | Where-Object { $_.Name -eq $hostpoolname }
$hostpoolResourceGroup = ($hostpool).id.split("/")[4]
# Get current WVD Configurgation
$sessionHosts = Get-AzWvdSessionHost -ResourceGroupName $hostpoolResourceGroup -HostPoolName $hostpool.name
$sessionHostName = ($sessionHosts.Name.Split("/")[-1]).Split(".")[0]
$currentVmInfo = Get-AzVM -name $sessionHostName
$virtualMachineSize = $currentVmInfo.hardwareprofile.vmsize
$virtualNetworkSubnet = (Get-AzNetworkInterface -ResourceId $currentVmInfo.NetworkProfile.NetworkInterfaces.id).IpConfigurations.subnet.id
Now we have our hostpool and sessionhosts loaded, the next step is the needed snapshot. This should be a snaphost which has been not syspreped before. This because of its limits.
You can run the Sysprep command up to 8 times on a single Windows image. After running Sysprep 8 times, you must recreate your Windows image. In previous versions of Windows, you could use the SkipRearm
answer file setting to reset the Windows Product Activation clock when running Sysprep. If you are using a volume licensing key or a retail product key, you don’t have to use SkipRearm
because Windows is automatically activated.
More information about sysprep a Windows installation check the Microsoft docs.
# Snapshot values for creating a disk
try {
$snapshot = get-azsnapshot -SnapshotName $snapshotname
$resourceGroupName = $snapshot.ResourceGroupName
}
catch {
Throw "No snapshot found, $_"
}
After loading all the basics it is time to deploy thing to Azure. Because of the number of temporary components I will create a new resource group first. This will help you cleaning up resources at the end, since every component is in the same resource group. Just deleting the resource group will be fine then.
# Creating a new temporary resource group first
$ResourceGroup = New-AzResourceGroup -Name $TempResourceGroup -Location $ResourceGroupLocation
Disk configuration
When creating a new disk you need to setup a disk configuration. After then you can create a new disk if it not exists already.
$VirtualMachineName = ('vm_' + $snapshot.name)
# Creating a disk
$diskConfig = New-AzDiskConfig -SkuName "Premium_LRS" -Location $ResourceGroup.location -CreateOption Copy -SourceResourceId $snapshot.Id
$diskname = ($VirtualMachineName.ToLower()+ '-OS')
$disk = Get-azdisk -diskname $diskname
try {
$disk = New-AzDisk -Disk $diskConfig -ResourceGroupName $ResourceGroup.resourceGroupName -DiskName $diskName
}
catch {
Throw "$diskname allready exits, $_"
}
Networking
The next step is the networking part. This consists of three components. A network interface card, public ip and a network security group. In the first part I will create a public IP. This will be the IP to connect when the virtual machine is finished.
In the next step I will create a network interface card and will connect the public IP to it.
In the third step I will create a Network Security Group (NSG) to protect the virtual machine for attackers. The NSG will be connected to the virtual machine only. Positive side effect is we leave the production NSG which is on the WVD subnet untouched.
At last I will add the RDP port 3389 to the NSG with my private IP only instead of the whole internet. The function Add-FirewallRule can be found in the complete script at my GitHub page.
$PublicIpParameters = @{
Name = ($VirtualMachineName.ToLower() + '_ip')
ResourceGroupName = $ResourceGroup.ResourceGroupName
Location = $ResourceGroup.Location
AllocationMethod = 'Dynamic'
Force = $true
}
$publicIp = New-AzPublicIpAddress @PublicIpParameters
$NicParameters = @{
Name = ($VirtualMachineName.ToLower() + '_nic')
ResourceGroupName = $ResourceGroup.resourceGroupName
Location = $ResourceGroup.Location
SubnetId = $virtualNetworkSubnet
PublicIpAddressId = $publicIp.Id
Force = $true
}
$nic = New-AzNetworkInterface @NicParameters
# Creating a temporary network security group
$NsgParameters = @{
ResourceGroupName = $ResourceGroup.ResourceGroupName
Location = $ResourceGroup.Location
Name = ($VirtualMachineName.ToLower() + '_nsg')
}
$nsg = New-AzNetworkSecurityGroup @NsgParameters
# Adding a security rule to only the network interface card
Add-FirewallRule -NSG $NSG -localPublicIp $localPublicIp -port 3389
$nic.NetworkSecurityGroup = $nsg
$nic | Set-AzNetworkInterface

Create VM
Now the network is in place and save let’s create a new VM with a new disk based on a snapshot.
# Creating virtual machine configuration
$VirtualMachine = New-AzVMConfig -VMName $VirtualMachineName -VMSize $virtualMachineSize
# Use the Managed Disk Resource Id to attach it to the virtual machine. Please change the OS type to linux if OS disk has linux OS
$VirtualMachine = Set-AzVMOSDisk -VM $VirtualMachine -ManagedDiskId $disk.Id -CreateOption Attach -Windows
# Create a public IP for the VM
$VirtualMachine = Add-AzVMNetworkInterface -VM $VirtualMachine -Id $nic.Id
#Create the virtual machine with Managed Disk
$newVm = New-AzVM -VM $VirtualMachine -ResourceGroupName $ResourceGroup.resourceGroupName -Location $ResourceGroup.Location
When the VM is created we create an username and password in the VM by installing the VMAccessAgent extention.
https://docs.microsoft.com/en-us/troubleshoot/azure/virtual-machines/support-agent-extensions
Generate credentials
First we need to create a random username and password. I also created a function for that as well, which can be found in the complete script.
$userName = create-randomString -type 'username'
$password = ConvertTo-SecureString (create-randomString -type 'password') -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($userName, $password);
$CredentialParameters = @{
ResourceGroupName = $ResourceGroup.ResourceGroupName
Location = $ResourceGroup.Location
VMName = $VirtualMachineName
Credential = $Credential
typeHandlerVersion = "2.0"
Name = "VMAccessAgent"
}
Set-AzVMAccessExtension @CredentialParameters
How to use
An example how to use this script.
$hostpoolname = 'wvd-hostpool'
$snapshotname = 'wvd-acc-per-2021.03.12-BS'
$TempResourceGroup = 'temp-deploy-updates'
$ResourceGroupLocation = 'westeurope'
$localPublicIp = '127.0.0.1'
.\WVD-Create-UpdateVM.ps1 -HostpoolName $hostpoolname -SnapshotName $snapshotname -TempResourceGroup $TempResourceGroup -ResourceGroupLocation $ResourceGroupLocation -localPublicIp $localPublicIp
Results
At last we combining everything we know together to write the content to our screen.
if ($newVm) {
#Adding the role
$publicIp = (Get-AzPublicIpAddress | where { $_.name -match $VirtualMachineName }).IpAddress
$bodyValues = [Ordered]@{
Status = $newVm.StatusCode
hostPool = $hostpoolName
virtualMachineName = $VirtualMachineName
resourceGroupName = $ResourceGroup.ResourceGroupName
virtualMachinePublicIp = $publicIp
username = $userName
password = $password | ConvertFrom-SecureString -AsPlainText
virtualMachineDisk = $diskname
engineersIp = $localpublicIp
}
}
Write-Output $bodyValues


The whole script from this article can be found at my Github Windows Virtual Desktop repository.
In the next episode I will describe how to finish the disk with an automated sysprep and moving the disk as a version into the Azure Shared Image Gallery.
Thank you for reading my blog post about WVD Image Management Automated.
Pingback:WVD Weekly Blog post 27th September - 4th October 2020 - WVD Community