Post

Azure DevOps: Bulk Reparent Work Items

Reparent Work Items in the UI

If you are reparenting only a few work items, then the easiest way is to use the Mapping view in the Azure DevOps backlog, as described by Microsoft:

turn-mapping-on Enabling the mapping pane map-workitems Reparenting work items in Azure DevOps with the mapping pane

Reparent Work Items with a Script

However, mapping (or reparenting) work items in the Azure DevOps UI can be a little clunky - it can be done in mass using the parent mapping pane, but what if you have hundreds or thousands of work items split across multiple parent/child relationships or multiple backlogs? Then it becomes harder since you can’t use this functionality in a query window, only from the backlog.

This is a very simple PowerShell script utilizing the Azure DevOps CLI extension that can very quickly update the parent on a query of work items. I used a combination of the CLI commands with PowerShell, since PowerShell makes it super simple to use loops and JSON parsing. Before the Azure DevOps CLI, this would have to have been done with using the APIs, which isn’t hard, but this solution uses way fewer lines of code!

In this example, I am using a Tag on the work items I want to update.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
###############################
# Reparent work item
###############################

# Prerequisites:
# az devops login (then paste in PAT when prompted)

[CmdletBinding()]
param (
    [string]$org, # Azure devops org without the URL, eg: "MyAzureDevOpsOrg"
    [string]$project, # Team project name that contains the work items, eg: "TailWindTraders"
    [string]$tag, # only one tag is supported, would have to add another clause in the $wiql, eg: "Reparent"
    [string]$newParentId # the ID of the new work item, eg: "223"
)

az devops configure --defaults organization="https://dev.azure.com/$org" project="$project"

$wiql="select [ID], [Title] from workitems where [Tags] CONTAINS '$tag' order by [ID]"

$query=az boards query --wiql $wiql | ConvertFrom-Json

ForEach($workitem in $query) {
    $links=az boards work-item relation show --id $workitem.id | ConvertFrom-Json
    ForEach($link in $links.relations) {
        if($link.rel -eq "Parent") {
            $parentId=$link.url.Split("/")[-1]
            if($parentId -ne $newParentId) {
                write-host "Unparenting" $links.id "from $parentId"
                az boards work-item relation remove --id $links.id --relation-type "parent" --target-id $parentId --yes

                write-host "Parenting" $links.id "to $newParentId"
                az boards work-item relation add --id $links.id --relation-type "parent" --target-id $newParentId
            }
            else {
                write-host "Work item" $links.id "is already parented to $parentId"
            }
        }
    }
}

Improvement Ideas

  • Utilize the environment variable or from file method to be able to run az devops login in an unattended fashion
  • Use the APIs if you are so inclined, but I still like to use the CLI when possible
  • Expand the WIQL, and maybe add it as a script parameter!
This post is licensed under CC BY 4.0 by the author.