Post

PowerShell Script: Tracking Field Usage Across Azure DevOps Projects

In response to a practical need to identify and quantify the usage of a specific field across projects in Azure DevOps, I developed this PowerShell script.

This script interacts with the Azure DevOps API, to retrieve a list of projects and analyze each for the presence of a designated field. It not only identifies which projects utilize this field but also counts the work items within those projects that include it.

Additionally, the script compiles these findings into a markdown file. This file serves as a summary, presenting a clear breakdown of the number of work items per project that make use of the field, thus providing valuable insights into field utilization across your Azure DevOps environment.

Script

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# Prompt user for inputs
$organization = Read-Host -Prompt "Enter the Azure DevOps organization URL in format https://dev.azure.com/ORGNAME"
$personalAccessToken = Read-Host -Prompt "Enter your personal access token"
$field = "FIELD_REFERENCE_NAME"
$batchSize = 10  # Define the number of projects to process in each batch

# Function to encode the personal access token and return the authorization header
function Get-AuthorizationHeader {
    param (
        [string]$token
    )
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$token"))
    return @{ Authorization = "Basic $base64AuthInfo" }
}

# Define the headers
$headers = Get-AuthorizationHeader -token $personalAccessToken

# Define the API URL template
$apiUrlTemplate = "$organization/{0}/_apis/{1}?api-version={2}"

# Function to get the list of all projects
function Get-AllProjects {
    $url = $apiUrlTemplate -f "", "projects", "7.0"
    return (Invoke-RestMethod -Uri $url -Method Get -Headers $headers).value
}

# Function to get the list of fields in a project
function Get-ProjectFields {
    param (
        [string]$projectId
    )
    $url = $apiUrlTemplate -f "$projectId", "wit/fields", "7.1-preview.3"
    return (Invoke-RestMethod -Uri $url -Method Get -Headers $headers).value
}

# Function to query work items for a project
function Query-WorkItems {
    param (
        [string]$projectName,
        [string]$field
    )
    $query = @"
    SELECT [System.Id], [System.WorkItemType]
    FROM workitems
    WHERE [System.TeamProject] = '$projectName'
    AND [System.WorkItemType] <> ''
    AND [$field] <> ''
"@
    $url = $apiUrlTemplate -f "$projectName", "wit/wiql", "6.0"
    $body = @{ query = $query } | ConvertTo-Json
    return (Invoke-RestMethod -Uri $url -Method Post -Headers $headers -Body $body -ContentType "application/json").workItems
}

# Main script to retrieve and filter projects by the specified field
$allProjects = Get-AllProjects
$projectsUsingField = @()

Write-Output "=============================================="
Write-Output "List of projects using the field '$field'"
Write-Output "=============================================="

$counter = 1

# Implement batching mechanism
for ($i = 0; $i -lt $allProjects.Count; $i += $batchSize) {
    $currentBatch = $allProjects[$i..($i + $batchSize - 1)]
    foreach ($project in $currentBatch) {
        $projectId = $project.id
        $projectName = $project.name
        $projectFields = Get-ProjectFields -projectId $projectId

        if ($projectFields | Where-Object { $_.referenceName -eq $field }) {
            $projectsUsingField += $projectName
            Write-Output "${counter}: $projectName"
            $counter++
        }
    }
}

Write-Output "=============================================="
Write-Output "Field ($field) Statistics:"
Write-Output "=============================================="

# Prepare markdown content
$markdownContent = @"
|No|Project Name| Field Usage|
|--|------------|------------|
"@

# Output the list of projects using the specified field and query work items
if ($projectsUsingField.Count -eq 0) {
    Write-Output "No projects are using the field '$field'."
} else {
    $index = 1
    foreach ($projectName in $projectsUsingField) {
        $workItems = Query-WorkItems -projectName $projectName -field $field
        $workItemCount = if ($workItems) { $workItems.Count } else { 0 }
        $markdownContent += "`n|$index|$projectName|$workItemCount|"
        $index++
        Write-Output "$projectName has $workItemCount work items using the field"
    }

    # Define the markdown file path relative to the script's directory
    $markdownFilePath = Join-Path -Path $PSScriptRoot -ChildPath "Field-Usage.md"

    # Write the markdown content to the file
    $markdownContent | Out-File -FilePath $markdownFilePath -Encoding utf8

    Write-Output "Markdown file created at: $markdownFilePath"
}

Dive into the script

Let’s break down each part of the script for a clearer understanding:

1. Prompting User Inputs

The script starts by prompting the user for the Azure DevOps organization URL and a personal access token. These are essential for authenticating API requests.

1. Function for Authorization Header

A function named Get-AuthorizationHeader is defined to generate the authorization header using the provided personal access token. This header will be utilized in subsequent REST API calls to Azure DevOps.

1. API URL Template and Functions for Azure DevOps Interactions

  • $apiUrlTemplate: This variable holds a template for constructing Azure DevOps API URLs with placeholders for project ID, resource type, and API version.
  • Get-AllProjects: Retrieves a list of all projects within the Azure DevOps organization.
  • Get-ProjectFields: Fetches the list of fields defined within a specific project.
  • Query-WorkItems: Executes a WIQL query to retrieve work items for a given project based on a specified field.

1. Retrieving and Filtering Projects

The script retrieves all projects and iterates through them in batches. For each project, it checks if the specified field is used and adds the project name to a list if it is.

1. Outputting Project and Field Statistics

The script outputs a list of projects using the specified field, along with the count of work items associated with that field in each project. Additionally, it generates markdown content for further documentation and analysis.

1. File Output

If projects are found using the specified field, the script writes markdown content to a file named “Field-Usage.md” in the script’s directory, providing a structured overview of field usage across projects.

Running the Script

1. Set Up Your Environment

  • PowerShell: Ensure you have PowerShell installed on your computer. PowerShell 5.1 or higher is recommended for better compatibility with the script features.

2. Prepare the Script

  • Place the Script: Save your script in a known directory.
  • Customize the Script: Make sure the $field variable in your script matches the field you want to analyze.

3. Run the Script

  • Open PowerShell: Start PowerShell from your Start menu or taskbar.

  • Navigate to Your Script: cd path\to\your\script

  • Execute the Script: .\YourScriptName.ps1

  • Input Requirements: The script will prompt you for the Azure DevOps organization URL and your personal access token. Make sure to enter these accurately to authenticate your API requests.

    • Organization URL: Enter the URL in the format https://dev.azure.com/ORGNAME.
    • Personal Access Token (PAT): Input your generated PAT from Azure DevOps. Ensure that the token has the necessary scopes for reading projects and work items.

4. View Results

  • Review the Markdown File: After the script finishes executing, it outputs the path to the markdown file it created (Field-Usage.md). You can open this file in any markdown viewer or text editor to see the report.

This is an example of how the generated markdown file looks like:

Generated Markdown File

References

Projects - List

Fields - List

Wiql - Query By Wiql

This post is licensed under CC BY 4.0 by the author.