<#
.SYNOPSIS
Complete Azure Virtual Desktop (AVD) Setup Script
.DESCRIPTION
This script sets up Azure Virtual Desktop for an existing Windows VM:
- Creates Host Pool (Personal type)
- Creates Desktop Application Group
- Creates Workspace
- Azure AD joins the VM
- Installs AVD agents
- Registers session host
- Assigns user to session host
.PARAMETER ResourceGroupName
The resource group containing the VM
.PARAMETER VMName
The name of the existing Windows VM
.PARAMETER Location
Azure region (default: eastus)
.PARAMETER HostPoolName
Name for the AVD Host Pool
.PARAMETER AppGroupName
Name for the Application Group
.PARAMETER WorkspaceName
Name for the AVD Workspace
.PARAMETER AssignedUser
User Principal Name to assign to the session host
.EXAMPLE
.\Setup-AVD-Complete.ps1 -ResourceGroupName "RG-WIN11-BASTION" -VMName "VM-Win11" -AssignedUser "user@domain.com"
.NOTES
Author: Generated by Claude Code
Date: 2026-01-03
Requires: Azure CLI, Owner/Contributor role on subscription
#>
param(
[Parameter(Mandatory=$true)]
[string]$ResourceGroupName,
[Parameter(Mandatory=$true)]
[string]$VMName,
[Parameter(Mandatory=$false)]
[string]$Location = "eastus",
[Parameter(Mandatory=$false)]
[string]$HostPoolName = "HP-AVD-Personal",
[Parameter(Mandatory=$false)]
[string]$AppGroupName = "AG-AVD-Desktop",
[Parameter(Mandatory=$false)]
[string]$WorkspaceName = "WS-AVD",
[Parameter(Mandatory=$false)]
[string]$AssignedUser
)
$ErrorActionPreference = "Stop"
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " Azure Virtual Desktop Setup Script" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
# Get subscription ID
Write-Host "[1/12] Getting subscription information..." -ForegroundColor Yellow
$subscriptionInfo = az account show --query "{id:id, name:name}" -o json | ConvertFrom-Json
$SubscriptionId = $subscriptionInfo.id
Write-Host " Subscription: $($subscriptionInfo.name)" -ForegroundColor Green
Write-Host " ID: $SubscriptionId" -ForegroundColor Green
# Verify VM exists
Write-Host ""
Write-Host "[2/12] Verifying VM exists..." -ForegroundColor Yellow
$vmInfo = az vm show --resource-group $ResourceGroupName --name $VMName --query "{name:name, location:location, vmId:vmId}" -o json 2>$null | ConvertFrom-Json
if (-not $vmInfo) {
Write-Host " ERROR: VM '$VMName' not found in resource group '$ResourceGroupName'" -ForegroundColor Red
exit 1
}
Write-Host " VM Found: $($vmInfo.name)" -ForegroundColor Green
Write-Host " Location: $($vmInfo.location)" -ForegroundColor Green
# Use VM's location if not specified
if (-not $Location) {
$Location = $vmInfo.location
}
# Create Host Pool
Write-Host ""
Write-Host "[3/12] Creating Host Pool: $HostPoolName..." -ForegroundColor Yellow
az desktopvirtualization hostpool create `
--resource-group $ResourceGroupName `
--name $HostPoolName `
--location $Location `
--host-pool-type "Personal" `
--personal-desktop-assignment-type "Automatic" `
--load-balancer-type "Persistent" `
--preferred-app-group-type "Desktop" `
--registration-info expiration-time="$((Get-Date).AddDays(1).ToString('yyyy-MM-ddTHH:mm:ss.fffffffZ'))" registration-token-operation="Update" `
--output none
Write-Host " Host Pool created successfully" -ForegroundColor Green
# Create Application Group
Write-Host ""
Write-Host "[4/12] Creating Application Group: $AppGroupName..." -ForegroundColor Yellow
$hostPoolArmPath = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.DesktopVirtualization/hostPools/$HostPoolName"
az desktopvirtualization applicationgroup create `
--resource-group $ResourceGroupName `
--name $AppGroupName `
--location $Location `
--application-group-type "Desktop" `
--host-pool-arm-path $hostPoolArmPath `
--output none
Write-Host " Application Group created successfully" -ForegroundColor Green
# Create Workspace
Write-Host ""
Write-Host "[5/12] Creating Workspace: $WorkspaceName..." -ForegroundColor Yellow
az desktopvirtualization workspace create `
--resource-group $ResourceGroupName `
--name $WorkspaceName `
--location $Location `
--output none
Write-Host " Workspace created successfully" -ForegroundColor Green
# Associate Application Group with Workspace
Write-Host ""
Write-Host "[6/12] Associating Application Group with Workspace..." -ForegroundColor Yellow
$appGroupArmPath = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.DesktopVirtualization/applicationGroups/$AppGroupName"
az desktopvirtualization workspace update `
--resource-group $ResourceGroupName `
--name $WorkspaceName `
--application-group-references $appGroupArmPath `
--output none
Write-Host " Association completed successfully" -ForegroundColor Green
# Generate Registration Token
Write-Host ""
Write-Host "[7/12] Generating registration token..." -ForegroundColor Yellow
$expirationTime = (Get-Date).AddHours(24).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffffffZ')
$tokenResult = az rest --method POST `
--uri "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.DesktopVirtualization/hostPools/$HostPoolName/retrieveRegistrationToken?api-version=2023-09-05" `
--body "{}" `
-o json | ConvertFrom-Json
$RegistrationToken = $tokenResult.token
Write-Host " Registration token generated (expires in 24 hours)" -ForegroundColor Green
# Enable System Assigned Managed Identity
Write-Host ""
Write-Host "[8/12] Enabling managed identity on VM..." -ForegroundColor Yellow
az vm identity assign `
--resource-group $ResourceGroupName `
--name $VMName `
--output none
Write-Host " Managed identity enabled" -ForegroundColor Green
# Install AADLoginForWindows extension (Azure AD Join)
Write-Host ""
Write-Host "[9/12] Installing Azure AD Login extension (Azure AD Join)..." -ForegroundColor Yellow
az vm extension set `
--resource-group $ResourceGroupName `
--vm-name $VMName `
--name "AADLoginForWindows" `
--publisher "Microsoft.Azure.ActiveDirectory" `
--output none
Write-Host " Azure AD Login extension installed" -ForegroundColor Green
# Install AVD Agents and set registration token via Run Command
Write-Host ""
Write-Host "[10/12] Installing AVD agents and configuring registration..." -ForegroundColor Yellow
$avdInstallScript = @"
`$ErrorActionPreference = 'Stop'
`$tempDir = 'C:\AVDTemp'
# Create temp directory
if (!(Test-Path `$tempDir)) {
New-Item -ItemType Directory -Path `$tempDir -Force | Out-Null
}
# Download AVD Agent
Write-Host 'Downloading AVD Agent...'
`$agentUrl = 'https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrmXv'
`$agentPath = Join-Path `$tempDir 'Microsoft.RDInfra.RDAgent.Installer-x64.msi'
Invoke-WebRequest -Uri `$agentUrl -OutFile `$agentPath -UseBasicParsing
# Download Boot Loader
Write-Host 'Downloading Boot Loader...'
`$bootLoaderUrl = 'https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrxrH'
`$bootLoaderPath = Join-Path `$tempDir 'Microsoft.RDInfra.RDAgentBootLoader.Installer-x64.msi'
Invoke-WebRequest -Uri `$bootLoaderUrl -OutFile `$bootLoaderPath -UseBasicParsing
# Install Boot Loader first
Write-Host 'Installing Boot Loader...'
Start-Process msiexec.exe -ArgumentList "/i `$bootLoaderPath /quiet /norestart" -Wait -NoNewWindow
# Install AVD Agent with registration token
Write-Host 'Installing AVD Agent with registration token...'
Start-Process msiexec.exe -ArgumentList "/i `$agentPath /quiet /norestart REGISTRATIONTOKEN=$RegistrationToken" -Wait -NoNewWindow
Write-Host 'AVD Agent installation completed'
"@
az vm run-command invoke `
--resource-group $ResourceGroupName `
--name $VMName `
--command-id RunPowerShellScript `
--scripts $avdInstallScript `
--output none
Write-Host " AVD agents installed" -ForegroundColor Green
# Set registration token in registry and restart services
Write-Host ""
Write-Host "[11/12] Configuring registry and restarting services..." -ForegroundColor Yellow
$registryScript = @"
`$token = '$RegistrationToken'
`$regPath = 'HKLM:\SOFTWARE\Microsoft\RDInfraAgent'
if (!(Test-Path `$regPath)) {
New-Item -Path `$regPath -Force | Out-Null
}
Set-ItemProperty -Path `$regPath -Name 'RegistrationToken' -Value `$token -Force
Set-ItemProperty -Path `$regPath -Name 'IsRegistered' -Value 0 -Type DWord -Force
Stop-Service RDAgentBootLoader -Force -ErrorAction SilentlyContinue
Stop-Service RDAgent -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 5
Start-Service RDAgentBootLoader
Start-Service RDAgent
Start-Sleep -Seconds 15
`$regValue = Get-ItemProperty -Path `$regPath
Write-Host "IsRegistered: `$(`$regValue.IsRegistered)"
"@
$result = az vm run-command invoke `
--resource-group $ResourceGroupName `
--name $VMName `
--command-id RunPowerShellScript `
--scripts $registryScript `
--query "value[0].message" `
-o tsv
Write-Host " Registry configured, services restarted" -ForegroundColor Green
Write-Host " $result" -ForegroundColor Gray
# Assign user to session host (if specified)
Write-Host ""
Write-Host "[12/12] Assigning user to session host..." -ForegroundColor Yellow
if ($AssignedUser) {
# Wait for session host to register
Start-Sleep -Seconds 10
az rest --method PATCH `
--uri "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.DesktopVirtualization/hostPools/$HostPoolName/sessionHosts/$VMName`?api-version=2023-09-05" `
--body "{`"properties`":{`"assignedUser`":`"$AssignedUser`"}}" `
--output none
Write-Host " User '$AssignedUser' assigned to session host" -ForegroundColor Green
} else {
Write-Host " No user specified, skipping assignment" -ForegroundColor Yellow
}
# Verify session host registration
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " Verifying Setup" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Start-Sleep -Seconds 5
$sessionHost = az rest --method GET `
--uri "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.DesktopVirtualization/hostPools/$HostPoolName/sessionHosts?api-version=2023-09-05" `
-o json | ConvertFrom-Json
if ($sessionHost.value.Count -gt 0) {
$host = $sessionHost.value[0]
Write-Host ""
Write-Host "Session Host Status:" -ForegroundColor Green
Write-Host " Name: $($host.name.Split('/')[1])" -ForegroundColor White
Write-Host " Status: $($host.properties.status)" -ForegroundColor White
Write-Host " Agent Version: $($host.properties.agentVersion)" -ForegroundColor White
Write-Host " Assigned User: $($host.properties.assignedUser)" -ForegroundColor White
Write-Host " Allow New Session: $($host.properties.allowNewSession)" -ForegroundColor White
} else {
Write-Host " Session host not yet registered. Please wait a few minutes." -ForegroundColor Yellow
}
# Summary
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " Setup Complete!" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Resources Created:" -ForegroundColor Green
Write-Host " - Host Pool: $HostPoolName" -ForegroundColor White
Write-Host " - Application Group: $AppGroupName" -ForegroundColor White
Write-Host " - Workspace: $WorkspaceName" -ForegroundColor White
Write-Host " - Session Host: $VMName" -ForegroundColor White
Write-Host ""
Write-Host "IMPORTANT - Manual Step Required:" -ForegroundColor Yellow
Write-Host " Assign 'Desktop Virtualization User' role to user(s) on the Application Group:" -ForegroundColor Yellow
Write-Host ""
Write-Host " az role assignment create \" -ForegroundColor Gray
Write-Host " --assignee `"<user-object-id>`" \" -ForegroundColor Gray
Write-Host " --role `"Desktop Virtualization User`" \" -ForegroundColor Gray
Write-Host " --scope `"$appGroupArmPath`"" -ForegroundColor Gray
Write-Host ""
Write-Host "Or via Azure Portal:" -ForegroundColor Yellow
Write-Host " 1. Go to Application Group '$AppGroupName'" -ForegroundColor White
Write-Host " 2. Access control (IAM) -> Add role assignment" -ForegroundColor White
Write-Host " 3. Select 'Desktop Virtualization User' role" -ForegroundColor White
Write-Host " 4. Add user(s) and save" -ForegroundColor White
Write-Host ""
Write-Host "To Connect:" -ForegroundColor Green
Write-Host " 1. Download AVD Client: https://aka.ms/avdclient" -ForegroundColor White
Write-Host " 2. Open client and click 'Subscribe'" -ForegroundColor White
Write-Host " 3. Sign in with your Azure AD account" -ForegroundColor White
Write-Host " 4. Double-click the desktop to connect" -ForegroundColor White
Write-Host ""
