This directory contains a modular Terraform configuration that deploys Azure Virtual Desktop (AVD) environments supporting multiple deployment patterns with enterprise-grade monitoring, scaling, and dashboard capabilities.
🏆 Enterprise-Grade Features:
- Microsoft Cloud Adoption Framework naming standards
- Comprehensive monitoring with Log Analytics
- Automatic scaling plans for cost optimization (40-70% savings)
- Custom dashboards for operational insights
- Cost management with alerts and budget tracking
This configuration supports four distinct AVD deployment patterns:
| Deployment Type | Description | Use Cases | Resource Sharing | Naming Suffix |
|---|---|---|---|---|
pooled_desktop |
Traditional shared desktop environment | Call centers, task workers, training labs | Multiple users per VM | desktop |
personal_desktop |
Dedicated 1:1 desktop assignments | Developers, power users, persistent workloads | One user per VM | personal |
pooled_remoteapp |
Shared published applications | Line-of-business apps, legacy applications | Multiple users per VM | apps |
personal_remoteapp |
Dedicated application access | Sensitive apps, compliance requirements | One user per VM | personalapps |
- Automatic scaling based on usage patterns
- Environment-specific schedules (dev vs prod)
- Cost savings of 40-70% for pooled deployments
- Smart scaling only for pooled deployments (desktop & RemoteApp)
- Enterprise-grade architecture: Uses built-in "Desktop Virtualization Power On Off Contributor" role with subscription-level scope
- Azure Portal compatibility: Separate host pool association resource ensures reliable portal integration
- Best practices: Random UUID for role assignments, lifecycle management for stability
- Log Analytics workspace with comprehensive logging
- Diagnostic settings for all AVD resources
- Performance metrics for session hosts
- Configurable retention (30-730 days)
- Real-time insights for AVD environments
- Key metrics display (sessions, performance, costs)
- Quick navigation to Azure resources
- Environment-specific views
- Daily cost alerts with configurable thresholds
- Budget tracking with dynamic monthly start dates (auto-calculated from current month)
- Budget notifications at 90% and 100% thresholds
- Cost optimization recommendations
- Spending insights and trends
All resources follow Microsoft Cloud Adoption Framework naming standards with deployment-specific suffixes:
| Resource Type | Pattern | Examples |
|---|---|---|
| Host Pool | vdpool-{prefix}-{environment}-{suffix} |
vdpool-avd-dev-desktop, vdpool-avd-prod-apps |
| Application Group | vdag-{prefix}-{environment}-{suffix} |
vdag-avd-dev-personal, vdag-avd-prod-personalapps |
| Workspace | vdws-{prefix}-{environment} |
vdws-avd-dev, vdws-avd-prod |
| Subnet | snet-{prefix}-{environment} |
snet-avd-dev, snet-avd-prod |
| Virtual Network | vnet-{prefix}-{environment} |
vnet-avd-dev, vnet-avd-prod |
| Network Security Group | nsg-{prefix}-{environment} |
nsg-avd-dev, nsg-avd-prod |
This Terraform configuration deploys a complete Azure Virtual Desktop environment with dynamic configuration based on the selected deployment type:
graph TB
subgraph "Azure Subscription"
subgraph "Resource Group"
subgraph "Azure Virtual Desktop Service"
WS["Workspace<br/>vdws-{prefix}-{env}"]
WSAG["Workspace↔AppGroup<br/>Association"]
AG["Application Group<br/>vdag-{prefix}-{env}-{suffix}<br/>(Dynamic Type)"]
HP["Host Pool<br/>vdpool-{prefix}-{env}-{suffix}<br/>(Dynamic Config)"]
REG["Registration Info<br/>(Token for DSC join)"]
end
subgraph "Networking"
VNET["Virtual Network<br/>vnet-{prefix}-{env}"]
SUBNET["Subnet<br/>snet-{prefix}-{env}"]
NSG["Network Security Group<br/>nsg-{prefix}-{env}"]
NSGA["NSG↔Subnet Association"]
end
subgraph "Session Hosts"
VM1["Session Host VM 1<br/>vm-{prefix}-{env}-01<br/>(Windows 11 + M365)"]
VM2["Session Host VM 2<br/>vm-{prefix}-{env}-02<br/>(Optional)"]
VM3["Session Host VM N<br/>vm-{prefix}-{env}-0N<br/>(Scalable)"]
NIC1["NIC 1<br/>nic-{prefix}-{env}-01"]
NIC2["NIC 2<br/>nic-{prefix}-{env}-02"]
NIC3["NIC N<br/>nic-{prefix}-{env}-0N"]
EXT1["VM Extensions:<br/>- DSC (AVD Agent Registration)<br/>- AADLoginForWindows<br/>- GuestAttestation"]
EXT2["VM Extensions:<br/>- DSC (AVD Agent Registration)<br/>- AADLoginForWindows<br/>- GuestAttestation"]
EXT3["VM Extensions:<br/>- DSC (AVD Agent Registration)<br/>- AADLoginForWindows<br/>- GuestAttestation"]
end
subgraph "Monitoring & Cost (Optional)"
LAW["Log Analytics<br/>law-{prefix}-{env}"]
DIAGHP["Diagnostic Setting<br/>(Host Pool → LAW)"]
DIAGVM["Diagnostic Setting<br/>(Session Hosts → LAW)"]
DASH["Dashboard<br/>dashboard-{prefix}-{env}"]
ACGROUP["Action Group<br/>ag-cost-{prefix}-{env}"]
BUDGET["Cost Budget<br/>budget-{prefix}-{env}"]
AUTOSHUTDOWN["Auto-Shutdown Schedule<br/>(18:00 daily per VM)"]
end
subgraph "Scaling (Pooled Only)"
SP["Scaling Plan<br/>scaling-{prefix}-{env}"]
ASSOC["Scaling Plan ↔<br/>Host Pool Association"]
SPROLE["Role Assignment<br/>Desktop Virtualization<br/>Power On Off Contributor"]
end
end
subgraph "Azure Active Directory"
USERS["Users / Groups<br/>(security_principal_object_ids)"]
ROLES["RBAC Roles:<br/>- Desktop Virtualization User<br/>- Virtual Machine User Login"]
end
subgraph "AVD Client Access"
WEBCLIENT["Web Client<br/>(rdweb.wvd.microsoft.com)"]
NATIVECLIENT["Native Clients<br/>(Windows / Mac / iOS / Android)"]
end
end
%% AVD Service relationships
WS --> WSAG --> AG --> HP
HP --> REG
%% Networking
SUBNET --> VNET
NSGA --> SUBNET
NSGA --> NSG
%% Session Hosts
HP --> VM1 & VM2 & VM3
REG --> EXT1 & EXT2 & EXT3
VM1 --> NIC1 --> SUBNET
VM2 --> NIC2 --> SUBNET
VM3 --> NIC3 --> SUBNET
VM1 --> EXT1
VM2 --> EXT2
VM3 --> EXT3
%% Monitoring
HP --> DIAGHP --> LAW
VM1 & VM2 & VM3 --> DIAGVM --> LAW
LAW --> DASH
HP --> DASH
VM1 & VM2 & VM3 --> AUTOSHUTDOWN
BUDGET --> ACGROUP
%% Scaling (pooled only, optional)
SPROLE --> SP
SP --> ASSOC --> HP
%% RBAC
USERS --> ROLES
ROLES -.-> AG
ROLES -.-> VM1 & VM2 & VM3
%% Client access
USERS --> WEBCLIENT & NATIVECLIENT
WEBCLIENT & NATIVECLIENT --> WS
%% Styling
classDef avdService fill:#e1f5fe,stroke:#0288d1
classDef networking fill:#f3e5f5,stroke:#7b1fa2
classDef compute fill:#e8f5e8,stroke:#388e3c
classDef security fill:#fff3e0,stroke:#f57c00
classDef client fill:#fce4ec,stroke:#c62828
classDef monitoring fill:#fff8e1,stroke:#f9a825
classDef scaling fill:#e8eaf6,stroke:#3949ab
classDef assoc fill:#f5f5f5,stroke:#9e9e9e
class WS,AG,HP,REG,WSAG avdService
class VNET,SUBNET,NSG,NSGA networking
class VM1,VM2,VM3,NIC1,NIC2,NIC3,EXT1,EXT2,EXT3 compute
class USERS,ROLES security
class WEBCLIENT,NATIVECLIENT client
class LAW,DIAGHP,DIAGVM,DASH,ACGROUP,BUDGET,AUTOSHUTDOWN monitoring
class SP,ASSOC,SPROLE scaling
| Component | Purpose | Name Pattern | Conditional |
|---|---|---|---|
| Resource Group | Container for all AVD resources | rg-{prefix}-{environment} |
Always |
| Virtual Network | Isolated network for session hosts | vnet-{prefix}-{environment} |
Always |
| Subnet | Session host network segment | snet-{prefix}-{environment} |
Always |
| Network Security Group | Subnet traffic control | nsg-{prefix}-{environment} |
Always |
| Host Pool | Manages session host capacity and load balancing | vdpool-{prefix}-{environment}-{suffix} |
Always |
| Registration Info | Short-lived token used by DSC to join session hosts | (child of Host Pool) | Always |
| Application Group | Defines published desktops or RemoteApp applications | vdag-{prefix}-{environment}-{suffix} |
Always |
| Workspace | User-facing portal aggregating app groups | vdws-{prefix}-{environment} |
Always |
| Published Applications | Specific apps for RemoteApp deployments | (child of App Group) | RemoteApp types only |
| Session Host VMs | Windows 11 VMs running user sessions | vm-{prefix}-{environment}-{nn} |
Always |
| VM Extensions | DSC Agent Registration, AAD Login, Guest Attestation | (per VM) | Always |
| RBAC Assignments | Desktop Virtualization User + VM User Login roles | (role assignments) | Always |
| Log Analytics Workspace | Centralised monitoring and diagnostics | law-{prefix}-{environment} |
enable_monitoring = true |
| Diagnostic Settings | Routes Host Pool and VM metrics to Log Analytics | (per resource) | enable_monitoring = true |
| Scaling Plan | Automatic cost optimization for pooled deployments | scaling-{prefix}-{environment} |
enable_scaling_plans = true + pooled types |
| Scaling Plan ↔ Host Pool Association | Links scaling plan to host pool (separate resource for portal compatibility) | (association resource) | enable_scaling_plans = true + pooled types |
| Scaling Plan Role Assignment | "Desktop Virtualization Power On Off Contributor" at subscription scope | (UUID role assignment) | enable_scaling_plans = true + pooled types |
| Action Group | Email notification target for cost alerts | ag-cost-{prefix}-{environment} |
enable_cost_alerts = true |
| Cost Budget | Monthly spend tracking with 90%/100% alert thresholds | budget-{prefix}-{environment} |
enable_cost_alerts = true |
| Auto-Shutdown Schedule | Stops VMs at 18:00 daily to reduce off-hours costs | (per VM) | enable_cost_alerts = true |
| Dashboard | Operational insights in Azure Portal | dashboard-{prefix}-{environment} |
enable_dashboards = true |
✅ All names follow Microsoft Cloud Adoption Framework standards
The configuration automatically adjusts based on the deployment_type variable:
| Setting | pooled_desktop | personal_desktop | pooled_remoteapp | personal_remoteapp |
|---|---|---|---|---|
| Host Pool Type | Pooled | Personal | Pooled | Personal |
| App Group Type | Desktop | Desktop | RemoteApp | RemoteApp |
| Load Balancer | BreadthFirst/DepthFirst | N/A | BreadthFirst/DepthFirst | N/A |
| Max Sessions | User-defined | 1 (automatic) | User-defined | 1 (automatic) |
| Start VM on Connect | false | true | false | true |
| Requires Apps | No | No | Yes | Yes |
| Supports Scaling | Yes | No | Yes | No |
| Environment | Deployment Type | File | Use Case | Resulting Host Pool Name |
|---|---|---|---|---|
| Development | Pooled Desktop | dev-pooled-desktop.tfvars |
Testing, training, call centers | vdpool-avd-dev-desktop |
| Development | Personal Desktop | dev-personal-desktop.tfvars |
Developer workstations | vdpool-avd-dev-personal |
| Development | Pooled RemoteApp | dev-pooled-remoteapp.tfvars |
App testing, legacy apps | vdpool-avd-dev-apps |
| Production | Personal RemoteApp | prod-personal-remoteapp.tfvars |
Executive/compliance apps | vdpool-avd-prod-personalapps |
| Environment | Deployment Type | File | Features | Use Case |
|---|---|---|---|---|
| Production | Pooled RemoteApp + Monitoring | prod-pooled-remoteapp-with-monitoring.tfvars |
Monitoring, Scaling, Dashboards | Production apps with insights |
| Development | Pooled Desktop + Enhanced Scaling | dev-pooled-desktop-enhanced-scaling.tfvars |
Built-in role with subscription scope, Advanced schedules, User notifications, Auto-shutdown | Development with full cost optimization |
- Weekdays: Ramp-up 08:00, Peak 09:00-17:00, Ramp-down 17:00, Off-peak 18:00+
- Minimum hosts: 20% during ramp-up/peak, 20% during ramp-down
- Weekends: Ramp-up 09:00, Peak 10:00-16:00, Ramp-down 16:00, Off-peak 17:00+
- Minimum hosts: 10% during ramp-up/peak, 10% during ramp-down
- Aggressive scaling for maximum cost savings
- Weekdays: Ramp-up 07:00, Peak 08:00-18:00, Ramp-down 18:00, Off-peak 19:00+
- Minimum hosts: 30% during ramp-up/peak, 30% during ramp-down
- Weekends: Ramp-up 08:00, Peak 09:00-17:00, Ramp-down 17:00, Off-peak 18:00+
- Minimum hosts: 20% during ramp-up/peak, 20% during ramp-down
- Conservative scaling for reliability
Note: Schedules are fully customizable via scaling_plan_schedules variable. Enhanced scaling deployments may have different schedules.
- Host Pool Logs: Connection, error, management, and registration events
- Session Host Metrics: CPU, memory, disk, and performance data
- Custom Queries: Pre-built queries for common AVD scenarios
- Retention Options: 30, 60, 90, 120, 180, 365, or 730 days
- Session Metrics: Real-time session data
- Performance: Resource utilization
- Cost Analysis: Spending insights
- Health Status: System health overview
- Events & Alerts: Recent activity
The configuration is broken into logical files:
| File | Purpose |
|---|---|
providers.tf |
Specifies the required provider versions and configures AzureRM, AzureAD, Random, and AzAPI providers. Subscription ID is read from the ARM_SUBSCRIPTION_ID environment variable. |
variables.tf |
Declares all variables that can be customised for an environment. Most defaults come from the supplied ARM template. |
main.tf |
Contains resource definitions for the resource group, network, host pool, application group, workspace, role assignments, NICs, virtual machines, VM extensions, monitoring, scaling, auto-shutdown, and dashboards. |
outputs.tf |
Exposes key information about the deployment (resource group, host pool name, monitoring insights, etc.). |
templates/dashboard.tpl |
Custom Azure dashboard template for operational insights. |
set-auth.ps1 |
Authentication helper script — reads .env and sets ARM_* environment variables for the current PowerShell session. Run once before any Terraform commands. |
.env.example |
Template showing the required credential variables. Copy to .env and populate with real values. |
.env |
Your local credentials file (never committed — excluded by .gitignore). |
To deploy this configuration you typically create a variable file such as
dev.tfvars or prod.tfvars and pass it explicitly with -var-file. See the Using multiple environments section below for guidance.
Important — do not use the
.auto.tfvarsextension. Terraform automatically loads every*.auto.tfvarsfile in the directory simultaneously (alphabetically). Because this repository contains multiple environment configs, using.auto.tfvarscauses all of them to merge — with the last file loaded winning on any conflicting variable (e.g. a prod file withadmin_password = ""silently overriding a dev file's password). Always use plain.tfvarsand pass the file explicitly with-var-file.
- Terraform 1.2 or later and the required providers (all managed by
providers.tf): - An Azure subscription with the Virtual Desktop service enabled.
- An Azure Service Principal with Contributor access to the target subscription.
- A
.envcredentials file (copied from.env.example) with the Service Principal credentials — see the Authentication Setup section for full instructions. - Object IDs for users, groups or service principals that require access to the
published desktops. These IDs are supplied via the
security_principal_object_idsvariable.
- Clone the repository and navigate to the project directory
- Create your credentials file
Copy-Item .env.example .env # Edit .env and fill in your ARM_CLIENT_ID, ARM_CLIENT_SECRET, ARM_TENANT_ID, ARM_SUBSCRIPTION_ID
- Load credentials into your PowerShell session (repeat each time you open a new terminal)
.\set-auth.ps1 - Choose your deployment type by selecting the appropriate
.tfvarsfile or creating a custom one - Deploy using Terraform
For detailed authentication setup, Service Principal creation steps, and multi-machine guidance see deployment-guide.md — Authentication Setup.
The repository includes pre-configured examples for each deployment pattern:
.\set-auth.ps1
terraform init
terraform plan -var-file=dev-pooled-desktop.tfvars
terraform apply -var-file=dev-pooled-desktop.tfvars.\set-auth.ps1
terraform init
terraform plan -var-file=dev-personal-desktop.tfvars
terraform apply -var-file=dev-personal-desktop.tfvars.\set-auth.ps1
terraform init
terraform plan -var-file=dev-pooled-remoteapp.tfvars
terraform apply -var-file=dev-pooled-remoteapp.tfvars.\set-auth.ps1
terraform init
terraform plan -var-file=prod-personal-remoteapp.tfvars
terraform apply -var-file=prod-personal-remoteapp.tfvars.\set-auth.ps1
terraform init
terraform plan -var-file=dev-pooled-desktop-enhanced-scaling.tfvars
terraform apply -var-file=dev-pooled-desktop-enhanced-scaling.tfvarsCreate your own .tfvars file with the required settings:
# Custom deployment configuration
deployment_type = "pooled_desktop" # Choose deployment pattern
environment = "prod"
prefix = "company"
# Network configuration
vnet_address_space = ["10.0.0.0/24"]
subnet_address_prefix = "10.0.0.0/24"
# Deployment-specific settings
session_host_count = 5
max_session_limit = 6
load_balancer_type = "DepthFirst" # For pooled types only
# Monitoring and scaling (optional)
enable_monitoring = true
enable_scaling_plans = true
enable_cost_alerts = true
enable_dashboards = true
monitoring_retention_days = 30
cost_alert_threshold = 100
# Security
security_principal_object_ids = [
"user-or-group-object-id-1",
"user-or-group-object-id-2"
]
admin_password = "SecurePassword123!"
# For RemoteApp deployments, define published applications
published_applications = [
{
name = "excel"
display_name = "Microsoft Excel"
description = "Spreadsheet Application"
path = "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE"
command_line_arguments = ""
command_line_setting = "Allow"
show_in_portal = true
}
]Azure best practices recommend organising resources by environment and using a consistent naming strategy. To support this, the configuration exposes an environment variable that is appended to all resource names. Each environment should be deployed into its own resource group and (ideally) its own subscription to simplify governance and cost management.
To deploy multiple environments:
-
Create one variable file per environment. For example:
dev.tfvarsenvironment = "dev" location = "australiaeast" security_principal_object_ids = ["00000000-0000-0000-0000-000000000000"] admin_password = "P@ssword123!" enable_monitoring = true enable_scaling_plans = true tags = { owner = "dev-team" }
prod.tfvarsenvironment = "prod" location = "australiaeast" security_principal_object_ids = ["11111111-1111-1111-1111-111111111111"] admin_password = "AnotherSecureP@ssw0rd!" vm_size = "Standard_D8ds_v4" max_session_limit = 4 enable_monitoring = true enable_scaling_plans = true enable_cost_alerts = true tags = { owner = "prod-team" cost_center = "AVD01" }
-
Use Terraform workspaces or run Terraform in separate directories to maintain independent state files for each environment:
terraform init -upgrade terraform workspace new dev terraform workspace new prod terraform workspace select dev terraform apply -var-file=dev.tfvars terraform workspace select prod terraform apply -var-file=prod.tfvars
-
Review and adjust variable values such as VM sizes, number of hosts (
session_host_count), network ranges and tags to suit each environment's requirements.
The following recommendations are drawn from the Azure Cloud Adoption Framework and the AVD documentation:
-
Plan your capacity and region placement. The resource organisation guidance warns that deploying more than 5,000 VMs in a single region can create performance bottlenecks; organisations with large deployments should use multiple subscriptions and regions.
-
Keep AVD resources in a single region. Host pools, workspaces, session hosts and their network should live in the same Azure region to minimise latency. Avoid mixing session hosts from different regions within a host pool.
-
Separate service objects from compute. The Cloud Adoption Framework recommends placing AVD service objects (host pools, application groups, workspaces) in a dedicated resource group and the session host VMs in a separate resource group. This configuration simplifies lifecycle management and role assignments. In this example code, a single resource group is used for simplicity; consider splitting the configuration into modules if your organisation requires stricter separation.
-
Adopt a consistent naming and tagging strategy. Azure recommends defining naming conventions early; names should include business and operational details, such as workload, environment and region. Tags support cost management, automation and documentation.
-
Use role‑based access control (RBAC). Assign the
Desktop Virtualization Userrole on the application group and theVirtual Machine User Loginrole on the session host resource group to grant users access. The ARM‑based template uses the same roles; this Terraform configuration applies them using the object IDs supplied viasecurity_principal_object_ids. -
Rotate registration tokens regularly. The AVD host pool registration token expires after two hours in this configuration. If you wish to keep the token valid indefinitely, set
type = "Permanent"andexpiration_datefar in the future. Keep the token secret—Terraform stores it in state. -
Use remote state and apply locks. When deploying to shared environments (such as production), store your Terraform state in Azure Storage and enable state locking to avoid concurrent modifications. For example, configure the backend as:
terraform { backend "azurerm" { resource_group_name = "tfstate-rg" storage_account_name = "tfstateacct" container_name = "tfstate" key = "avd-${var.environment}.tfstate" } }
-
Enable monitoring for all environments. Use the monitoring features to gain operational insights and optimize costs.
-
Use scaling plans for pooled deployments. Enable automatic scaling for pooled desktop and RemoteApp deployments to reduce costs by 40-70%.
-
Set up cost alerts for production. Configure cost thresholds and alerts to prevent budget overruns.
The original ARM template created a single session host and nested deployments for the network, control plane and session hosts. This Terraform configuration:
- Uses native Terraform resources to create the virtual network, subnet and NSG. The NSG is intentionally empty to match the ARM template's empty
securityRulesarray. - Creates the host pool, application group and workspace with similar properties (pooled type, BreadthFirst load balancing and desktop preferred application group).
- Generates registration information directly on the host pool and exposes the token via
azurerm_virtual_desktop_host_pool_registration_info.avd.token. In the ARM template the token is retrieved vialistRegistrationTokens; in Terraform it is available as an attribute and used by the Microsoft DSC extension for reliable AVD agent installation and registration. - Leverages count to create multiple NICs, VMs and extensions when
session_host_count > 1. The ARM template deploys one VM; you can adjustsession_host_countto any number your subscription quota allows. - Uses Microsoft's official DSC configuration approach for session host registration, following the same proven method used in Microsoft's ARM templates and documentation.
- Adds comprehensive monitoring and scaling capabilities not present in the original ARM template.
This configuration uses Microsoft's official DSC (Desired State Configuration) approach for registering session hosts with the host pool, following Microsoft's documented best practices:
DSC-Based Approach (Current Implementation):
resource "azurerm_virtual_machine_extension" "avd_dsc" {
name = "Microsoft.Powershell.DSC"
publisher = "Microsoft.Powershell"
type = "DSC"
type_handler_version = "2.73"
settings = <<-SETTINGS
{
"modulesUrl": "${var.configuration_zip_file}",
"configurationFunction": "Configuration.ps1\\AddSessionHost",
"properties": {
"HostPoolName": "${azurerm_virtual_desktop_host_pool.avd.name}",
"aadJoin": true
}
}
SETTINGS
protected_settings = <<PROTECTED_SETTINGS
{
"properties": {
"registrationInfoToken": "${local.registration_token}"
}
}
PROTECTED_SETTINGS
}Benefits of DSC Approach:
- ✅ Microsoft-supported method - Official recommended approach
- ✅ Proven reliability - Used in Microsoft's own templates and documentation
- ✅ Secure token handling - Registration token passed via protected settings
- ✅ Automated configuration - Handles full AVD agent installation and registration
- ✅ Azure AD join support - Built-in support for domain-less environments
- ✅ Error resilience - Robust retry mechanisms for network issues
This configuration has been tested with Terraform 1.6 and version 4.38.1 of the Azurerm provider. It deploys successfully to an Azure subscription with permissions to create resource groups, networking, Azure Virtual Desktop resources and virtual machines. After deployment, you can verify the results by navigating to Azure Virtual Desktop → Host pools in the Azure portal; the session host should appear in the host pool's Session hosts blade.
To remove all resources created by this configuration, run:
.\set-auth.ps1
terraform destroy -var-file=dev-pooled-desktop.tfvarsYou will be prompted to confirm the destruction. Destroying the resources will remove the host pool, session hosts, network and resource group.
Important: If VMs are stopped (due to scaling plans or auto-shutdown), you may encounter extension deletion errors. To avoid this:
-
Start VMs before destroy (recommended):
.\set-auth.ps1 az vm start --resource-group rg-avd-dev --name vm-avd-dev-01 --no-wait az vm start --resource-group rg-avd-dev --name vm-avd-dev-02 --no-wait Start-Sleep -Seconds 60 # Wait for VMs to reach running state terraform destroy -var-file=dev-pooled-desktop.tfvars
-
Or delete the resource group directly (bypasses extension errors entirely):
az group delete --name rg-avd-dev --yes --no-wait
This automatically cleans up all resources including extensions.
See the deployment guide for detailed troubleshooting steps.
| Variable | Purpose | Default | Notes |
|---|---|---|---|
deployment_type |
AVD deployment pattern | "pooled_desktop" |
New: Determines naming suffix and configuration (pooled_desktop, personal_desktop, pooled_remoteapp, personal_remoteapp) |
environment |
Environment identifier (dev/test/prod) | "dev" |
Appended to all resource names following Microsoft standards |
prefix |
Project/organization prefix | "avd" |
Used in all resource names (vdpool-{prefix}-{environment}-{suffix}) |
session_host_count |
Number of session host VMs to deploy | 1 |
Scales the entire VM infrastructure |
security_principal_object_ids |
Azure AD object IDs for desktop access | [] |
Required: Must be populated before deployment |
admin_password |
Local admin password for session hosts | - | Required: Must meet Azure complexity requirements |
published_applications |
Applications for RemoteApp deployments | [] |
Required for RemoteApp: List of applications to publish |
load_balancer_type |
Load balancing algorithm for pooled types | "BreadthFirst" |
New: BreadthFirst or DepthFirst (ignored for personal types) |
personal_desktop_assignment_type |
Assignment for personal desktops | "Automatic" |
New: Automatic or Direct user assignment |
registration_token_expiration_hours |
Hours until registration token expires | 2 |
Longer for dev (8h), shorter for prod (1-2h) |
vm_size |
Azure VM size for session hosts | "Standard_D4ds_v4" |
Choose based on user workload requirements |
max_session_limit |
Max concurrent sessions per host | 2 |
Balance user experience vs. cost (automatically set to 1 for personal types) |
enable_monitoring |
Enable Log Analytics monitoring | false |
New: Enables comprehensive monitoring |
enable_scaling_plans |
Enable automatic scaling | false |
New: Cost optimization for pooled deployments |
enable_cost_alerts |
Enable cost monitoring alerts | false |
New: Budget tracking and notifications |
enable_dashboards |
Enable custom dashboards | false |
New: Operational insights dashboard |
monitoring_retention_days |
Log Analytics retention period | 30 |
New: Choose from 30, 60, 90, 120, 180, 365, 730 days |
cost_alert_threshold |
Daily cost threshold for alerts | 100 |
New: Set based on expected daily costs |
dashboard_refresh_interval |
Dashboard refresh interval (minutes) | 15 |
New: Choose from 5, 15, 30, 60 minutes |
The configuration now provides detailed naming information via the naming_convention output:
terraform output naming_conventionExample output:
{
"app_group_pattern" = "vdag-avd-dev-desktop"
"deployment_suffix" = "desktop"
"follows_standards" = "Microsoft Cloud Adoption Framework"
"host_pool_pattern" = "vdpool-avd-dev-desktop"
"subnet_pattern" = "snet-avd-dev"
"workspace_pattern" = "vdws-avd-dev"
}# Check monitoring configuration
terraform output monitoring_insights
# Get quick access links
terraform output quick_links
# Monitor specific resources
terraform output log_analytics_workspace_name
terraform output scaling_plan_name
terraform output dashboard_name
# Check scaling plan configuration details
terraform output scaling_plan_host_pool_association_id
terraform output scaling_plan_role_assignment_id| Output | Description | Example Value |
|---|---|---|
host_pool_name |
Microsoft-compliant host pool name | vdpool-avd-dev-desktop |
application_group_name |
Microsoft-compliant application group name | vdag-avd-dev-desktop |
workspace_name |
Microsoft-compliant workspace name | vdws-avd-dev |
deployment_config |
Complete configuration details for the deployment | Configuration object with all settings |
published_applications |
List of published apps (RemoteApp only) | Array of application details |
session_host_names |
Names of deployed session host VMs | ["vm-avd-dev-01", "vm-avd-dev-02"] |
scaling_plan_host_pool_association_id |
ID of the scaling plan-host pool association | Resource ID string |
scaling_plan_role_assignment_id |
ID of the scaling plan role assignment | Resource ID string |
For detailed deployment instructions, troubleshooting, and advanced configuration options, see:
- deployment-guide.md - Comprehensive deployment guide with examples
- DEPENDENCY_FLOW.md - Detailed dependency flow and resource creation order
This configuration follows Microsoft best practices and Azure Cloud Adoption Framework guidelines. When contributing:
- Maintain Microsoft-compliant naming for all resources
- Follow the existing code structure and patterns
- Add comprehensive documentation for new features
- Test with multiple deployment types before submitting
- Update example configurations for new features
🏆 Enterprise-Ready: This configuration provides production-ready AVD deployments with comprehensive monitoring, scaling, and cost optimization capabilities while maintaining Microsoft Cloud Adoption Framework compliance.