Azure Virtual Desktop – Terraform – Create a Scaling Plan for Pooled Host Pools (Part 4)

27 Oct

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.

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"

The intention here is to get you quickly started with Terraform on Azure Virtual Desktop Solution:

DescriptionLinks
Create an autoscale scaling plan for Azure Virtual DesktopCreate an autoscale scaling plan for Azure Virtual Desktop | Microsoft Learn
Setting up your computer to get started with Terrafor using PowershellInstall Terraform on Windows with Azure PowerShell
AVD Configure Azure Virtual Desktophttps://learn.microsoft.com/en-us/azure/developer/terraform/configure-azure-virtual-desktop
Terraform Learninghttps://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)”

Trackbacks/Pingbacks

  1. AVD Community Newsletter – 2nd November 2023 – AVD Community - November 3, 2023

    […] Aresh Sarkari has written a blog post on Creating the Scaling Plan for Azure Virtual Desktop using Terraform. You can read about here – Azure Virtual Desktop – Terraform – Create a Scaling Plan for Pooled Host Pools (Part 4) | AskAr… […]

Leave a Reply

Discover more from AskAresh

Subscribe now to keep reading and get access to the full archive.

Continue reading