Implement scaling plans using Bicep for Azure Virtual Desktop.

Implement scaling plans using Bicep for Azure Virtual Desktop.

Hello! welcome back to my blog. Today I want to give you a little overview over the scaling options when using Azure Virtual Desktop. Scaling is a a important aspect on infrastructure, and Azure Virtual Desktop is no different. When you configure your scaling plan in a correct way, you will get an cost-effectiveness and optimal performance, and yes this will also work in fluctuating dynamic environments. Let's look in this posts what it can do for your organization, and of course how are we going to implement using the portal and in the final step using Bicep.

Is scaling important and what are the benefits?

Let's first start with answering the question, is scaling important? I can confirm with a quick yes! scaling is important with several reasons.

  • Cost Optimization: Cost is an important reason to use a scaling plan because many organizations don't need to use the environment during the weekend or at least not for a high demand, so you will definitaly save some money when shutting down the environment.
  • Sustainability Because you are shutting down machines and only use the machines your organization needs you reduce your environmental impact and therefore lower your Carbon Foodprint
  • Efficiency Because you have scaling in place you don't have to interact with the system to do manual interactions, the scaling plan does it for you.

It is good to mention, that in combination with a reservation on the virtual machine, it will not always be cheaper when using a scaling plan. The reason why is that a reservation will benefit more from a fully operating virtual machine, but if you calculate and monitor your environment there is a sweet spot and you can do both!


What are the options when using a Scaling plan?

There are two different scaling methods you can choose from when creating a scaling plan:

  • Power management autoscaling: powers on and off session hosts to adjust to the available capacity in a host pool. If you want to apply a scaling plan to a host pool with standard management, this is the option that you should use.

  • Dynamic autoscaling (preview) powers on and off session hosts and creates and deletes session hosts to adjust to the available capacity in a host pool. Dynamic autoscaling can only be used for pooled host pools with session host configuration.

There are some extra notes:

  • You can't use autoscale and scale session hosts using Azure Automation on the same host pool. You must use one or the other.
  • Power management autoscaling is available in Azure and Azure Government in the same regions you can create host pools in.
  • Dynamic autoscaling is only available in Azure and isn't supported in Azure Government.

Further explaination and examples can be found on the Microsoft learn site


What are the prerequisites when implementing the Power management autoscaling

You can find the specific information on the Microsoft site but i will highlight the important ones.

  • You need to deploy the Scaling Plan in the same region the hostpool is deployed.
  • The max default session limit needs to be configured and don't use the default values.
  • You will need to give RBAC role Desktop Virtualization Power On Off Contributor to the "Windows Virtual Desktop" user on the hostpool. I will advice to set this on the resource group.

What are the prerequisites when implementing the Dynamic autoscaling (preview)

You can find the specific information on the Microsoft site but i will highlight the important ones.

  • Dynamic autoscaling can only be used for pooled host pools with session host configuration, that means that you will need to set some presets more information can be found at this section.
  • Dynamic autoscaling currently requires access to the public Azure Storage endpoint wvdhpustgr0prod.blob.core.windows.net to deploy the RDAgent when creating session hosts.
  • You will need to give RBAC role Desktop Virtualization Power On Off Contributor to the "Windows Virtual Desktop" user on the hostpool. I will advice to set this rol on the resource group.

What are the options when creating a schedule?

When creating schedules for the Scaling Plan you have several options where you can choose from, lets give a little break down:

  • Ramp-Up (Breadth First): This will distribute users to all available session hosts, it will spin-up machines when the tresshold is met.

  • Ramp-Up (Depth First): This will distribute users to the session hosts with the highest number of connections but has not reached the maximum tresshold yet,it will spin-up machines when the tresshold is met.

  • Ramp-Down: This will sign-off and shutdown machines when criteria is met and there is no active session anymore on the running sessionhost.

  • Peak Hours: You can specify the time you will expect peak hours.

  • Off-Peak Hours: You can specify the time you will expect off-peak hours.

You can find some specific examples and scenarios on this Microsoft page .


Let's build an scaling plan an attach it to the hostpool

Keep in mind that when you want do deploy this. You first want to check your desired schedule, you can do this by monitor and check your environment during a longer period, to see which Scaling Plan will benefit and don't give you problems during the day.

First let us change the max session limit on the hostpool:

