Azure Virtual Desktop Accelerator for Terraform Guide for Active Directory Domain Services joined session host
This guide is designed to help you get started with deploying Azure Virtual Desktop using the provided Terraform template(s) within this repository. Before you deploy, it is recommended to review the template(s) to understand the resources that will be deployed and the associated costs.
This accelerator is to be used as starter kit and you can expand its functionality by developing your own deployments. This scenario deploys a new Azure Virtual Desktop workload, so it cannot be used to maintain, modify or add resources to an existing or already deployed Azure Virtual Desktop workload from this accelerator.
Note This terraform accelerator requires the Custom Image Build before deploying the Baseline. If you prefer to use the marketplace image with no customization see
- Prerequisites
- Planning
- AVD Spoke Network
- AVD Baseline
- Implementation
- Backend Setup
- Terraform file Structure
- Estimated Cost
This guide describes how to deploy Azure Virtual Desktop Accelerator using the Terraform. To get started with Terraform on Azure check out their tutorial.
- Meet the prerequisites listed here
- Current version of the Azure CLI
- Current version of the Terraform CLI
- An Azure Subscription(s) where you or an identity you manage has
Owner
RBAC permissions - Ensure Encryption at Host feature is already enabled on the subscription. To enable: az feature register --name EncryptionAtHost --namespace Microsoft.Compute. To validate: az feature show --name EncryptionAtHost --namespace Microsoft.Compute
The deployments will require a "Prefix" which will be included in all the deployed resources name.
Resource Groups and resource names are derived from the Prefix
parameter. Pick a unique resource prefix that is 3-5 alphanumeric characters in length without whitespaces.
Azure Virtual Desktop resources and dependent services for establishing the Azure Virtual Desktop spoke network:
- Network Security group
- New VNet and subnet
- Peering to the hub virtual network
- Baseline NSG
- Route table
The Azure Virtual Desktop Network Terraform files are all written as individual files each having a specific function. Variables have been created in all files for consistency, all changes to defaults are to be changed from the terraform.tfvars.sample file. The structure is as follows:
file Name | Description |
---|---|
data.tf | This file has data lookup |
dns_zones.tf | This file creates the private DNS zone and links |
output.tf | This will contains the outputs post deployment |
rg.tf | Creates the resource groups |
routetable.tf | Creates a routetable |
locals.tf | This file is for locals |
main.tf | This file contains the Terraform provider settings and version |
nsg.tf | Creates the network security group with required URLs |
variables.tf | Variables have been created in all files for various properties and names |
networking.tf | Creates the AVD spoke virtual network, subnet and peering to the hub network |
terraform.tfvars.sample | This file contains the values for the variables change per your requirements |
Validated on provider versions:
- hashicorp/azurerm v3.22.0
Azure Virtual Desktop resources and dependent services for establishing the baseline.
- Azure Virtual Desktop resources:
- 1 Host Pools – pooled
- 1 Desktop application group
- 1 Workspaces – 1 pooled
- Options to add personal and remote app host pools, workspaces, desktop application groups
- 2 Session host VMs domain join (options to use custom image or marketplace image)
- AVD Monitoring, log analytics workspace and diagnostic logs enabled
- AVD Scaling plan
- Associated Desktop Application Group for personal
- Associated Desktop Application Group and Remote Application Group for pooled
- Azure Files Storage with FSLogix share, RBAC role assignment and private endpoint
- Application Security group
- Key Vault and private endpoint
The Azure Virtual Desktop Baseline Terraform files are all written as individual files each having a specific function. Variables have been created in all files for consistency, all changes to defaults are to be changed from the terraform.tfvars.sample file. The structure is as follows:
file Name | Description |
---|---|
main.tf | This file deploys Azure Virtual Desktop |
data.tf | This file has data lookup |
locals.tf | This file is for locals |
host.tf | This file deploys session host using the custom image in the Azure Compute Gallery |
provider.tf | This file contains the Terraform provider settings and version |
afstorage.tf | This file creates the Storage account and Azure files shares with RBAC |
keyvault.tf | This file creates the Key Vault to be used |
appsecgrp.tf | This file creates the Application security group to be used |
avd.tf | This file creates the a Azure Virtual Desktop service objects |
rbac.tf | This will creates the rbac permissions |
output.tf | This will contains the outputs post deployment |
variables.tf | Variables have been created in all files for various properties and names |
rg.tf | Creates the resources group for the deployment |
terraform.tfvars.sample | This file contains the values for the variables change per your requirements |
Validated on provider versions:
- hashicorp/random v3.3.2
- hashicorp/azuread v2.26.1
- hashicorp/azurerm v3.22.0
- Clone your repo with the following git command:
git clone <https://github.com/Azure/avdaccelerator.git>
- Change your terminal into that new subdirectory:
cd avdaccelerator/workload/terraform/greenfield/ADDSscenario
az account list --output table
az account set --subscription 'Your AVD workload subscription ID'
- Rename
terraform.tfvars.sample
toterraform.tfvars
- Edit the
terraform.tfvars
configuration variables in the sectionModify the following variables to match your environment
to your preferences - Run terraform:
terraform init
terraform plan
terraform apply
The default templates write a state file directly to disk locally to where you are executing terraform from. If you wish to AzureRM backend please see AzureRM Backend. This deployment highlights using Azure Blog Storage to store state file and Key Vault
Click to expand
#### Using Azure CLIRESOURCE_GROUP_NAME=tstate
STORAGE_ACCOUNT_NAME=tstate$RANDOM
CONTAINER_NAME=tstate
az group create --name $RESOURCE_GROUP_NAME --location <eastus>
az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob
ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME --account-key $ACCOUNT_KEY
echo "storage_account_name: $STORAGE_ACCOUNT_NAME"
echo "container_name: $CONTAINER_NAME"
echo "access_key: $ACCOUNT_KEY"
az keyvault create --name "<Azure Virtual Desktopkeyvaultdemo>" --resource-group $RESOURCE_GROUP_NAME --location "<East US>"
az keyvault secret set --vault-name "<Azure Virtual Desktopkeyvaultdemo>" --name terraform-backend-key --value "<W.........................................>"
A breakdown of estimated cost for this deployment. Adjust to sku will change the estimates.
58 were free:
- 20 x azurerm_log_analytics_datasource_windows_performance_counter
- 9 x azurerm_log_analytics_datasource_windows_event
- 5 x azurerm_resource_group
- 4 x azurerm_role_assignment
- 2 x azurerm_network_interface
- 2 x azurerm_private_dns_zone_virtual_network_link
- 2 x azurerm_subnet
- 1 x azurerm_application_security_group
- 1 x azurerm_firewall_policy_rule_collection_group
- 1 x azurerm_key_vault
- 1 x azurerm_key_vault_access_policy
- 1 x azurerm_key_vault_secret
- 1 x azurerm_network_security_group
- 1 x azurerm_storage_account_network_rules
- 1 x azurerm_subnet_network_security_group_association
- 1 x azurerm_user_assigned_identity
- 1 x azurerm_virtual_desktop_application_group
- 1 x azurerm_virtual_desktop_host_pool
- 1 x azurerm_virtual_desktop_workspace
- 1 x azurerm_virtual_desktop_workspace_application_group_association
- 1 x azurerm_virtual_network
Generated by: Infracost
# Creates the Azure Virtual Desktop Spoke Network resources
module "network" {
source = "../../modules/network"
avdLocation = var.avdLocation
rg_network = var.rg_network
vnet = var.vnet
snet = var.snet
pesnet = var.pesnet
vnet_range = var.vnet_range
nsg = "${var.nsg}-${var.prefix}-${var.environment}-${var.avdLocation}"
prefix = var.prefix
rt = "${var.rt}-${var.prefix}-${var.environment}-${var.avdLocation}"
hub_connectivity_rg = var.hub_connectivity_rg
hub_vnet = var.hub_vnet
subnet_range = var.subnet_range
pesubnet_range = var.pesubnet_range
next_hop_ip = var.next_hop_ip
fw_policy = var.fw_policy
hub_subscription_id = var.hub_subscription_id
spoke_subscription_id = var.spoke_subscription_id
identity_subscription_id = var.identity_subscription_id
identity_rg = var.identity_rg
identity_vnet = var.identity_vnet
}
# Create Azure Log Analytics workspace for Azure Virtual Desktop
module "avm_res_operationalinsights_workspace" {
source = "Azure/avm-res-operationalinsights-workspace/azurerm"
version = "0.1.3"
enable_telemetry = var.enable_telemetry
resource_group_name = azurerm_resource_group.mon.name
location = var.avdLocation
name = lower(replace("log-avd-${var.environment}-${var.avdLocation}", "-", ""))
tags = local.tags
}
module "avm_res_desktopvirtualization_hostpool" {
source = "Azure/avm-res-desktopvirtualization-hostpool/azurerm"
version = "0.1.4"
virtual_desktop_host_pool_location = azurerm_resource_group.this.location
virtual_desktop_host_pool_name = "${var.hostpool}-${var.prefix}-${var.environment}-${var.avdLocation}"
virtual_desktop_host_pool_type = "Pooled" // "Personal" or "Pooled"
virtual_desktop_host_pool_resource_group_name = azurerm_resource_group.this.name
virtual_desktop_host_pool_load_balancer_type = "BreadthFirst" // "DepthFirst" or "BreadthFirst"
virtual_desktop_host_pool_custom_rdp_properties = "drivestoredirect:s:*;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:1;redirectprinters:i:1;devicestoredirect:s:*;redirectcomports:i:1;redirectsmartcards:i:1;usbdevicestoredirect:s:*;enablecredsspsupport:i:1;use multimon:i:0"
virtual_desktop_host_pool_maximum_sessions_allowed = 16
virtual_desktop_host_pool_start_vm_on_connect = true
resource_group_name = azurerm_resource_group.this.name
virtual_desktop_host_pool_scheduled_agent_updates = {
enabled = "true"
schedule = tolist([{
day_of_week = "Sunday"
hour_of_day = 0
}])
}
diagnostic_settings = {
to_law = {
name = "to-law"
workspace_resource_id = module.avm_res_operationalinsights_workspace.resource.id
}
}
}
resource "azurerm_virtual_desktop_host_pool_registration_info" "registrationinfo" {
expiration_date = timeadd(timestamp(), "48h")
hostpool_id = module.avm_res_desktopvirtualization_hostpool.resource.id
}
# Get an existing built-in role definition
data "azurerm_role_definition" "this" {
name = "Desktop Virtualization User"
}
# Get an existing Azure AD group that will be assigned to the application group
data "azuread_group" "existing" {
display_name = var.user_group_name
security_enabled = true
}
# Assign the Azure AD group to the application group
resource "azurerm_role_assignment" "this" {
principal_id = data.azuread_group.existing.object_id
scope = module.avm_res_desktopvirtualization_applicationgroup.resource.id
role_definition_id = data.azurerm_role_definition.this.id
skip_service_principal_aad_check = false
}
# Create Azure Virtual Desktop application group
module "avm_res_desktopvirtualization_applicationgroup" {
source = "Azure/avm-res-desktopvirtualization-applicationgroup/azurerm"
enable_telemetry = var.enable_telemetry
version = "0.1.2"
virtual_desktop_application_group_name = "${var.dag}-${var.prefix}-${var.environment}-${var.avdLocation}-01"
virtual_desktop_application_group_type = "Desktop"
virtual_desktop_application_group_host_pool_id = module.avm_res_desktopvirtualization_hostpool.resource.id
virtual_desktop_application_group_resource_group_name = azurerm_resource_group.this.name
virtual_desktop_application_group_location = azurerm_resource_group.this.location
user_group_name = var.user_group_name
virtual_desktop_application_group_tags = local.tags
}
# Create Azure Virtual Desktop workspace
module "avm_res_desktopvirtualization_workspace" {
source = "Azure/avm-res-desktopvirtualization-workspace/azurerm"
version = "0.1.2"
enable_telemetry = var.enable_telemetry
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
description = "${var.prefix} Workspace"
name = "${var.workspace}-${var.prefix}-${var.environment}-${var.avdLocation}-01"
tags = local.tags
diagnostic_settings = {
to_law = {
name = "to-law"
workspace_resource_id = module.avm_res_operationalinsights_workspace.resource.id
}
}
}
resource "azurerm_virtual_desktop_workspace_application_group_association" "workappgrassoc" {
application_group_id = module.avm_res_desktopvirtualization_applicationgroup.resource.id
workspace_id = module.avm_res_desktopvirtualization_workspace.resource.id
}
# Get the service principal for Azure Vitual Desktop
data "azuread_service_principal" "spn" {
client_id = "9cdead84-a844-4324-93f2-b2e6bb768d07"
}
resource "random_uuid" "example" {}
data "azurerm_role_definition" "power_role" {
name = "Desktop Virtualization Power On Off Contributor"
}
resource "azurerm_role_assignment" "new" {
principal_id = data.azuread_service_principal.spn.object_id
scope = data.azurerm_subscription.primary.id
role_definition_name = "Desktop Virtualization Power On Off Contributor"
}
# This ensures we have unique CAF compliant names for our resources.
module "naming" {
source = "Azure/naming/azurerm"
version = "0.3.0"
}
# This is the storage account for the diagnostic settings
resource "azurerm_storage_account" "this" {
account_replication_type = "ZRS"
account_tier = "Standard"
location = azurerm_resource_group.this.location
name = module.naming.storage_account.name_unique
resource_group_name = azurerm_resource_group.this.name
}
# Create Azure Virtual Desktop scaling plan
module "avm_res_desktopvirtualization_scaling_plan" {
source = "Azure/avm-res-desktopvirtualization-scalingplan/azurerm"
enable_telemetry = var.enable_telemetry
version = "0.1.2"
virtual_desktop_scaling_plan_name = "${var.scplan}-${var.prefix}-${var.environment}-${var.avdLocation}-01"
virtual_desktop_scaling_plan_location = azurerm_resource_group.this.location
virtual_desktop_scaling_plan_resource_group_name = azurerm_resource_group.this.name
virtual_desktop_scaling_plan_time_zone = "Eastern Standard Time"
virtual_desktop_scaling_plan_description = "${var.prefix} Scaling Plan"
virtual_desktop_scaling_plan_tags = local.tags
virtual_desktop_scaling_plan_host_pool = toset(
[
{
hostpool_id = module.avm_res_desktopvirtualization_hostpool.resource.id
scaling_plan_enabled = true
}
]
)
virtual_desktop_scaling_plan_schedule = toset(
[
{
name = "Weekday"
days_of_week = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
ramp_up_start_time = "09:00"
ramp_up_load_balancing_algorithm = "BreadthFirst"
ramp_up_minimum_hosts_percent = 50
ramp_up_capacity_threshold_percent = 80
peak_start_time = "10:00"
peak_load_balancing_algorithm = "DepthFirst"
ramp_down_start_time = "17:00"
ramp_down_load_balancing_algorithm = "BreadthFirst"
ramp_down_minimum_hosts_percent = 50
ramp_down_force_logoff_users = true
ramp_down_wait_time_minutes = 15
ramp_down_notification_message = "The session will end in 15 minutes."
ramp_down_capacity_threshold_percent = 50
ramp_down_stop_hosts_when = "ZeroActiveSessions"
off_peak_start_time = "18:00"
off_peak_load_balancing_algorithm = "BreadthFirst"
},
{
name = "Weekend"
days_of_week = ["Saturday", "Sunday"]
ramp_up_start_time = "09:00"
ramp_up_load_balancing_algorithm = "BreadthFirst"
ramp_up_minimum_hosts_percent = 50
ramp_up_capacity_threshold_percent = 80
peak_start_time = "10:00"
peak_load_balancing_algorithm = "DepthFirst"
ramp_down_start_time = "17:00"
ramp_down_load_balancing_algorithm = "BreadthFirst"
ramp_down_minimum_hosts_percent = 50
ramp_down_force_logoff_users = true
ramp_down_wait_time_minutes = 15
ramp_down_notification_message = "The session will end in 15 minutes."
ramp_down_capacity_threshold_percent = 50
ramp_down_stop_hosts_when = "ZeroActiveSessions"
off_peak_start_time = "18:00"
off_peak_load_balancing_algorithm = "BreadthFirst"
}
]
)
diagnostic_settings = {
to_law = {
name = "to-storage-account"
storage_account_resource_id = azurerm_storage_account.this.id
}
}
}
No requirements.
The following providers are used by this module:
The following resources are used by this module:
- azurerm_application_security_group.example (resource)
- azurerm_availability_set.aset (resource)
- azurerm_key_vault.kv (resource)
- azurerm_key_vault_key.stcmky (resource)
- azurerm_key_vault_key.stkek (resource)
- azurerm_key_vault_secret.localpassword (resource)
- azurerm_network_interface.avd_vm_nic (resource)
- azurerm_private_dns_zone_virtual_network_link.filelink (resource)
- azurerm_private_dns_zone_virtual_network_link.vaultlink (resource)
- azurerm_private_endpoint.afpe (resource)
- azurerm_private_endpoint.kvpe (resource)
- azurerm_resource_group.mon (resource)
- azurerm_resource_group.rg (resource)
- azurerm_resource_group.shrg (resource)
- azurerm_resource_group.this (resource)
- azurerm_role_assignment.af_role (resource)
- azurerm_role_assignment.keystor (resource)
- azurerm_role_assignment.new (resource)
- azurerm_role_assignment.this (resource)
- azurerm_storage_account.azfile (resource)
- azurerm_storage_account.this (resource)
- azurerm_storage_account_customer_managed_key.cmky (resource)
- azurerm_storage_account_network_rules.stfw (resource)
- azurerm_storage_share.FSShare (resource)
- azurerm_user_assigned_identity.mi (resource)
- azurerm_virtual_desktop_host_pool_registration_info.registrationinfo (resource)
- azurerm_virtual_desktop_workspace_application_group_association.workappgrassoc (resource)
- azurerm_virtual_machine_extension.ama (resource)
- azurerm_virtual_machine_extension.domain_join (resource)
- azurerm_virtual_machine_extension.mal (resource)
- azurerm_virtual_machine_extension.vmext_dsc (resource)
- azurerm_virtual_network_dns_servers.customdns (resource)
- azurerm_windows_virtual_machine.avd_vm (resource)
- random_password.vmpass (resource)
- random_string.random (resource)
- random_uuid.example (resource)
- time_rotating.avd_token (resource)
- time_sleep.wait (resource)
- azuread_group.existing (data source)
- azuread_service_principal.spn (data source)
- azurerm_client_config.cfg (data source)
- azurerm_client_config.current (data source)
- azurerm_private_dns_zone.pe-filedns-zone (data source)
- azurerm_private_dns_zone.pe-vaultdns-zone (data source)
- azurerm_role_definition.power_role (data source)
- azurerm_role_definition.storage_role (data source)
- azurerm_role_definition.this (data source)
- azurerm_subnet.pesubnet (data source)
- azurerm_subnet.subnet (data source)
- azurerm_subscription.primary (data source)
- azurerm_virtual_network.remote (data source)
- azurerm_virtual_network.vnet (data source)
The following input variables are required:
Description: List of allowed IP Addresses
Type: list(string)
Description: Location of the resource group.
Type: any
Description: Spoke Subscription id
Type: string
Description: Name of the Azure Virtual Desktop desktop application group
Type: string
Description: Custom DNS configuration
Type: list(string)
Description: Domain GUID
Type: string
Description: Name of the domain to join
Type: string
Description: Domain SID
Type: string
Description: Username for domain join (do not include domain name as this is appended)
Type: string
Description: Environment name sets the type of environment (Development (dev), Test (test), Production (prod)) that will be deployed, this information will be use as part of the resources naming.
Type: string
Description: Name of the firewall policy
Type: string
Description: Name of the Azure Virtual Desktop host pool
Type: string
Description: The resource group for hub connectivity resources
Type: string
Description: The resource group for the hub DNS zone
Type: any
Description: Hub Subscription id
Type: string
Description: Name of domain controller vnet
Type: string
Description: Name of the Resource group in which to identity resources are deployed
Type: string
Description: identity Subscription id
Type: string
Description: Name of the vnet in which to identity resources are deployed
Type: string
Description: local admin username
Type: string
Description: Netbios domain name
Type: string
Description: Next hop IP address
Type: string
Description: Name of the nsg
Type: string
Description: Offer of the image
Type: string
Description: Distinguished name of the organizational unit for the session host
Type: any
Description: Name of the Azure Virtual Desktop remote application group
Type: string
Description: Name of the Azure Virtual Desktop host pool
Type: string
Description: Name of subnet
Type: string
Description: Address range for private endpoints subnet
Type: list(string)
Description: Prefix of the name under 5 characters
Type: string
Description: Publisher of the image
Type: string
Description: Name of the Azure Virtual Desktop Personal workspace
Type: string
Description: Name of the Azure Virtual Desktop remote application group
Type: string
Description: Name of the Azure Virtual Desktop remote app group
Type: string
Description: Name of the Azure Virtual Desktop workspace
Type: string
Description: Number of AVD machines to deploy
Type: any
Description: Name of the Resource group in which to deploy avd service objects
Type: string
Description: Name of the Resource group in which to deploy network resources
Type: string
Description: Resource group AVD machines will be deployed to
Type: any
Description: Name of the Resource group in which to deploy shared resources
Type: string
Description: Name of the Resource group in which to deploy service objects
Type: string
Description: Name of the Resource group in which to deploy storage
Type: string
Description: Name of the route table
Type: string
Description: Name of the session host scaling plan
Type: string
Description: SKU of the image
Type: string
Description: Name of subnet
Type: string
Description: Spoke Subscription id
Type: string
Description: Address range for session host subnet
Type: list(string)
Description: Microsoft Entra ID Group for AVD users
Type: string
Description: Size of the machine to deploy
Type: any
Description: Name of avd vnet
Type: string
Description: Address range for deployment VNet
Type: list(string)
Description: Name of the Azure Virtual Desktop workspace
Type: string
The following input variables are optional (have default values):
Description: Password of the user to authenticate with the domain
Type: string
Default: "ChangeMe123$"
Description: This variable controls whether or not telemetry is enabled for the module.
For more information see https://aka.ms/avm/telemetry.
If it is set to false, then no telemetry will be collected.
Type: bool
Default: true
The following outputs are exported:
Description: Name of the Azure Virtual Desktop DAG
Description: Name of the Azure Virtual Desktop host pool
Description: Name of the Azure Virtual Desktop workspace
Description: This output is the full output for the resource to allow flexibility to reference all possible values for the resource. Example usage: module..resource.id
The following Modules are called:
Source: Azure/avm-res-desktopvirtualization-applicationgroup/azurerm
Version: 0.1.2
Source: Azure/avm-res-desktopvirtualization-hostpool/azurerm
Version: 0.1.4
Source: Azure/avm-res-desktopvirtualization-scalingplan/azurerm
Version: 0.1.2
Source: Azure/avm-res-desktopvirtualization-workspace/azurerm
Version: 0.1.2
Source: Azure/avm-res-operationalinsights-workspace/azurerm
Version: 0.1.3
Source: ../../modules/insights
Version:
Source: Azure/naming/azurerm
Version: 0.3.0
Source: ../../modules/network
Version:
Click to expand
Microsoft Support is not yet handling issues for any published tools in this repository. However, we would like to welcome you to open issues using GitHub issues to collaborate and improve these tools.
The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.