In today’s digital age, managing cloud resources efficiently is paramount, not just for operational efficacy but also for cost management. Enter Azure Virtual Desktop (AVD) Scaling Plans – Microsoft’s answer to dynamic and intelligent scaling of your virtual desktop infrastructure. No longer do organizations need to overprovision resources or let them sit idle; with AVD Scaling Plans, you get a responsive environment tailored to your usage patterns. In this blog post, we’ll create the scaling plans using Terraform.

In the previous blog post, we delved into the distinctions between the Personal Desktop (1:1 mapping), Pooled Desktop (1:Many mapping) and Remote App configurations, providing a comprehensive guide on their creation via Terraform. The series continues as we further explore how to create the AVD Scaling Plan for Pooled Host Pool.
- Azure Virtual Desktop – Terraform – Create a Host Pool, Desktop Application Group and Workspace for Personal Desktop (Part 1) | AskAresh
- Azure Virtual Desktop – Terraform – Create a Host Pool, Desktop Application Group and Workspace for Pooled Desktop (Part 2)
- Azure Virtual Desktop – Terraform – Create a Host Pool, Desktop Application Group and Workspace for Pooled Remote App aka Published Applications (Part 3) | AskAresh
Table of Content
Pre-requisites
Following are the pre-requisites before you begin
- An Azure subscription
- The Terraform CLI
- The Azure CLI
- Permissions within the Azure Subscription for using Terraform

Terraform – Authenticating via Service Principal & Client Secret
Before running any Terraform code, we will execute the following PowerShell (Run as administrator)and store the credentials as environment variables. If we do this via the environment variable, we don’t have to keep the below information within the providers.tf file. In a future blog post, there are better ways to store the below details, and I hope to showcase them:
# PowerShell
$env:ARM_CLIENT_ID = "9e453b62-0000-0000-0000-00000006e1ac"
$env:ARM_CLIENT_SECRET = "Z318Q~00000000000000000000000000000000_"
$env:ARM_TENANT_ID = "a02e602c-0000-000-0000-0e0000008bba61"
$env:ARM_SUBSCRIPTION_ID = "7b051460-00000-00000-00000-000000ecb1"
- Azure Subcription ID – Azure Portal Subcription copy the ID
- Client ID – From the above step you will have the details
- Client Secret – From the above step you will have the details
- Tenant ID – While creating the Enterprise Apps in ADD you will have the details
Terraform Folder Structure
The following is the folder structure for the terrraform code:

Azure Virtual Desktop Scaling Plan – Create a directory in which the below Terraform code will be published (providers.tf, main.tf, variables.tf and output.tf)
+---Config-AVD-ScalingPlans
| | main.tf
| | output.tf
| | providers.tf
| | variables.tf
Configure AVD – ScalingPlans – Providers.tf
Create a file named providers.tf and insert the following code:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.76.0"
}
azuread = {
source = "hashicorp/azuread"
}
}
}
provider "azurerm" {
features {}
}
Configure AVD – ScalingPlans – main.tf
Create a file named main.tf and insert the following code. Let me explain what all we are attempting to accomplish here:
- Leverage a existing Resource Group
- Leverage a existing Host Pool
- Create a custom role AVD AutoScale and assign to the Resource Group
- This is a prerequisite for ensuring the scaling plan can increase and decrease the resources in your resource group.
- Assign the role – AVD AutoScale to the service principal (AVD)
- Create a a scaling plan with a production grade schedule
- Associate the scaling plan with the host pool
# Generate a random UUID for role assignment
resource "random_uuid" "example" {}
# Fetch details of the existing Azure Resource Group
data "azurerm_resource_group" "example" {
name = var.resource_group_name
}
# Fetch details of the existing Azure Virtual Desktop Host Pool
data "azurerm_virtual_desktop_host_pool" "existing" {
name = var.existing_host_pool_name
resource_group_name = var.resource_group_name
}
# Define the Azure Role Definition for AVD AutoScale
resource "azurerm_role_definition" "example" {
name = "AVD-AutoScale"
scope = data.azurerm_resource_group.example.id
description = "AVD AutoScale Role"
# Define the permissions for this role
permissions {
actions = [
# List of required permissions.
"Microsoft.Insights/eventtypes/values/read",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Compute/virtualMachines/powerOff/action",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/read",
"Microsoft.DesktopVirtualization/hostpools/read",
"Microsoft.DesktopVirtualization/hostpools/write",
"Microsoft.DesktopVirtualization/hostpools/sessionhosts/read",
"Microsoft.DesktopVirtualization/hostpools/sessionhosts/write",
"Microsoft.DesktopVirtualization/hostpools/sessionhosts/usersessions/delete",
"Microsoft.DesktopVirtualization/hostpools/sessionhosts/usersessions/read",
"Microsoft.DesktopVirtualization/hostpools/sessionhosts/usersessions/sendMessage/action",
"Microsoft.DesktopVirtualization/hostpools/sessionhosts/usersessions/read"
]
not_actions = []
}
assignable_scopes = [
data.azurerm_resource_group.example.id,
]
}
# Fetch the Azure AD Service Principal for Windows Virtual Desktop
data "azuread_service_principal" "example" {
display_name = "Azure Virtual Desktop"
}
# Assign the role to the service principal
resource "azurerm_role_assignment" "example" {
name = random_uuid.example.result
scope = data.azurerm_resource_group.example.id
role_definition_id = azurerm_role_definition.example.role_definition_resource_id
principal_id = data.azuread_service_principal.example.id
skip_service_principal_aad_check = true
}
# Define the Azure Virtual Desktop Scaling Plan
resource "azurerm_virtual_desktop_scaling_plan" "example" {
name = var.scaling_plan_name
location = var.location
resource_group_name = var.resource_group_name
friendly_name = var.friendly_name
description = var.scaling_plan_description
time_zone = var.timezone
tags = var.tags
dynamic "schedule" {
for_each = var.schedules
content {
name = schedule.value.name
days_of_week = schedule.value.days_of_week
ramp_up_start_time = schedule.value.ramp_up_start_time
ramp_up_load_balancing_algorithm = schedule.value.ramp_up_load_balancing_algorithm
ramp_up_minimum_hosts_percent = schedule.value.ramp_up_minimum_hosts_percent
ramp_up_capacity_threshold_percent= schedule.value.ramp_up_capacity_threshold_pct
peak_start_time = schedule.value.peak_start_time
peak_load_balancing_algorithm = schedule.value.peak_load_balancing_algorithm
ramp_down_start_time = schedule.value.ramp_down_start_time
ramp_down_load_balancing_algorithm= schedule.value.ramp_down_load_balancing_algorithm
ramp_down_minimum_hosts_percent = schedule.value.ramp_down_minimum_hosts_percent
ramp_down_force_logoff_users = schedule.value.ramp_down_force_logoff_users
ramp_down_wait_time_minutes = schedule.value.ramp_down_wait_time_minutes
ramp_down_notification_message = schedule.value.ramp_down_notification_message
ramp_down_capacity_threshold_percent = schedule.value.ramp_down_capacity_threshold_pct
ramp_down_stop_hosts_when = schedule.value.ramp_down_stop_hosts_when
off_peak_start_time = schedule.value.off_peak_start_time
off_peak_load_balancing_algorithm = schedule.value.off_peak_load_balancing_algorithm
}
}
# Associate the scaling plan with the host pool
host_pool {
hostpool_id = data.azurerm_virtual_desktop_host_pool.existing.id
scaling_plan_enabled = true
}
}
Configure AVD – ScalingPlans – variables.tf
Create a file named variables.tf and insert the following code. The place where we define existing or new variables:
# Define the resource group of the Azure Virtual Desktop Scaling Plan
variable "resource_group_name" {
description = "The name of the resource group."
type = string
default = "AE-DEV-AVD-01-PO-D-RG"
}
# Define the attributes of the Azure Virtual Desktop Scaling Plan
variable "scaling_plan_name" {
description = "The name of the Scaling plan to be created."
type = string
default = "AVD-RA-HP-01-SP-01"
}
# Define the description of the scaling plan
variable "scaling_plan_description" {
description = "The description of the Scaling plan to be created."
type = string
default = "AVD Host Pool Scaling plan"
}
# Define the timezone of the Azure Virtual Desktop Scaling Plan
variable "timezone" {
description = "Scaling plan autoscaling triggers and Start/Stop actions will execute in the time zone selected."
type = string
default = "AUS Eastern Standard Time"
}
# Define the freindlyname of the Azure Virtual Desktop Scaling Plan
variable "friendly_name" {
description = "The friendly name of the Scaling plan to be created."
type = string
default = "AVD-RA-HP-SP-01"
}
# Define the host pool type(Pooled or Dedicated) of the Azure Virtual Desktop Scaling Plan
variable "host_pool_type" {
description = "The host pool type of the Scaling plan to be created."
type = string
default = "Pooled"
}
# Define the details of the scaling plan schedule
variable "schedules" {
description = "The schedules of the Scaling plan to be created."
type = list(object({
name = string
days_of_week = list(string)
ramp_up_start_time = string
ramp_up_load_balancing_algorithm = string
ramp_up_minimum_hosts_percent = number
ramp_up_capacity_threshold_pct = number
peak_start_time = string
peak_load_balancing_algorithm = string
ramp_down_start_time = string
ramp_down_load_balancing_algorithm = string
ramp_down_minimum_hosts_percent = number
ramp_down_capacity_threshold_pct = number
ramp_down_wait_time_minutes = number
ramp_down_stop_hosts_when = string
ramp_down_notification_message = string
off_peak_start_time = string
off_peak_load_balancing_algorithm = string
ramp_down_force_logoff_users = bool
}))
default = [
{
name = "weekdays_schedule"
days_of_week = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
ramp_up_start_time = "08:00"
ramp_up_load_balancing_algorithm = "BreadthFirst"
ramp_up_minimum_hosts_percent = 20
ramp_up_capacity_threshold_pct = 60
peak_start_time = "09:00"
peak_load_balancing_algorithm = "DepthFirst"
ramp_down_start_time = "18:00"
ramp_down_load_balancing_algorithm = "DepthFirst"
ramp_down_minimum_hosts_percent = 10
ramp_down_capacity_threshold_pct = 90
ramp_down_wait_time_minutes = 30
ramp_down_stop_hosts_when = "ZeroActiveSessions"
ramp_down_notification_message = "You will be logged off in 30 min. Make sure to save your work."
off_peak_start_time = "20:00"
off_peak_load_balancing_algorithm = "DepthFirst"
ramp_down_force_logoff_users = false
}
]
}
# Define the location of the Azure Virtual Desktop Scaling Plan
variable "location" {
description = "The location where the resources will be deployed."
type = string
default = "australiaeast"
}
# Define the tags of the Azure Virtual Desktop Scaling Plan
variable "tags" {
description = "The tags to be assigned to the Scaling plan."
type = map(string)
default = {
"Billing" = "IT"
"Department" = "IT"
"Location" = "AUS-East"
}
}
# Define the name of the Azure Virtual Desktop Host Pool
variable "existing_host_pool_name" {
description = "The name of the existing Azure Virtual Desktop Host Pool."
type = string
default = "AE-DEV-AVD-01-PO-D-HP"
}
Configure AVD – ScalingPlans – output.tf
Create a file named output.tf and insert the following code. This will showcase in the console what is getting deployed in form of a output.
# Output the ID of the Azure Virtual Desktop Scaling Plan
output "scaling_plan_id" {
description = "The ID of the Virtual Desktop Scaling Plan."
value = azurerm_virtual_desktop_scaling_plan.example.id
}
Intialize Terraform – AVD – ScalingPlans
Run terraform init to initialize the Terraform deployment. This command downloads the Azure provider required to manage your Azure resources. (Its pulling the AzureRM and AzureAD)
terraform init -upgrade

