Azure Virtual Desktop - Working with registration tokens
A registration token for an Azure Virtual Desktop host pool is a unique identifier used to join new session hosts to the host pool. You would use it to securely add and manage virtual machines within the host pool, ensuring that only authorized devices are granted access.
When using newer versions of Azure Bicep starting with v0.11.1
, it is no longer possible to access the Azure Virtual Desktop (AVD) host pool registration token directly using the resource identifier inside of a Bicep template.
The following code is no longer valid.
resource hp 'Microsoft.DesktopVirtualization/hostPools@2022-04-01-preview' = {
name: 'test-avd-hp-we-001'
location: deploymentLocation
properties: {
hostPoolType: 'Pooled'
loadBalancerType: 'BreadthFirst'
preferredAppGroupType: 'Desktop'
maxSessionLimit: 10
startVMOnConnect: true
registrationInfo: {
registrationTokenOperation: 'Update'
expirationTime: dateTimeAdd(expirationTime, 'PT2H')
}
}
}
// This is where the problems start
output registrationToken string = hp.properties.registrationInfo.token
The deployment fails with the following error.
{
"code": "DeploymentOutputEvaluationFailed",
"message": "Unable to evaluate template outputs: 'registrationToken'. Please see error details and deployment operations. Please see https://aka.ms/arm-common-errors for usage details.",
"details": [
{
"code": "DeploymentOutputEvaluationFailed",
"target": "registrationToken",
"message": "The template output 'registrationToken' is not valid: The language expression property 'token' can't be evaluated.."
}
]
}
Introduction
So, why would you want to use this feature at all?
There are several reasons why you might want to access the registration token during an Azure Bicep deployment. In my test lab, I often update the token during deployment and capture it as output for manual dev/test purposes. You can also store the registration token in an Azure KeyVault or pass it back to another module. This is a smart move if you're deploying your session hosts within the same deployment.
Troubleshooting
So let's take a closer look at why it is no longer possible to access the registration token by referencing the host pool resource identifier.
When you add the code shown above to your Azure Bicep project in Visual Studio Code, everything looks fine. There is no error from the Bicep language server that the registration token reference is invalid code.
Let's remember that this code was still valid until the version of Bicep CLI v0.11.1
. To find out what happens under the hood we compile our Bicep file into an ARM template.
To create the first ARM template example, I will use the latest Bicep CLI v0.16.2
, in which we are experiencing deployment issues and which is the most recent version at the time of writing this article.
Note: I am a big fan of the Bicep CLI embedded in the Azure CLI. It is very easy to list all available versions using the
az bicep list-versions
command. You can seamlessly switch between available versions using the commandaz bicep install --version 0.xx.x
.
After executing the command az bicep build --file main.bicep
, our line representing the registration token output appears as follows:
"outputs": {
"registrationToken": {
"type": "string",
"value": "[reference(resourceId('Microsoft.DesktopVirtualization/hostPools', 'test-avd-hp-we-001'), '2021-07-12').registrationInfo.token]"
}
Here we can see a reference function was built holding the resource Id of our host pool and an API version 2021-07-12
.
Okay, now let's downgrade our Bicep CLI to version v0.10.61
and run the build command again.
To downgrade the Bicep CLI, we will run the command: az bicep install --version 0.10.61
.
"outputs": {
"registrationToken": {
"type": "string",
"value": "[reference(resourceId('Microsoft.DesktopVirtualization/hostPools', 'test-avd-hp-we-001')).registrationInfo.token]"
}
}
In this example, we can see that our output was built using the same reference
and resourceId
functions. The difference here is that no API version was specified.
We can even go one step further and validate this behaviour using the Azure Resource Manager REST API. In the first round, we build the following request using the resource id of our host pool and the API version '2021-07-12' provided in the ARM template we built with the latest Bicep CLI.
GET: https://management.azure.com/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-demo-avd1-we/providers/Microsoft.DesktopVirtualization/hostPools/test-avd-hp-we-001?api-version=2021-07-12
RESPONSE:
{
"name": "test-avd-hp-we-001",
"id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/rg-demo-avd1-we/providers/Microsoft.DesktopVirtualization/hostpools/test-avd-hp-we-001",
"type": "Microsoft.DesktopVirtualization/hostpools",
"location": "westeurope",
"tags": null,
"kind": null,
"systemData": {
...
},
"properties": {
...
"hostPoolType": "Pooled",
...
"maxSessionLimit": 10,
"loadBalancerType": "BreadthFirst",
"validationEnvironment": false,
"registrationInfo": null,
...
}
}
In the response, the 'registrationInfo' property is null, indicating that we don't receive any registration information from the REST API using this version.
We can still cross-check this by using an older API version for our request. So let's do it.
GET: https://management.azure.com/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-demo-avd1-we/providers/Microsoft.DesktopVirtualization/hostPools/test-avd-hp-we-001?api-version=2021-01-14-preview
RESPONSE:
{
"name": "test-avd-hp-we-001",
"id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/rg-demo-avd1-we/providers/Microsoft.DesktopVirtualization/hostpools/test-avd-hp-we-001",
"type": "Microsoft.DesktopVirtualization/hostpools",
"location": "westeurope",
"tags": null,
"kind": null,
"systemData": {
...
},
"properties": {
"friendlyName": null,
"description": null,
"hostPoolType": "Pooled",
...
"maxSessionLimit": 10,
"loadBalancerType": "BreadthFirst",
...
"registrationInfo": {
"resetToken": false,
"expirationTime": "2023-03-26T21:55:24Z",
"token": "***",
"registrationTokenOperation": "None"
},
...
}
}
Look, we can access the registration token directly again using the older API version.
Solutions
Downgrade to Bicep CLI v0.10.61
Let's not talk about it. The Bicep CLI is under active development and it's always a good idea to use the latest version to benefit from the latest features and fixes.
Create the reference yourself
During our troubleshooting, we discovered that accessing the registration token directly through the resource identifier is no longer possible. Code such as hp.properties.registrationInfo.token
will not function with newer versions of the Bicep CLI due to the API version Bicep compiling into our template.
To resolve the issue, simply construct the reference yourself. The code below, for instance, will compile into a valid ARM template:
registrationtoken: reference(hp.id).registrationInfo.token
Now you can proceed with exciting tasks using your registration token, such as storing it as an Azure KeyVault secret.
Closing words
I don't believe it's negative that in recent API versions, accessing the registration token is no longer possible. The registration token is a secret and should be handled accordingly.
In the future, I would like to see a dedicated function to access the token. Similar to what has already been solved for Azure KeyVaults with .GetSecret()
. However, this is something that would have to be implemented on the provider side and not in Bicep.
Whenever you run into problems like those described in this blog, it's always a good idea to compile your template into ARM code. There is no serious reason to learn ARM templates in the age of Azure Bicep. However, some problems are easier to solve and understand at the ARM code level.
Inspiration
This blog was written while listening to the album 'Money For Nothing (Remastered 2022)' by Dire Straits. Enjoy it.
Subscribe to my newsletter
Read articles from Lukas Rottach directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Lukas Rottach
Lukas Rottach
I am an Azure Architect based in Switzerland, specializing in Azure Cloud technologies such as Azure Functions, Microsoft Graph, Azure Bicep and Terraform. My expertise lies in Infrastructure as Code, where I excel in automating and optimizing cloud infrastructures. With a strong passion for automation and development, I aim to share insights and practices to inspire and educate fellow tech enthusiasts. Join me on my journey through the dynamic world of the Azure cloud, where innovation meets efficiency.