Instantly share code, notes, and snippets.

Embed
What would you like to do?
Update build and release agent queues automatically in Azure DevOps
<#
.SYNOPSIS
Set the queue by name for all projects and builds/releases based on (optional) filters.
.PARAMETER NewQueueName
Name of the queue that will be set for all matched builds/releases.
.PARAMETER Token
A PAT for VSTS with at least these rights: "Agent Pools (read, manage)", "Build (read and execute)" and "Release (read, write, execute and manage)"
.PARAMETER AccountName
Name of your vsts account (name before .visualstudio.com in the url).
.PARAMETER ExcludeProjectNameFilter
Optional. If set projects matching the names in the (string) array will be excluded.
.PARAMETER ExcludeBuildNameFilter
Optional. If set builds matching the names in the (string) array will be excluded. Filter is additive with ProjectNameFilter.
.PARAMETER ExcludeReleaseNameFilter
Optional. If set releases matching the names in the (string) array will be excluded. Filter is additive with ProjectNameFilter.
#>
param(
[parameter(mandatory=$true)]
[string] $NewQueueName,
[parameter(mandatory=$true)]
[string] $AccountName,
[parameter(mandatory=$true)]
[string] $Token,
[array] $ExcludeProjectNameFilter,
[array] $ExcludeBuildNameFilter,
[array] $ExcludeReleaseNameFilter
)
$ErrorActionPreference = "Stop"
# if you haven't yet updated to the new url format, exchange the lines below
$baseUrl = "https://$($AccountName)@dev.azure.com/$($AccountName)"
# $baseUrl = "https://$($AccountName).visualstudio.com"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($AccountName):$Token"))
$projectApiUrl = "$baseUrl/_apis/projects?api-version=4.1"
$projectResponse = Invoke-RestMethod -Headers @{Authorization="Basic $base64AuthInfo"} -Uri $projectApiUrl -Method Get
ForEach($p in $projectResponse.value | Sort-Object -property name) {
$projectName = $p.name
if ($ExcludeProjectNameFilter -contains $projectName) {
continue
}
# each project has its own list of queues; same agent might have different ids between projects
# interestingly enough this endpoint is nowhere to be found in the documentation..
$buildAgentQueueUrl = "$baseUrl/$projectName/_apis/distributedtask/queues?api-version=4.1-preview"
$queuesResponse = Invoke-RestMethod -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -Uri $buildAgentQueueUrl -Method Get
Write-Host "Working on project: $projectName"
# get the id of the agent we want to use
$newQueue = $queuesResponse.value | Where-Object {
$_.name -eq $newQueueName | Select-Object -First 1
}
if ($newQueue -eq $null) {
Write-Host " Could not find queue '$NewQueueName' in project '$projectName'"
continue;
}
# update all build definitions
$buildDefinitionsUrl = "$baseUrl/$projectName/_apis/build/definitions?api-version=4.1"
$buildResponse = Invoke-RestMethod -Headers @{Authorization="Basic $base64AuthInfo"} -Uri $buildDefinitionsUrl -Method Get
if ($buildResponse.count -eq 0) {
Write-Host " No builds in project"
}
$buildResponse.value | foreach {
if ($ExcludeBuildNameFilter -contains $_.name) {
continue
}
$updateRequired = $false
# Download the build definition
$separator = if ($_.url.Contains("?")) { "&" } else { "?" }
$updateUrl = "$($_.url)$($separator)api-version=4.1"
$definition = Invoke-RestMethod -Headers @{Authorization="Basic $base64AuthInfo"} -Uri $updateUrl -Method Get
if ($definition.queue.name -ne $newQueueName) {
$updateRequired = $true
$definition.queue.name = $newQueue.name
$definition.queue.id = $newQueue.id
$definition.queue.pool = $newQueue.pool
}
# build phases can either be "inherit" or set to a custom queue
# to be safe just override all
$definition.process.phases | foreach {
# ignore inherited
if ($_.target.queue -ne $null) {
if ($_.target.queue.id -eq $newQueue.id) {
continue
}
$_.target.queue.id = $newQueue.id
$updateRequired = $true
}
}
if ($updateRequired) {
$json = $definition | ConvertTo-Json -Depth 99
# now upload the changed definition
$response = Invoke-RestMethod -Headers @{Authorization="Basic $base64AuthInfo"} -Uri $updateUrl -ContentType "application/json" -Method Put -Body $json
Write-Host " Set queue '$($response.queue.name)' on build '$($_.name)'"
}
}
# update all release definitions
# note the use of "vsrm" in the url (without it, it won't work).
# all the "sample urls" in the doc use the ".vsrm." subdomain (Visual Studio Release Management I assume), but there is no mention of it elsewhere
# https://www.visualstudio.com/en-us/docs/integrate/api/rm/definitions
# https://stackoverflow.com/a/41585777
$releaseDefinitionsUrl = "https://$($AccountName).vsrm.visualstudio.com/$projectName/_apis/release/definitions?api-version=4.1"
$releaseResponse = Invoke-RestMethod -Headers @{Authorization="Basic $base64AuthInfo"} -Uri $releaseDefinitionsUrl -Method Get
if ($releaseResponse.count -eq 0) {
Write-Host " No release definitions in project"
}
$releaseResponse.value | foreach {
if ($ExcludeReleaseNameFilter -contains $_.name) {
continue
}
$updateRequired = $false
# Download the release definition
$separator = if ($_.url.Contains("?")) { "&" } else { "?" }
$updateUrl = "$($_.url)$($separator)api-version=4.1"
$definition = Invoke-RestMethod -Headers @{Authorization="Basic $base64AuthInfo"} -Uri $updateUrl -Method Get
$definition.environments | foreach {
# releases have multiple environments and each environment may have multiple deploy phases
# thus only skip upload if all environments have the correct queue
# also it's by id and not name
# phases can only be set, never "inherited" (like builds)
$_.deployPhases | foreach {
if ($_.deploymentInput.queueId -eq $newQueue.id) {
continue
}
$_.deploymentInput.queueId = $newQueue.id
$updateRequired = $true
}
}
if ($updateRequired) {
$json = $definition | ConvertTo-Json -Depth 99
# now upload the changed definition
$response = Invoke-RestMethod -Headers @{Authorization="Basic $base64AuthInfo"} -Uri $updateUrl -ContentType "application/json" -Method Put -Body $json
Write-Host " Set queue '$NewQueueName' on release '$($_.name)'"
}
}
}
Write-Host "All done!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment