Deploying Azure Virtual Desktop Session Host using Bicep only

Deploying Azure Virtual Desktop Session Host using Bicep.

Welcome back to my blog!

It’s been a while since I last wrote a blog. My previous one was about Azure Kubernetes Service. In the meantime, I’ve been working on a few Azure Virtual Desktop deployments and found out that we can actually deploy session hosts entirely using Bicep. This also means that if the environment doesn’t need any extra configuration and customization in the sessionhost, the whole process can pretty much be handled with Bicep alone. That’s why I wanted to share this with you and of course a big shoutout to my friend and colleague Simon Lee from BuildWithCaffeine for pointing me in the right direction.

Of course, you can also combine this with an Azure Pipeline, just like I’ve covered in my previous blogs. But keeping everything in Bicep without relying on custom scripts makes the whole process a lot cleaner and easier to manage.


How are we gonna start?

We will kick things off with an environment that’s already up and running in my Azure subscription. This setup was deployed using the same Bicep templates I shared in my earlier blogs. From there, I’ll walk you through how we can extend that setup and get session hosts fully deployed with just Bicep, no extra scripts needed.

You might be thinking: “No extra scripts at all?” Fair enough we do still need a single PowerShell deployment script to run the Bicep.

We are using the Azure Verified Modules, built by the community and following Bicep best practices. No need for any custom Bicep modules here.

So, we are working with three files here: a deployment script that calls the Bicep file and the Bicep parameter file. Of course, you’re free to tweak these to fit your own environment.

One thing worth mentioning I am using passwords directly in the parameter file for this demo. Ideally, for security reasons, you’d want to use Key Vault instead because that's best practice. But to keep things simple, I’m sticking with a hardcoded password here.

In my next blog, I’ll show you how to do this properly with Key Vault.


The Bicep file

Since this Bicep file combines calling an existing environment with creating a new session host, let me walk you through how that works in practice.

We are using the Bicep Azure Verified Modules, but when it comes to referencing existing Bicep resources, you’ll need to use a different approach like in the example below:

In this example, you can see that I’m calling an existing resource group using the resource module and the existing parameter. I’m doing this in the Bicep file for the environment I already have set up.

1@description('Reference to the existing existing hostpool')
2resource existingResourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
3  name: existingResourceGroupName
4  scope: subscription(existingSubscriptionId)
5}

Let’s take a look at the full Bicep file. This file handles calling multiple existing resources, so you’ll need to fill in the existing parameters in your Bicep parameter file (we will get to that in a bit). After it references all the existing resources such as Subscription, Resource Group, Virtual Network, Subnet, Hostpool, Shared Image Gallery, and the existing versions of the images, it will create a VM based on the existing Image Version in the Shared Image Gallery.

  1targetScope = 'subscription'
  2
  3param updatedBy string
  4
  5@allowed([
  6  'test'
  7  'dev'
  8  'prod'
  9  'acc'
 10])
 11param environmentType string 
 12
 13param subscriptionId 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  'ukwest'
 29  'uksouth'
 30  'francecentral'
 31  'francesouth'
 32  'switzerlandnorth'
 33  'switzerlandwest'
 34  'germanywestcentral'
 35  'germanynorth'
 36  'centralus'
 37  'eastus'
 38  'swedencentral'
 39  'swedensouth'
 40])
 41param location string 
 42
 43@description('Location shortcode. Used for end of resource names.')
 44param locationShortCode string 
 45
 46@description('Add tags as required as Name:Value')
 47param tags object = {
 48  Environment: environmentType
 49  LastUpdatedOn: utcNow('d')
 50  LastDeployedBy: updatedBy
 51}
 52
 53param existingSubscriptionId string 
 54param existingResourceGroupName string 
 55param existingHostpoolName string 
 56param existingVnetName string
 57param existingSubnetName string 
 58param existingGalleryName string
 59param existingVersionName string 
 60param existingVersionNumber string
 61
 62@minValue(1)
 63@description('Number of session hosts to deploy')
 64param sessionHostCount int
 65
 66@description('Number of session hosts to deploy')
 67param sessionHostname string 
 68
 69@description('local admin username for the session host VMs')
 70param sessionHostUsername string 
 71
 72@secure()
 73@description('local admin password for the session host VMs')
 74param sessionHostPassword string
 75
 76@description('OS type for the session host VM')
 77param osType string
 78
 79@description('vm size for the session host VM')
 80param vmSize string
 81
 82@description('license type for the session host VM')
 83param licenseType string
 84
 85@description('disk size in GB for the session host VM')
 86param diskSizeGB int
 87
 88@description('disk sku for the session host VM')
 89param diskStorageAccountType string
 90
 91@description('Username for the domain join user account')
 92param domainJoinUsername string
 93
 94@secure()
 95@description('Password for the domain join user account')
 96param domainJoinPassword string 
 97
 98@description('Domain name for the domain join user account')
 99param domainName string 
100
101@description('Reference to the existing existing hostpool')
102resource existingResourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
103  name: existingResourceGroupName
104  scope: subscription(existingSubscriptionId)
105}
106
107@description('Reference to the existing existing hostpool')
108resource existingResourceHostpool 'Microsoft.DesktopVirtualization/hostPools@2024-08-08-preview' existing = {
109  name: existingHostpoolName
110  scope: resourceGroup(existingSubscriptionId, existingResourceGroupName)
111}
112
113@description('Reference to the existing existing hostppol')
114resource existingResourceVnet 'Microsoft.Network/virtualNetworks@2024-07-01' existing = {
115  name: existingVnetName
116  scope: resourceGroup(existingSubscriptionId, existingResourceGroupName)
117}
118
119@description('Reference to the existing existing hostppol')
120resource existingResourceSubnet 'Microsoft.Network/virtualNetworks/subnets@2024-07-01' existing = {
121  name: existingSubnetName
122  parent: existingResourceVnet
123}
124
125// Loop to create multiple session hosts
126module createSessionHost 'br/public:avm/res/compute/virtual-machine:0.18.0' = [
127  for i in range(0, sessionHostCount): {
128    scope: resourceGroup(existingSubscriptionId, existingResourceGroupName)
129    name: 'avd-${i}'
130    params: {
131      name: '${sessionHostname}-${i + 1}'
132      location: location
133      osType: osType
134      vmSize: vmSize
135      availabilityZone: 1
136      bootDiagnostics: true
137      secureBootEnabled: true
138      licenseType: licenseType
139      enableAutomaticUpdates: true
140      encryptionAtHost: true
141      securityType: 'TrustedLaunch'
142      adminUsername: sessionHostUsername
143      adminPassword: sessionHostPassword
144      managedIdentities: {
145        systemAssigned: true
146      }
147      imageReference: {
148        id: '/subscriptions/${existingSubscriptionId}/resourceGroups/${existingResourceGroupName}/providers/Microsoft.Compute/galleries/${existingGalleryName}/images/${existingVersionName}/versions/${existingVersionNumber}'
149      }
150      osDisk: {
151        caching: 'ReadWrite'
152        diskSizeGB: diskSizeGB
153        managedDisk: {
154          storageAccountType: diskStorageAccountType
155        }
156      }
157      nicConfigurations: [
158        {
159          name: 'nic-avd-${i + 1}'
160          enableAcceleratedNetworking: true
161          ipConfigurations: [
162            {
163              name: 'ipconfig-${deploymentGuid}-${i + 1}'
164              subnetResourceId: existingResourceSubnet.id
165            }
166          ]
167        }
168      ]
169      
170      extensionDomainJoinConfig: {
171        enabled: true
172        settings: {
173          name: domainName
174          user: domainJoinUsername
175
176          restart: true
177          options: 3
178        }
179      }
180      extensionDomainJoinPassword: domainJoinPassword 
181
182      extensionHostPoolRegistration: {
183        enabled: true
184        modulesUrl: 'https://raw.githubusercontent.com/Azure/RDS-Templates/master/ARM-wvd-templates/DSC/Configuration.zip'
185        configurationFunction: 'Configuration.ps1\\AddSessionHost'
186        hostPoolName: existingResourceHostpool.name
187        registrationInfoToken: existingResourceHostpool.listRegistrationTokens().value[0].token
188      }
189      tags: tags
190    }
191    dependsOn: [existingResourceGroup]
192  }
193]

For this, it uses the public module available from the Azure Verified Modules. This will deploy individual Virtual Machines that’s looped through: want one VM? You get one. Want ten? You get ten.

In the module, I use two extensions. First, EnableHostPoolRegistration, which needs the moduleURL where the AVD templates live and the PowerShell script that handles the configuration behind the scenes. Both are available in the same location and can be copied as-is. I also specify that it should pull an existing token from the hostpool using:

