Skip to content

Instantly share code, notes, and snippets.

@rfennell
Created January 30, 2025 11:13
Show Gist options
  • Save rfennell/4f97272ce4122f552402f273b317e91c to your computer and use it in GitHub Desktop.
Save rfennell/4f97272ce4122f552402f273b317e91c to your computer and use it in GitHub Desktop.
A PowerShell script that used the Azure DevOps CLI to increment a Work Item field based on a WIQL query
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, HelpMessage="The URL of the Azure DevOps organisation e.g. https://dev.azure.com/myorg")]
$org,
[Parameter(Mandatory=$true, HelpMessage="The name of the project e.g 'My Project' ")]
$project,
[Parameter(Mandatory=$false, HelpMessage="The name of the field to update, defaults to 'Custom.PBIAge'")]
$fieldName = "Custom.PBIAge",
[Parameter(Mandatory=$false, HelpMessage="The WIQL query to find the work items to update. This is used if --sharedWiQueryId is not specified")]
$query = "SELECT [System.Id] FROM workitems WHERE ([System.WorkItemType] = 'Bug' OR [System.WorkItemType] = 'Product Backlog Item' ) AND [System.State] IN ('Active', 'Committed')",
[Parameter(Mandatory=$false, HelpMessage="The shared Work Item Query to find the work items to update.")]
$sharedWiQueryId,
[Parameter(Mandatory=$false, HelpMessage="The Personal Access Token to use to login to the organisation, if not present it is assumed login has previously been done using 'az login'")]
$pat
)
# Check we have a query or a sharedWiQueryId
if ($query -eq "" -and $sharedWiQueryId -eq "") {
Write-Error "Either the --query or --sharedWiQueryId parameter must be provided"
exit 1
}
# if pat is not null or empty then login to the organisation
if ($pat -ne $null -and $pat -ne "") {
# PAT authentication is easy to use, but a PAT will expire and need to be renewed on a regular basis
write-host "Logging into $org/$project using the provided PAT"
write-output $pat | az devops login --organization $org
} else {
# You can use the 'az login' cli command prior to running this script
# This can use
# - The simple 'az login' and do an interactive login
# - A service principal 'az login --service-principal -u <AppID> -p <PWD> --tenant <TenantID>'
# - A managed identity 'az login --identity' is not an option unless the script is being run on an Azure VM
write-host "Using the currently active 'az login' session to connect to $url/$project"
}
if ($sharedWiQueryId -eq $null) {
Write-Host "Using the WIQL provided with --query"
# We append the project filter to the query so that only the required project is queried
# If this is not done then the query will return all work items in the organisation that the user has access too
# Note: On why this required
# - This has been done in this manner, as opposed to including it in the query string, as it was deemed this makes the query easier to understand
# - You can't just use the 'az boards query' --project parameter as this does not work with the --wiql parameter.
$query += " AND [System.TeamProject] = '$project'"
write-host "Using the query '$query'"
# Get the work items that match the query, and force into an array as PS will not return an array for a single item
$workitems = @(az boards query --org $org --project $project --wiql $query | ConvertFrom-Json)
} else {
Write-Host "Using the WIQL saved as Shared WI Query with the ID specified --sharedWiQueryId $sharedWiQueryId"
# You can pass in a shared query id to load a complete query, inc the project filter
# This is not the default mechanism as though it makes it easier to edit the WQIL, in Azure DevOps UI,
# it is harder to understand from this script to know what the query is doing.
# Also the query could edited in the Ui, changing behaviour of the script unexpectedly
# Get the work items that match the query, and force into an array as PS will not return an array for a single item
$workitems = @(az boards query --org $org --project $project --id $sharedWiQueryId | ConvertFrom-Json)
}
write-host "Found $($workitems.Count) work items that match the query"
# Loop across all the work items that match the query
foreach ($wi in $workitems) {
$id = $wi.id
# Get the current value of the fieldName
$currentWi = az boards work-item show --org $org --id $id | ConvertFrom-Json
$fieldValue = $currentWi.fields.$fieldName
Write-Host " Processing '$($currentWi.fields."System.WorkItemType")' WI #$id"
# Check the fieldName is numeric and if the value is null set to zero
if ($fieldValue -eq $null) {
$fieldValue = 0
} elseif ($fieldValue -isnot [double] -and $fieldValue -isnot [int64]) {
Write-Warning "The fieldName '$fieldName' does not contain numeric data'"
continue
}
write-host " The current value of the fieldName '$fieldName' will be updated from $fieldValue to $($fieldValue+1)"
$updatedWi = az boards work-item update --org $org --id $id --fields "$fieldName=$($fieldValue+1)" | ConvertFrom-Json
# error if the field was not updated
if ($updatedWi.fields.$fieldName -ne $fieldValue+1) {
Write-Host "The fieldName '$fieldName' was not updated to $($fieldValue+1) "
exit 1
}
}
Write-Host "All work items have been updated"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment