Setting Azure DevOps Agent Pool Descriptions via the Azure DevOps API

The Issue

If you are using Azure Scale Set based Azure DevOps Agent Pools (VMSS) to provide dynamically scalable agent pools, unlike with self hosted agent pools, there is no capabilities tab for the individual agents.

Agent Capabilities tab

My understanding is that this UX design choice was made as the capabilities of all the agents in a VMSS pool are identical, as they are the same disk image, so why show the same data multiple times.

However, this can raise a discoverability problem for teams who are not involved in the VMSS disk image creation process. They have no way to see what capabilities are available on various pools, or if versions of tools have been updated between disk image versions.

A Solution

Though the individual agents do not have a capabilities tab, the VMSS pool does have an editable 'Details' tab. So, why not publish the capabilities of the agents in the pool's details tab?

This can be achieved with a call to the Azure DevOp API. Though admittedly a call that is not well documented.

 2## <copyright file="Update-VMSSPoolDetails.ps1">(c) Richard Fennell. </copyright>
 4# Updates the description of a VMSS based pool in Azure DevOps
 8    [parameter(Mandatory = $true, HelpMessage = "URL of the Azure DevOps Organisation e.g. ''")]
 9    $orgUri ,
11    [parameter(Mandatory = $false, HelpMessage = "Personal Access Token")]
12    $pat,
14    [parameter(Mandatory = $true, HelpMessage = "The name of the AppPool to update")]
15    $poolName,
17    [parameter(Mandatory = $true, HelpMessage = "The new description for the VMSS based pool")]
18    $description 
22function Get-WebClient {
23    param
24    (
25        [string]$pat,
26        [string]$ContentType = "application/json"
27    )
29    $wc = New-Object System.Net.WebClient
30    $wc.Headers["Content-Type"] = $ContentType
32    $pair = ":${pat}"
33    $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
34    $base64 = [System.Convert]::ToBase64String($bytes)
35    $wc.Headers.Add(Authorization, "Basic $base64");
37    $wc
40$wc = Get-WebClient -pat $pat 
41write-host "Finding the Agent Pool '$poolName' in '$orgUri' " -ForegroundColor Green
42$uri = "$($orgUri)/_apis/distributedtask/pools?api-version=5.0-preview.1"
43$jsondata = $wc.DownloadString($uri) | ConvertFrom-Json
44$poolid = ($jsondata.value | where { $ -eq $poolName }).id
46$wc = Get-WebClient -pat $pat -ContentType "application/octet-stream"
47write-host "Updating details of pool ID '$poolid' on '$orgUri' with '$description'" -ForegroundColor Green
48$uri = "$($orgUri)/_apis/distributedtask/pools/$poolid/poolmetadata?api-version=5.0-preview.1"
49$wc.UploadString($uri, "PUT", $description) 

This script can be called passing in either a simple string description or something more complex such as markdown. For example

1Update-VMSSPoolDetails.ps1 -orgUri -pat $pat -poolName 'Azure VM Scale Set' -description "# Agent Details `n This is a description of the agents in this pool `n |Capability| Setting| `n |-|-| `n |Agent.OS | Windows_NT| `n |AgentOSVersion | 10.0| "

Agent Pool Details tab

The question then becomes how do you build the description parameter? There are no end of options, here are a few that come to mind:

  • Generate the string as part of your base image generation process, maybe using Packer
  • Or as you deploy your base images to Azure, maybe with Terraform or an Azure DevOps pipeline
  • Or even as part of a custom maintenance job, where you build the details based on the environment variables of the agent (remember an Azure DevOps capability is just an environment variable at the OS level)

So hopefully plenty of ideas for you to work with..