Intro
In the first blog post (here), I wrote the most basic Bicep code for deploying a host pool, application groups, and a workspace. The result will be the same resources in this part, but the code will be a bit more advanced, allowing us to customize the deployment. I will keep the code simple, and the goal is to make the part we change for each deployment as simple as possible, while the Bicep modules contain the more advanced code.
Main Bicep file
In the first blog post, my main bicep file was 44 lines of code; this time, it is only 31. It has become shorter because I am now using a module for some of the deployment code, and the idea is to update the module with any new settings but keep the main.bicep file as simple as possible. We can keep the main.bicep file is relatively simple, but at the same time, it also allows the person deploying to choose customized settings. In the code below, I have added only the required parameters to deploy the AVD environment, but as you will see in the module code, I could have passed more settings to the module. The deployment also changed from a resource group deployment to a subscription deployment. I use subscription deployments since I want the resource group created as part of the deployment so it follows the same naming standard.
targetScope = 'subscription'
param location string = 'WestEurope'
param name string = 'level2'
@allowed([
'Production'
'Test'
])
param environment string = 'Production'
param tags object = {
Owner: 'Martin'
Environment: environment
}
resource rg 'Microsoft.Resources/resourceGroups@2024-07-01' = {
name: 'rg-${name}'
location: location
tags: tags
}
module avd 'Modules/avd.bicep' = {
name: 'avd'
scope: rg
params: {
location: location
name: name
tags: tags
}
}
Module file
I created a Bicep module named avd.bicep, containing all the elements I want to deploy along with settings for these elements. The top sections are, as always, the parameters I use throughout the file. When the parameter doesn’t have a value, it is mandatory to provide this value when calling the module. If the parameter does have a value, it is an optional parameter, and therefore, if you don’t pass any value when calling the module, the value from the module file is used. This configuration enables setting a default configuration for all our settings, ensuring we can deploy AVD fast and easily, knowing that the deployment is using our recommended settings.
In the code below, I am using the @Allowed setting on some of the parameters. This ensures that only the allowed values are accepted, and any auto-complete will be able to read these values, making it easy to use the module with the correct parameter values.
param location string
param name string
param tags object
@allowed([
'Pooled'
'Personal'
])
param hostPoolType string = 'Pooled'
@allowed([
'BreadthFirst'
'DepthFirst'
])
param loadBalancerType string = 'DepthFirst'
@allowed([
'Desktop'
'RemoteApp'
])
param preferredAppGroupType string = 'Desktop'
resource hostpool 'Microsoft.DesktopVirtualization/hostPools@2024-08-08-preview' = {
name: 'vdpool-${name}'
location: location
properties: {
hostPoolType: hostPoolType
loadBalancerType: loadBalancerType
preferredAppGroupType: preferredAppGroupType
}
tags: tags
}
resource workspace 'Microsoft.DesktopVirtualization/workspaces@2024-08-08-preview' = {
name: 'vdws-${name}'
location: location
properties: {
friendlyName: '${name} workspace'
applicationGroupReferences: [
resourceId('Microsoft.DesktopVirtualization/applicationGroups', desktop_dag.name)
resourceId('Microsoft.DesktopVirtualization/applicationGroups', remote_app_dag.name)
]
description: '${name} Workspace'
}
tags: tags
}
resource desktop_dag 'Microsoft.DesktopVirtualization/applicationGroups@2024-08-08-preview' = {
name: 'vdag-${name}-desktop'
location: location
properties: {
friendlyName: '${name} Desktop'
applicationGroupType: 'Desktop'
hostPoolArmPath: resourceId('Microsoft.DesktopVirtualization/hostpools', hostpool.name)
}
tags: tags
}
resource remote_app_dag 'Microsoft.DesktopVirtualization/applicationGroups@2024-08-08-preview' = {
name: 'vdag-${name}-remoteapp'
location: location
properties: {
friendlyName: '${name} remote app'
applicationGroupType: 'RemoteApp'
hostPoolArmPath: resourceId('Microsoft.DesktopVirtualization/hostpools', hostpool.name)
}
tags: tags
}
Deployment
The code below deploys the code from this article.
New-AzSubscriptionDeployment -Name level2 -Location WestEurope -TemplateFile .\main.bicep
Summary
In this blog post, I updated the code from the first post to use a Bicep module. The module ensures the deployment remains simple in the base code and allows for a default configuration on most settings. The only required parameters in the code above are a name, a location, and tags. Tags could have been optional as well, but I use tags as much as possible, enabling me to identify who the owner is and whether the resources are used in testing or production.
Although the result of deploying the code from this post and the first post is the same, we are getting close to having a dynamic deployment that allows us to have a very simple deployment and a very customizable deployment if we want to.
More posts are upcoming since we need to ensure permissions and also include a storage account. Stay tuned.
Credits and sources
Microsoft Learn
https://learn.microsoft.com/en-us/azure/virtual-desktop/ https://learn.microsoft.com/en-us/azure/virtual-desktop/terminology https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations#virtual-desktop-infrastructure