Forum Discussion

Logan_Silliman's avatar
Jul 02, 2024

Update to Microsoft Desktop Virtualization API v. 2023-09-05 by August 2, 2024 to avoid any impact

[Recommended actions updated on July 29, 2024]

 

WARNING! Be mindful when using secrets in deployment templates and follow Azure best practices when managing secrets. Our examples in this discussion post are to be used for educational purposes only.

 

Older Microsoft Desktop Virtualization API version(s) utilized for your Azure Virtual Desktop host pool resource will no longer support ‘get’ actions for registration token retrieval as of August 2nd, 2024.

 

 The affected API versions are as follows:  

  • 2019-01-23-preview 
  • 2019-09-24-preview 
  • 2019-12-10-preview 
  • 2020-09-21-preview  
  • 2020-11-02-preview  
  • 2020-11-10-preview  
  • 2021-01-14-preview 

 

On August 2nd, 2024, these affected API versions will no longer support the retrieval of the registration token. Users on older versions will not be able to use the 'get' action to retrieve the token. However, with the newer versions, there are two ways for customers to retrieve registration tokens moving forward:

  1. [Recommended] Using list* resource functions: Microsoft.DesktopVirtualization/hostpools resources now expose a listRegistrationTokens() function. This works if you already have valid registration tokens on your host pool and you want to retrieve them from an existing host pool.
  2. Using a  'post' action to securely retrieve the token

 

Action Required 

  1. Review any workflows you may have that rely on readers retrieving access tokens and update them to extract the registration tokens for a host pool in a new way.
  2. Ensure you are using up to date versions of the Microsoft Desktop Virtualization API.  

To take action, here are examples of how to extract the registration tokens for a host pool and update to the 2023-09-05 API version using Bicep and ARM templates. 

 

WARNING! Be mindful when using secrets in deployment templates and follow Azure best practices when managing secrets. Our examples in this discussion post are to be used for educational purposes only.

 

[Recommended] Take action using list* resource functions

This solution works if you already have valid registration tokens on your host pool and you want to retrieve them from an existing host pool. 

 

If you are using Bicep templates in your deployment:

 

 

 

 

 

@sys.description('AVD Host Pool resource ID. (Default: )')
param hostPoolResourceId string

var varHostpoolSubId = split(hostPoolResourceId, '/')[2]
var varHostpoolRgName = split(hostPoolResourceId, '/')[4]
var varHostPoolName = split(hostPoolResourceId, '/')[8]

// GET hostpool
resource hostPoolGet 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' existing = {
  name: varHostPoolName
  scope: resourceGroup('${varHostpoolSubId}', '${varHostpoolRgName}')
}

@sys.description('The registration token of the host pool. This is not secure! Only for educational/testing purposes. Please follow security practices @ https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/scenarios-secrets ')
output registrationToken array = hostPoolGet.listRegistrationTokens()

 

 

 

 

 

If you are using ARM templates in your deployment:

 

 

 

 

 

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.28.1.47646",
      "templateHash": "2750874554099795062"
    }
  },
  "parameters": {
    "hostPoolResourceId": {
      "type": "string",
      "metadata": {
        "description": "AVD Host Pool resource ID. (Default: )"
      }
    }
  },
  "variables": {
    "varHostpoolSubId": "[split(parameters('hostPoolResourceId'), '/')[2]]",
    "varHostpoolRgName": "[split(parameters('hostPoolResourceId'), '/')[4]]",
    "varHostPoolName": "[split(parameters('hostPoolResourceId'), '/')[8]]"
  },
  "resources": [],
  "outputs": {
    "registrationToken": {
      "type": "array",
      "metadata": {
        "description": "The registration token of the host pool. This is not secure! Only for educational/ testing purposes. Please follow security practices @ https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/scenarios-secrets "
      },
      "value": "[listRegistrationTokens(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', format('{0}', variables('varHostpoolSubId')), format('{0}', variables('varHostpoolRgName'))), 'Microsoft.DesktopVirtualization/hostPools', variables('varHostPoolName')), '2023-09-05')]"
    }
  }
}

 

 

 

 

 

 

Other ways to take action

One alternative is to always (re)create your host pool, which in turn will re-generate registration tokens that can then be retrieved using the PUT operation. 

 

If you are using Bicep templates in your deployment...

Use the retrieveToken.bicep module to retrieve the registration token from a host pool by using a PUT operation:

 

 

 

 

 

 

@sys.description('Optional. Host Pool token validity length. Usage: \'PT8H\' - valid for 8 hours; \'P5D\' - valid for 5 days; \'P1Y\' - valid for 1 year. When not provided, the token will be valid for 8 hours.')
param tokenValidityLength string = 'PT8H'

@sys.description('Generated. Do not provide a value! This date value is used to generate a registration token.')
param baseTime string = utcNow('u')

param vLocation string

param vHostPoolName string

param vHostPoolType string

param vPreferredAppGroupType string

param vMaxSessionLimit int

param vLoadBalancerType string


resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' = {
  name: vHostPoolName
  location: vLocation
    properties: {
    hostPoolType: vHostPoolType
    preferredAppGroupType: vPreferredAppGroupType
    maxSessionLimit: vMaxSessionLimit
    loadBalancerType: vLoadBalancerType
    registrationInfo: {
      expirationTime: dateTimeAdd(baseTime, tokenValidityLength)
      registrationTokenOperation: 'Update'
    }
  }
}