existingResourceHostpool.listRegistrationTokens().value[0].token The session host can’t be added when we don't use a registration token.

It’s also worth mentioning that I use an extension to domain-join the VM. To make this work, of course, you’ll need a domain controller or ADDS in place.


The Bicep param file

The Bicep parameter file basically holds the values we want to use for the Bicep deployment. Some values I like to keep fixed in the Bicep file itself because they don’t need to be changed, such as enableSecureBoot or Bootdiagnostics, which should always be set to true when building it WAF alligned.

But there are values you’ll want to tweak, like the SKU size or the disk SKU. another example, if you’re deploying a Windows 2022 edition on AVD, you’ll need to adjust the license type to Windows_Server.

Of course, you’re free to change these values yourself, and if you want to include more settings in your Bicep parameter file, you can absolutely do that.

 1using 'deploy-sessionhost.bicep'
 2
 3//parameters for the deployment.
 4param updatedBy = ''
 5param subscriptionId = ''
 6param environmentType = 'test' 
 7param location = 'westeurope' 
 8param locationShortCode = 'weu' 
 9param productType = 'avd'
10
11//Paramameters for the existing AVD environtment. 
12param existingSubscriptionId = ''
13param existingResourceGroupName = 'rg-avd-prod-weu'
14param existingHostpoolName = 'vdpool-avd-prod-weu'
15param existingVnetName = 'vnet-avd-prod-weu'
16param existingSubnetName = 'snet-avd'
17param existingGalleryName = 'galavdprodweu'
18param existingVersionName  = 'img-avd-prod-weu'
19param existingVersionNumber = '2025.07.03'
20
21//Parameters for the local user account session host VM's
22param sessionHostname = 'pof-avd'
23param sessionHostUsername = 'yourusername'
24param sessionHostPassword = 'yourpassword'
25
26//Parameters for the session host VM's
27param osType = 'Windows'
28param vmSize = 'Standard_DS2_v2'
29param licenseType = 'Windows_Client'
30param diskSizeGB = 128
31param diskStorageAccountType = 'Premium_LRS'
32
33///Number of session hosts to deploy
34param sessionHostCount = 2
35
36//Parameters for the domain join user account
37param domainJoinPassword = ''
38param domainJoinUsername = 'yourdomain\\youraccount'
39param domainName = 'your.domain'

The version is set by using the following line in the Bicep file: So you only have to fill the existingVersionNumber

id: '/subscriptions/${existingSubscriptionId}/resourceGroups/${existingResourceGroupName}/providers/Microsoft.Compute/galleries/${existingGalleryName}/images/${existingVersionName}/versions/${existingVersionNumber}'


The Deployment script

The deployment script is basically the same one we always use for deploying Bicep files, with just a few small tweaks. First, it checks and updates all versions that are uses like Azure CLI, Bicep, and Desktop Virtualization. We also set the subscription and check if the account that runs the script has the right access, then set the necessary variables.

Next, the script makes sure all required files are present. After that, it creates a registration token using the az desktopvirtualization module, this token is valid for up to 2 hours so that the Bicep deployment can run smoothly and it is able to add sessionhosts.

