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.
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.
##-----------------------------------------------------------------------
## <copyright file="Update-VMSSPoolDetails.ps1">(c) Richard Fennell. </copyright>
##-----------------------------------------------------------------------
# Updates the description of a VMSS based pool in Azure DevOps
param
(
[parameter(Mandatory = $true, HelpMessage = "URL of the Azure DevOps Organisation e.g. 'https://dev.azure.com/myorg'")]
$orgUri ,
[parameter(Mandatory = $false, HelpMessage = "Personal Access Token")]
$pat,
[parameter(Mandatory = $true, HelpMessage = "The name of the AppPool to update")]
$poolName,
[parameter(Mandatory = $true, HelpMessage = "The new description for the VMSS based pool")]
$description
)
function Get-WebClient {
param
(
[string]$pat,
[string]$ContentType = "application/json"
)
$wc = New-Object System.Net.WebClient
$wc.Headers["Content-Type"] = $ContentType
$pair = ":${pat}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$wc.Headers.Add(“Authorization”, "Basic $base64");
$wc
}
$wc = Get-WebClient -pat $pat
write-host "Finding the Agent Pool '$poolName' in '$orgUri' " -ForegroundColor Green
$uri = "$($orgUri)/_apis/distributedtask/pools?api-version=5.0-preview.1"
$jsondata = $wc.DownloadString($uri) | ConvertFrom-Json
$poolid = ($jsondata.value | where { $_.name -eq $poolName }).id
$wc = Get-WebClient -pat $pat -ContentType "application/octet-stream"
write-host "Updating details of pool ID '$poolid' on '$orgUri' with '$description'" -ForegroundColor Green
$uri = "$($orgUri)/_apis/distributedtask/pools/$poolid/poolmetadata?api-version=5.0-preview.1"
$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
Update-VMSSPoolDetails.ps1 -orgUri https://dev.azure.com/myorg -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| "
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..