We continue with giving the Windows Virtual Desktop APP the correct rol assignment.

Go in the Azure portal to Scaling plams and create a new scaling plan, when you are filling in the information you need to specify the same location and resource group in which your hostpool resides.

Go to schedules and click on add schedule, in this example I will pick only the working days:

Choose the start time of the working day and set your tresshold, in this example I will choose for Breadth-first:

Decide what your peak hour will be:

Choose the hour of the ramp-down, you an of course decide to force users to log-off.

And as a last step you can fill in the exact time the company is at rest and you don't expect anyone to log in.

When adding the schedule you can choose on which hostpool you want to attach the scaling plan and enable it of course.

Create the scaling plan and check you hostpool if the schedule is attached.

Now you can build a session hosts and check if the schedule works, for a testing purpose you can ofcourse set the max limit on 1 so you will know for sure that it will use the scaling plan.


Let's build the same scaling plan using Bicep

Of course we can use Bicep as well to implement this lets use the Bicep Azure Verified Module for that. The same prerequisites are required when deploying this with IaC, so you need to set the roles and alter your maxsessionlimits I have created a little bit of code you can find in my repo, but i will break it down step by step.

First we have the deployment script we are using to deploy the Scaling Plan the script is also located in the repo.

  • When you are using the deployment script keep in mind that you need to modify the location where you have your bicep file.
  • Because we are not going to use a biccepparam file you don't see that location in the command, if you want to use it please and add the bicepparam in the az deployment command line, here is an example:

--parameters ./scalingplan.bicepparam

An example of the deployment script:

 1
 2param(
 3    [Parameter(Mandatory = $true)][string] $subscriptionID = "",
 4    [Parameter(Mandatory = $true)][ValidateSet("northeurope", "westeurope")][string] $location = "", 
 5    [ValidateSet("avd")][string][Parameter(Mandatory = $true, ParameterSetName = 'Default')] $productType = "",
 6    [Parameter(Mandatory = $true, Position = 3)] [validateSet("prod", "acc", "dev", "test")] [string] $environmentType = "",
 7    [switch] $deploy
 8)
 9
10# Ensure parameters are captured
11Write-Host "Subscription ID: $subscriptionID"
12Write-Host "Location: $location"
13Write-Host "Product Type: $productType"
14Write-Host "Environment Type: $environmentType"
15
16$deploymentID = (New-Guid).Guid
17
18<# Set Variables #>
19az account set --subscription $subscriptionID --output none
20if (!$?) {
21    Write-Host "Something went wrong while setting the correct subscription. Please check and try again." -ForegroundColor Red
22}
23
24
25$updatedBy = (az account show | ConvertFrom-Json).user.name 
26$location = $location.ToLower() -replace " ", ""
27
28$LocationShortCodeMap = @{
29    "westeurope"  = "weu";
30    "northeurope" = "neu";
31}
32
33$locationShortCode = $LocationShortCodeMap.$location
34
35if ($deploy) {
36    Write-Host "Running a Bicep deployment with ID: '$deploymentID' for Environment: '$environmentType' with a 'WhatIf' check." -ForegroundColor Green
37    az deployment sub create `
38    --name $deploymentID `
39    --location $location `
40    --template-file ./scalingplan.bicep `
41    --parameters updatedBy=$updatedBy location=$location locationShortCode=$LocationShortCode productType=$productType environmentType=$environmentType `
42    --confirm-with-what-if `
43}