And of course it is running with a What-if statement so you can control your deployment.

  1param(
  2    [Parameter(Mandatory = $true)][string] $subscriptionID = "",
  3    [Parameter(Mandatory = $true)][ValidateSet("northeurope", "westeurope")][string] $location = "", 
  4    [ValidateSet("avd")][string][Parameter(Mandatory = $true, ParameterSetName = 'Default')] $productType = "",
  5    [Parameter(Mandatory = $true, Position = 3)] [validateSet("prod", "acc", "dev", "test")] [string] $environmentType = "",
  6    [switch] $deploy,
  7    [switch] $updateBicep
  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<# Validate Prerequisites #>
 19Write-Host "Validating prerequisites..." -ForegroundColor Cyan
 20
 21# Validate Azure CLI is available
 22if (-not (Get-Command az -ErrorAction SilentlyContinue)) {
 23    Write-Host "Azure CLI is not installed or not in PATH. Please install Azure CLI first." -ForegroundColor Red
 24    Write-Host "Download from: https://aka.ms/installazurecliwindows" -ForegroundColor Yellow
 25    exit 1
 26}
 27
 28# Check Azure CLI version
 29try {
 30    $azVersion = az version --query '"azure-cli"' --output tsv 2>$null
 31    if ($azVersion) {
 32        Write-Host "Azure CLI version: $azVersion" -ForegroundColor Green
 33    }
 34} catch {
 35    Write-Host "Warning: Unable to determine Azure CLI version" -ForegroundColor Yellow
 36}
 37
 38<# Check and Update Bicep CLI #>
 39Write-Host "Checking Bicep CLI..." -ForegroundColor Cyan
 40
 41# Check if Bicep is installed
 42$bicepInstalled = $false
 43try {
 44    $bicepVersion = az bicep version --output tsv 2>$null
 45    if ($bicepVersion) {
 46        Write-Host "Current Bicep CLI version: $bicepVersion" -ForegroundColor Green
 47        $bicepInstalled = $true
 48    }
 49} catch {
 50    Write-Host "Bicep CLI not found or not accessible." -ForegroundColor Yellow
 51}
 52
 53# Install or update Bicep
 54if (-not $bicepInstalled) {
 55    Write-Host "Installing Bicep CLI..." -ForegroundColor Yellow
 56    try {
 57        az bicep install
 58        if ($LASTEXITCODE -eq 0) {
 59            Write-Host "Bicep CLI installed successfully." -ForegroundColor Green
 60            $bicepVersion = az bicep version --output tsv 2>$null
 61            Write-Host "Installed Bicep CLI version: $bicepVersion" -ForegroundColor Green
 62        } else {
 63            Write-Host "Failed to install Bicep CLI." -ForegroundColor Red
 64            exit 1
 65        }
 66    } catch {
 67        Write-Host "Error installing Bicep CLI: $($_.Exception.Message)" -ForegroundColor Red
 68        exit 1
 69    }
 70} elseif ($updateBicep) {
 71    Write-Host "Updating Bicep CLI to latest version..." -ForegroundColor Yellow
 72    try {
 73        az bicep upgrade
 74        if ($LASTEXITCODE -eq 0) {
 75            Write-Host "Bicep CLI updated successfully." -ForegroundColor Green
 76            $newBicepVersion = az bicep version --output tsv 2>$null
 77            Write-Host "Updated Bicep CLI version: $newBicepVersion" -ForegroundColor Green
 78        } else {
 79            Write-Host "Failed to update Bicep CLI." -ForegroundColor Red
 80        }
 81    } catch {
 82        Write-Host "Error updating Bicep CLI: $($_.Exception.Message)" -ForegroundColor Red
 83    }
 84} else {
 85    # Check for updates and auto-update if available
 86    Write-Host "Checking for Bicep CLI updates..." -ForegroundColor Cyan
 87    try {
 88        $updateCheck = az bicep list-versions --output json | ConvertFrom-Json
 89        if ($updateCheck -and $updateCheck.Count -gt 0) {
 90            $latestVersion = $updateCheck[0]
 91            if ($bicepVersion -ne $latestVersion) {
 92                Write-Host "Bicep CLI update available: $latestVersion (current: $bicepVersion)" -ForegroundColor Yellow
 93                Write-Host "Auto-updating Bicep CLI to latest version..." -ForegroundColor Yellow
 94                
 95                az bicep upgrade
 96                if ($LASTEXITCODE -eq 0) {
 97                    $newBicepVersion = az bicep version --output tsv 2>$null
 98                    Write-Host "Bicep CLI updated successfully to version: $newBicepVersion" -ForegroundColor Green
 99                } else {
100                    Write-Host "Failed to auto-update Bicep CLI. Continuing with current version." -ForegroundColor Yellow
101                }
102            } else {
103                Write-Host "Bicep CLI is up to date." -ForegroundColor Green
104            }
105        }
106    } catch {
107        Write-Host "Could not check for Bicep CLI updates. Continuing with current version." -ForegroundColor Yellow
108    }
109}
110# Check if Az.DesktopVirtualization module is installed
111Write-Host "Checking for Az.DesktopVirtualization PowerShell module..." -ForegroundColor Cyan
112if (-not (Get-Module -ListAvailable -Name Az.DesktopVirtualization)) {
113    Write-Host "Az.DesktopVirtualization module not found. Installing..." -ForegroundColor Yellow
114    try {
115        Install-Module -Name Az.DesktopVirtualization -Force -Scope CurrentUser -AllowClobber
116        Write-Host "Az.DesktopVirtualization module installed successfully." -ForegroundColor Green
117    } catch {
118        Write-Host "Failed to install Az.DesktopVirtualization module: $($_.Exception.Message)" -ForegroundColor Red
119        exit 1
120    }
121} else {
122    Write-Host "Az.DesktopVirtualization module is already installed." -ForegroundColor Green
123}
124<# Set Subscription Context #>
125Write-Host "Setting subscription context..." -ForegroundColor Cyan
126az account set --subscription $subscriptionID --output none
127if ($LASTEXITCODE -ne 0) {
128    Write-Host "Failed to set subscription context. Please verify the subscription ID and your access." -ForegroundColor Red
129    Write-Host "Please ensure you are logged in with 'az login' and have access to subscription: $subscriptionID" -ForegroundColor Yellow
130    exit 1
131}
132
133<# Retrieve Account Information with Error Handling #>
134Write-Host "Retrieving account information..." -ForegroundColor Cyan
135$accountInfo = az account show --output json 2>&1
136if ($LASTEXITCODE -ne 0) {
137    Write-Host "Failed to retrieve account information. Error: $accountInfo" -ForegroundColor Red
138    Write-Host "Please ensure you are logged in to Azure CLI with 'az login'" -ForegroundColor Yellow
139    Write-Host "If you're already logged in, try: az account clear && az login" -ForegroundColor Yellow
140    exit 1
141}
142
143try {
144    $accountJson = $accountInfo | ConvertFrom-Json -ErrorAction Stop
145    if ($accountJson.user -and $accountJson.user.name) {
146        $updatedBy = $accountJson.user.name
147        Write-Host "Successfully authenticated as: $updatedBy" -ForegroundColor Green
148    } else {
149        Write-Host "Warning: Unable to retrieve user name from account information." -ForegroundColor Yellow
150        $updatedBy = "Unknown User"
151    }
152} catch {
153    Write-Host "Failed to parse account information. Raw output: $accountInfo" -ForegroundColor Red
154    Write-Host "Error details: $($_.Exception.Message)" -ForegroundColor Red
155    Write-Host "Please try the following steps:" -ForegroundColor Yellow
156    Write-Host "1. Run 'az login' to authenticate with Azure CLI" -ForegroundColor Yellow
157    Write-Host "2. Run 'az account set --subscription $subscriptionID' to set the correct subscription" -ForegroundColor Yellow
158    Write-Host "3. Run 'az account show' to verify your authentication status" -ForegroundColor Yellow
159    exit 1
160}
161
162<# Validate Subscription Access #>
163Write-Host "Validating subscription access..." -ForegroundColor Cyan
164$currentSub = az account show --query id --output tsv 2>&1
165if ($LASTEXITCODE -ne 0 -or $currentSub -ne $subscriptionID) {
166    Write-Host "Subscription validation failed. Expected: $subscriptionID, Current: $currentSub" -ForegroundColor Red
167    exit 1
168}
169Write-Host "Subscription validation successful: $subscriptionID" -ForegroundColor Green
170
171<# Set Variables #>
172$location = $location.ToLower() -replace " ", ""
173
174$LocationShortCodeMap = @{
175    "westeurope"  = "weu";
176    "northeurope" = "neu";
177}
178
179$locationShortCode = $LocationShortCodeMap.$location
180
181<# Validate Required Files #>
182Write-Host "Validating required files..." -ForegroundColor Cyan
183$requiredFiles = @("./deploy-sessionhost.bicep", "./deploy-sessionhosts.bicepparam")
184foreach ($file in $requiredFiles) {
185    if (-not (Test-Path $file)) {
186        Write-Host "Required file not found: $file" -ForegroundColor Red
187        exit 1
188    }
189}
190Write-Host "All required files found." -ForegroundColor Green
191
192<# Validate Bicep Template #>
193Write-Host "Validating Bicep template..." -ForegroundColor Cyan
194try {
195    az bicep build --file ./deploy-sessionhost.bicep --stdout > $null
196    if ($LASTEXITCODE -eq 0) {
197        Write-Host "Bicep template validation successful." -ForegroundColor Green
198    } else {
199        Write-Host "Bicep template validation failed. Please check your template." -ForegroundColor Red
200        exit 1
201    }
202} catch {
203    Write-Host "Error validating Bicep template: $($_.Exception.Message)" -ForegroundColor Red
204    exit 1
205}
206
207<# Generate Registration Token for Azure Virtual Desktop Host Pool #>
208Write-Host "Generating registration token for Azure Virtual Desktop Host Pool..." -ForegroundColor Cyan
209
210# Define Host Pool name and resource group (update these variables as needed)
211$hostPoolName = "vdpool-avd-prod-weu"
212$resourceGroupName = "rg-avd-prod-weu"
213$date = (Get-Date).AddHours(2).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK")
214
215try {
216    $registrationToken = az desktopvirtualization hostpool update `
217        --name $hostPoolName `
218        --resource-group $resourceGroupName `
219        --registration-info expiration-time=$date registration-token-operation="Update" 
220
221    if ($LASTEXITCODE -eq 0 -and $registrationToken) {
222        Write-Host "Registration token generated successfully:" -ForegroundColor Green
223        Write-Host $registrationToken -ForegroundColor Cyan
224    } else {
225        Write-Host "Failed to generate registration token. Error: $registrationToken" -ForegroundColor Red
226        exit 1
227    }
228} catch {
229    Write-Host "Exception while generating registration token: $($_.Exception.Message)" -ForegroundColor Red
230    exit 1
231}
232
233<# Deploy Resources #>
234if ($deploy) {
235    Write-Host "Running a Bicep deployment with ID: '$deploymentID' for Environment: '$environmentType' with a 'WhatIf' check." -ForegroundColor Green
236    
237    try {
238        az deployment sub create `
239            --name $deploymentID `
240            --location $location `
241            --template-file ./deploy-sessionhost.bicep `
242            --parameters ./deploy-sessionhosts.bicepparam `
243            --parameters updatedBy="$updatedBy" location="$location" locationShortCode="$locationShortCode" productType="$productType" environmentType="$environmentType" `
244            --confirm-with-what-if
245        
246        if ($LASTEXITCODE -eq 0) {
247            Write-Host "Deployment completed successfully!" -ForegroundColor Green
248        } else {
249            Write-Host "Deployment failed. Please check the error messages above." -ForegroundColor Red
250            exit 1
251        }
252    } catch {
253        Write-Host "Deployment failed with exception: $($_.Exception.Message)" -ForegroundColor Red
254        exit 1
255    }
256} else {
257    Write-Host "Deployment not requested. Use -deploy switch to execute the deployment." -ForegroundColor Yellow
258}
259
260Write-Host "Script execution completed." -ForegroundColor Green

In the snippet below you can see the information that needs to be set manually in the ps1 file when you want to deploy your sessionhosts.

Use your own values in the deployment script else the registration key will not work

1$hostPoolName = "vdpool-avd-prod-weu"
2$resourceGroupName = "rg-avd-prod-weu"
3$date = (Get-Date).AddHours(2).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK")

How to run this?

You can find these files in my repository, but of course, you can also just copy and paste them from this website. One tip: keep all the files in the same folder.

Then, open your command prompt and navigate to the location where you saved the files. When you run the command below (with your own details, of course), the script will start executing.

The full command including product type and other parameters is mandatory for now because it’s part of the validation in the PowerShell command. However, for the session host Bicep itself, it is'nt really needed, since the values are already taken from the Bicep parameter file. You will see the warning message when deploying the script.

Just make sure to run the command exactly like this:

.\deploy-sessionhost.ps1 -subscriptionID "yoursubscriptionid" -location "westeurope" -productType "avd" -environmentType "prod" -deploy

When you don't want to use al those validators as shown below, just remove them in the validation set in the powershell script.

-subscriptionID "" -location "westeurope" -productType "avd" -environmentType "prod"


Some deployment details:

We will start by running the script using the command from the folder where the three files are located:

After the check it will ask you if you want to continu:

When confirming it will start the deployment you can check this under your deployments in the subscription.

And you can check the Hostpool if the registration key is being registered by the deployment script:

In my example i have deployed 2 Azure Virtual Desktops so it will take a couple of minutes when the deployment finishes, but you will see the registration in the hostpool as shown below:

So the Session Host are deployed!!


Conclusion:

Deploying Azure Virtual Desktop session hosts with Bicep really makes life easier. By using Azure Verified Modules and keeping most of the values in your Bicep parameter file, you can automate almost the entire process without touching custom scripts. The only thing you need is a simple deployment script to kick things off, and after that, Bicep does the rest.

When calling existing resources with Bicep you can easily build/update new resources for Azure Virtual Desktop and the best part is that it’s flexible and maintainable.

Thanks for reading and visiting my blog if you have any question reach out!