Terraform-powered Azure Virtual Desktop lab for demos, training, and proof-of-concept work. It follows a landing-zone style layout while keeping the default deployment focused on AVD fundamentals instead of expensive perimeter services.
Note
Azure costs only start when you run terraform apply. Keep the lab profile for demos and destroy the environment when you are finished.
| Capability | Default build | Why it matters |
|---|---|---|
| Pooled host pool | 2 x Windows 11 Enterprise multi-session hosts | Real AVD pooled behavior without a large VM footprint. |
| Microsoft Entra join | No domain controllers | Modern sign-in path and lower lab complexity. |
| Desktop and RemoteApps | Desktop, Microsoft Edge, Notepad | Demonstrates both common app group patterns. |
| FSLogix profiles | Azure Files with Microsoft Entra Kerberos | Shows profile containers and roaming profile behavior. |
| Autoscale and start | Scaling plan plus Start VM on Connect | Shows AVD power management and cost controls. |
| Observability | Log Analytics, diagnostics, AVD Insights workbook | Gives the lab an operations story, not just a deployment. |
| Safety guardrails | No public IPs on hosts, Key Vault, optional policy/budget | Keeps the default lab practical and reviewable. |
No Azure Firewall, VPN Gateway, Application Gateway, AKS, Bastion, or domain controllers are deployed by default.
The lab separates service, compute, storage, security, monitoring, and networking concerns:
User -> AVD workspace -> Desktop or RemoteApp -> Host pool -> Session host
|
+-> FSLogix profile share
+-> AVD diagnostics
Start with the wiki-style documentation in wiki/README.md, or use the guided docs below.
| Guide | Purpose |
|---|---|
| Full lab guide | End-to-end walkthrough from prerequisites to destroy. |
| Demo runbook | A presenter-friendly script for showing the lab. |
| Operations checklist | Day-2 checks, cost controls, and cleanup steps. |
| Architecture overview | Resource layout and design goals. |
| Network topology | CIDRs, traffic flows, NSGs, and Private Link notes. |
| Identity model | Entra groups, RBAC, VM sign-in, and app access. |
| Testing guide | Connectivity, crash tests, and acceptance criteria. |
| Troubleshooting guide | Common deployment, sign-in, profile, and drift fixes. |
| Phase | Command or page | Outcome |
|---|---|---|
| 1. Plan | Full lab guide | Understand prerequisites, cost posture, and inputs. |
| 2. Configure | terraform.tfvars plus environments/lab.tfvars |
Subscription, owner, region, and Entra group IDs are set. |
| 3. Deploy | terraform apply -var-file="environments/lab.tfvars" |
AVD lab resources are created. |
| 4. Validate | ./scripts/Test-AvdLabConnectivity.ps1 -StartStoppedHosts |
Control plane, session hosts, FSLogix, and network checks pass. |
| 5. Break and recover | ./scripts/Invoke-AvdLabCrashTest.ps1 -RunChaos |
Restart, drain, deallocate/start, and recovery behavior is tested. |
| 6. Destroy | terraform destroy -var-file="environments/lab.tfvars" |
Azure resources are removed when the lab is done. |
PowerShell:
Copy-Item terraform.tfvars.example terraform.tfvars
terraform init
terraform fmt -check -recursive
terraform validate
terraform plan -var-file="environments/lab.tfvars"Bash:
cp terraform.tfvars.example terraform.tfvars
terraform init
terraform fmt -check -recursive
terraform validate
terraform plan -var-file="environments/lab.tfvars"Set at least these values in terraform.tfvars:
subscription_id = "00000000-0000-0000-0000-000000000000"
owner = "Your Name"
avd_users_group_id = "00000000-0000-0000-0000-000000000000"Optional:
avd_admins_group_id = "00000000-0000-0000-0000-000000000000"
alert_email = "admin@example.com"Leave admin_password = "" to generate a password and store it in Key Vault.
terraform apply -var-file="environments/lab.tfvars"After apply, capture the outputs:
terraform outputKey outputs:
| Output | Use |
|---|---|
avd_webclient_url |
User access URL. |
host_pool_name |
Host pool lookup and troubleshooting. |
session_host_names |
VM validation and crash tests. |
fslogix_share_path |
Profile container location. |
avd_insights_workbook_id |
Monitoring workbook. |
Run the full non-destructive health check:
./scripts/Test-AvdLabConnectivity.ps1 -VarFile "environments/lab.tfvars" -StartStoppedHostsRun a dry-run crash test:
./scripts/Invoke-AvdLabCrashTest.ps1Run live resilience tests:
./scripts/Invoke-AvdLabCrashTest.ps1 -RunChaos -Scenario DrainHost,SingleHostRestart,AllHostsRestart,SingleHostDeallocateStart,AllHostsDeallocateStartThe live crash test intentionally disrupts hosts. Use it only when no one needs an active session.
| Profile | Purpose | Defaults |
|---|---|---|
lab |
Lowest-friction demo | 2 x Standard_D2s_v5, StandardSSD, FSLogix Standard LRS, auto-shutdown on |
dev |
Small development profile | Same cost posture as lab, separate environment naming |
prod |
Production-like comparison | 4 x Standard_D4s_v5, Premium disks, Premium/ZRS FSLogix, governance on |
Use the Azure Pricing Calculator for current pricing.
| Flag | Default | What it adds |
|---|---|---|
deploy_fslogix |
true |
Azure Files profile share and SMB RBAC |
deploy_remote_app_group |
true |
RemoteApp app group with demo apps |
deploy_scaling_plan |
true |
Weekday AVD autoscale schedule |
enable_start_vm_on_connect |
true |
Host pool Start VM on Connect |
deploy_monitoring |
true |
Log Analytics, diagnostics, workbook, alerts |
deploy_custom_image |
false |
Azure Compute Gallery scaffolding |
deploy_private_endpoints |
false |
Private endpoints for Key Vault and Azure Files |
deploy_avd_private_link |
false |
Private Link for host pool and workspace |
deploy_governance |
false |
Azure Policy assignments and budget |
.
|-- main.tf
|-- variables.tf
|-- outputs.tf
|-- environments/
|-- landing-zones/
|-- modules/
|-- scripts/
|-- docs/
|-- wiki/
|-- policies/
`-- tests/
- Start VM on Connect and autoscale require Azure Virtual Desktop service-principal RBAC.
- AVD autoscale owns host-pool load balancing during schedule windows, so Terraform ignores
load_balancer_typedrift on the host pool. - FSLogix with Entra Kerberos requires tenant and storage prerequisites.
- Private endpoint modes are off by default because local public laptops often cannot reach private-only data-plane endpoints during bootstrap.
- Keep local
terraform.tfstateprivate. It can contain sensitive values.
- AVD cost estimation
- Entra-joined session hosts
- FSLogix storage
- FSLogix with Entra ID
- AVD autoscale
- AVD Insights
MIT. See LICENSE.