Listed below is the bicep file that we will use to create the Scaling Plan, we will call the existing hostpool that is already in your environment, the file is also located in my repo. Some extra things that you need to think about in this bicep file.

  • you need to add your existing subscription id that can be filled in, in the existingSubscriptionId value
  • you need to add your existing resource group name that can be filled in, in the existingResourcegroupName value
  • you need to add your existing hostpool name that can be filled in, in the existingHostpoolName value
  1
  2targetScope = 'subscription'
  3
  4param updatedBy string 
  5
  6@allowed([
  7  'test'
  8  'dev'
  9  'prod'
 10  'acc'
 11  
 12])
 13param environmentType string 
 14
 15@description('Unique identifier for the deployment')
 16param deploymentGuid string = newGuid()
 17
 18@description('Product Type: example avd.')
 19@allowed([
 20  'avd'
 21  ])
 22param productType string 
 23
 24@description('Azure Region to deploy the resources in.')
 25@allowed([
 26  'westeurope'
 27  'northeurope'
 28  
 29])
 30param location string
 31
 32@description('Location shortcode. Used for end of resource names.')
 33param locationShortCode string 
 34
 35@description('Add tags as required as Name:Value')
 36param tags object = {
 37  Environment: environmentType
 38  LastUpdatedOn: utcNow('d')
 39  LastDeployedBy: updatedBy
 40}
 41
 42param existingSubscriptionId string = ''
 43
 44param existingResourcegroupName string = 'rg-${productType}-${environmentType}-${locationShortCode}'
 45param existingHostpoolName string = 'vdpool-${productType}-${environmentType}-${locationShortCode}'
 46param scalingPlanName string = 'scalingplan-${productType}-${environmentType}-${locationShortCode}'
 47
 48@description('Reference to the existing VNet')
 49resource existingHostpool 'Microsoft.DesktopVirtualization/hostPools@2024-08-08-preview' existing= {
 50  name: existingHostpoolName
 51  scope: resourceGroup(existingSubscriptionId, existingResourcegroupName)
 52}
 53
 54module createScalingPlan 'br/public:avm/res/desktop-virtualization/scaling-plan:0.3.0' = {
 55  name: 'createScalingPlan-${deploymentGuid}'
 56  scope: resourceGroup(existingResourcegroupName)
 57  params: {
 58    name: scalingPlanName
 59    hostPoolType: 'Pooled'
 60    timeZone: 'W. Europe Standard Time'
 61    schedules: [
 62  {
 63    rampUpStartTime:  {
 64      hour: 8
 65      minute: 0
 66    }
 67    peakStartTime: {
 68      hour: 9
 69      minute: 0
 70    }
 71    rampDownStartTime: {
 72      hour: 18
 73      minute: 0
 74    }
 75    offPeakStartTime: {
 76      hour: 20
 77      minute: 0
 78    }
 79    name: 'weekdays_schedule'
 80    daysOfWeek: [
 81      'Monday'
 82      'Tuesday'
 83      'Wednesday'
 84      'Thursday'
 85      'Friday'
 86    ]
 87    rampUpLoadBalancingAlgorithm: 'BreadthFirst'
 88    rampUpMinimumHostsPct: 20
 89    rampUpCapacityThresholdPct: 60
 90    peakLoadBalancingAlgorithm: 'BreadthFirst'
 91    rampDownLoadBalancingAlgorithm: 'BreadthFirst'
 92    rampDownMinimumHostsPct: 10
 93    rampDownCapacityThresholdPct: 90
 94    rampDownForceLogoffUsers: true
 95    rampDownWaitTimeMinutes: 30
 96    rampDownNotificationMessage: 'You will be logged off in 30 min. Make sure to save your work.'
 97    rampDownStopHostsWhen: 'ZeroSessions'
 98    offPeakLoadBalancingAlgorithm: 'BreadthFirst'
 99  }
100]
101    hostPoolReferences: [
102      {
103        hostPoolArmPath: existingHostpool.id
104        scalingPlanEnabled: true
105      }
106    ]
107tags: tags
108
109
110  }
111}

And as a final step we are calling the deployment script using the following command:

  • First login to the environment using

az login

  • Continue with the the command listed below.

.\deploy-now-scalingplan.ps1 -subscriptionID "" -location "westeurope" -productType "avd" -environmentType "acc" -deploy

  • Keep in mind that you need to parse in your own subscriptionID because that is needed.
  • Because there is a what-if statement in the script you will first see what is being created in green.
  • After the what-if you can confirm the deployment.

After the deployment you will see the Scaling Plan attached.

As you can see it's relative easy to implement this resources with Bicep using the Azure Verified Modules.


Conclusion

Scaling can be an essential component for maximizing the efficiency of Azure Virtual Desktop. It can help you reduce costs especially combined with an Azure reservation, but some monitoring and calculation needs to be done beforehand to hit the sweet spot. You certainly lower your Carbon Foodprint because you only start-up machines that are needed, so you don't waste unnessary power.

I have showed you in this article also two different methods to deploy a Scaling Plan, trough the portal and with Bicep, i hope you have learned something and see you all next time!

Happy scaling!