@sys.description('The registration token of the host pool.')
output registrationToken string = reference(hostPool.id).registrationInfo.token

 

 

 

 

 

 

 Here's an example of using the retrieveToken.bicep module to extract the registration token:

 

 

 

 

 

 

@sys.description('AVD Host Pool resource ID. (Default: )')
param hostPoolResourceId string

var varHostpoolSubId = split(hostPoolResourceId, '/')[2]
var varHostpoolRgName = split(hostPoolResourceId, '/')[4]
var varHostPoolName = split(hostPoolResourceId, '/')[8]

// Call on the hostpool
resource hostPoolGet 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' existing = {
  name: varHostPoolName
  scope: resourceGroup('${varHostpoolSubId}', '${varHostpoolRgName}')
}

module hostPool 'retrieveToken.bicep' = {
  name: varHostPoolName
  scope: resourceGroup('${varHostpoolSubId}', '${varHostpoolRgName}')
  params: {
    vHostPoolName: varHostPoolName
    vMaxSessionLimit: hostPoolGet.properties.maxSessionLimit
    vPreferredAppGroupType: hostPoolGet.properties.preferredAppGroupType
    vHostPoolType: hostPoolGet.properties.hostPoolType
    vLoadBalancerType: hostPoolGet.properties.loadBalancerType
    vLocation: hostPoolGet.location
  }
}


@sys.description('The registration token of the host pool.')
output registrationToken string = hostPool.outputs.registrationToken

 

 

 

 

 

 

If you are using ARM templates in your deployment:

 

 

 

 

 

 

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.28.1.47646",
      "templateHash": "15215789985349638425"
    }
  },
  "parameters": {
    "hostPoolName": {
      "type": "string"
    },
    "location": {
      "type": "string"
    },
    "baseTime": {
      "type": "string",
      "defaultValue": "[utcNow('u')]"
    }
  },
  "variables": {
    "expirationTime": "[dateTimeAdd(parameters('baseTime'), 'PT1H1M')]"
  },
  "resources": [
    {
      "type": "Microsoft.DesktopVirtualization/hostPools",
      "apiVersion": "2023-09-05",
      "name": "[parameters('hostPoolName')]",
      "location": "[parameters('location')]",
      "properties": {
        "maxSessionLimit": 2,
        "hostPoolType": "Personal",
        "loadBalancerType": "Persistent",
        "preferredAppGroupType": "Desktop",
        "registrationInfo": {
          "expirationTime": "[variables('expirationTime')]",
          "registrationTokenOperation": "Update"
        }
      }
    }
  ],
  "outputs": {
    "token": {
      "type": "string",
      "value": "[reference(resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))).registrationInfo.token]"
    }
  }
}

 

 

 

 

 

 

WARNING! Be mindful when using secrets in deployment templates and follow Azure best practices when managing secrets. Our examples in this discussion post are to be used for educational purposes only.

 

Additional Support  

If you have any questions, comments, or concerns about this, please feel free to post a comment.

  • HunterW620's avatar
    HunterW620
    Copper Contributor

    What's the point of using listRegistrationTokens() if there is no way to generate more than one token and have both be valid at the same time?

    I've tried using the portal, PowerShell Az modules, Azure CLI, and the API directly to generate token's for the same host pool and  listRegistrationTokens() only ever shows 1 token.

    • JasonMasten's avatar
      JasonMasten
      Icon for Microsoft rankMicrosoft

      HunterW620 a host pool can only have one token. The token expires based on the date time range selected / input when created. The same token may be used for multiple deployments as long as it hasn't expired or doesn't expire during the deployment. Why would you need more than one token at one point in time? The list function is a secure and simple way to get the token during an ARM template deployment.

      • HunterW620's avatar
        HunterW620
        Copper Contributor

        The function name is plural.

        list tokenS.

        The ability to have multiple valid tokens would be a huge benefit so they can be rolled without interruption if you have an environment where hosts are constantly being deployed but don’t want long living tokens with huge expiration times. 

  • yaegashi's avatar
    yaegashi
    Copper Contributor

    Logan_SillimanRegarding retrieveToken.bicep, it appears that the registration token is updated each time the module is invoked.  Is it safe to execute multiple deployments with this module concurrently?

    • Logan_Silliman's avatar
      Logan_Silliman
      Icon for Microsoft rankMicrosoft
      Great question! It depends! if this module is being invoked for different hostpools, then yes, it should be safe! If this module is invoked in parallel for the same hostpool, you will likely end up with conflicts.

      The provided samples are are intended solely for educational purposes. You should adapt them to fit your production use cases: e.g. you can store the resulting hostpool registrationToken in a keyvault and have all subsequent deployments access this value from key vault.
      Even better, you can start using listRegistrationTokens() function instead, which will retrieve existing tokens and won't recreate them.
      • JasonMasten's avatar
        JasonMasten
        Icon for Microsoft rankMicrosoft
        Logan, where is the documentation for the new "listRegistrationTokens()" function?
  • WARNING! Secrets should never be used in the output section of an ARM template. The value will be stored in plain text in the portal and can be seen by anyone with the Reader role. The registration token should be passed to a key vault secret resource within the same deployment and referenced using the "getSecret" function in Bicep when it is needed: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/key-vault-parameter?tabs=azure-cli#retrieve-secrets-in-bicep-file. Or use a key vault reference in JSON ARM templates: https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/key-vault-parameter?tabs=azure-cli#reference-secrets-with-dynamic-id

Resources