Create Terraform Execution Plan – AVD – ScalingPlans
Run terraform plan to create an execution plan.
terraform plan -out scaleplan.tfplan

Apply Terraform Execution Plan – AVD – ScalingPlans
Run terraform apply to apply the execution plan to your cloud infrastructure.
terraform apply "scaleplan.tfplan"


Validate the Output in Azure Portal
Go to the Azure portal, Select Azure Virtual Desktop and Select Scaling Plans and validate all the details such as Host Pool Assignment and Schedule:



Clean-up the above resources (Optional)
If you want to delete all the above resources then you can use the following commands to destroy. Run terraform plan and specify the destroy flag.
terraform plan -destroy -out scaleplan.destory.tfplan

terraform apply "scaleplan.destory.tfplan"

Quick Start Links
The intention here is to get you quickly started with Terraform on Azure Virtual Desktop Solution:
Description | Links |
Create an autoscale scaling plan for Azure Virtual Desktop | Create an autoscale scaling plan for Azure Virtual Desktop | Microsoft Learn |
Setting up your computer to get started with Terrafor using Powershell | Install Terraform on Windows with Azure PowerShell |
AVD Configure Azure Virtual Desktop | https://learn.microsoft.com/en-us/azure/developer/terraform/configure-azure-virtual-desktop |
Terraform Learning | https://youtube.com/playlist?list=PLLc2nQDXYMHowSZ4Lkq2jnZ0gsJL3ArAw |
I hope you will find this helpful information for getting started with Terraform to deploy the Azure Virtual Desktop – Scaling Plans. Please let me know if I have missed any steps or details, and I will be happy to update the post.
Thanks,
Aresh Sarkari
One Response to “Azure Virtual Desktop – Terraform – Create a Scaling Plan for Pooled Host Pools (Part 4)”