Created
January 30, 2025 11:13
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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