The Power of Azure DevOps Code Search
Azure DevOps’ code search feature is a feature that allows developers to search for specific parts of code across an entire Azure DevOps project. Whether you’re looking for references to a particular dependency, searching for specific classes, or trying to locate certain code patterns, this feature can save you countless hours of manual searching.
Some key features of Azure DevOps code search include:
- Cross-project search: Search across multiple projects within your organization.
- Syntax highlighting: Results are displayed with proper syntax highlighting for easy readability.
- Advanced filters: Use filters like file extensions, specific repositories, or even particular branches to narrow down your search.
- Regular expression support: For more complex search patterns.
You can find more detailed information about the code search functionality in the official Microsoft documentation.
The Limitation: Lack of Export Functionality
While Azure DevOps code search is undoubtedly powerful, it does have a significant limitation: there’s no built-in way to export the search results. This can be problematic when you need to:
- Analyze search results offline
- Share findings with team members who don’t have access to Azure DevOps
- Generate reports based on code search results
- Perform further processing or analysis on the search results
The Solution: A Custom PowerShell Script
To overcome this limitation, I’ve adapted a PowerShell script that leverages the Azure DevOps REST API to perform code searches and export the results to a CSV file. This script, originally created by Maciej Porebski and available on his GitHub repository, has been modified to suit our specific needs. This script allows you to:
- Search across an entire organization or a specific project
- Use the same search filters available in the web UI
- Export results including project name, repository, file path, and more
- Save the results in a CSV format for easy analysis and sharing
Here’s the script:
#requires -Version 7.2
<#
.SYNOPSIS
Azure DevOps REST API to get results across an organization based on provided search text, just as you would in the web UI
.NOTES
https://learn.microsoft.com/en-us/rest/api/azure/devops/search/code-search-results/fetch-code-search-results?view=azure-devops-rest-7.1&tabs=HTTP#coderesult
Based on provided answer from SO: https://stackoverflow.com/a/64973447/12974596
.EXAMPLE
./Search-AzureDevOpsCode.ps1 -OrganizationName Company -Project Enterprise -SearchFilter "ext:yml AND somestringvalue" -AzAccessToken (Get-AzAccessToken)
Return results from Enterprise project in the Company organization
.EXAMPLE
./Search-AzureDevOpsCode.ps1 -OrganizationName Company -SearchFilter "ext:yml AND somestringvalue" -AzAccessToken (Get-AzAccessToken)
Return results from the Company organization, considering all projects
#>
[CmdletBinding()]
param (
# Organization Name for Azure DevOps tenant
[Parameter(Mandatory, Position = 0)]
[string]
$OrganizationName,
# Search filter, based on same filters allowed in the Web UI
# e.g., ext:yml AND somestringValue
[Parameter(Mandatory, Position = 2)]
[string]
$SearchFilter,
# Personal Access Token for Azure DevOps
[Parameter(Mandatory, Position = 3)]
[string]
$personalToken,
# Project to search, optional.
# If not provided will perform search at the organization level
[string]
$Project,
# Output CSV file path
[Parameter(Mandatory, Position = 4)]
[string]
$OutputCsvPath
)
process {
$body = @{
searchText = $SearchFilter
'$top' = 1000
} | ConvertTo-Json
# Encode the PAT in base64
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalToken)"))
$header = @{
'Authorization' = "Basic $token"
}
$ProjectNames = if ([string]::IsNullOrEmpty($Project)) {
$irmParams = @{
Uri = "https://dev.azure.com/$OrganizationName/_apis/projects?api-version=5.1"
Method = 'GET'
ContentType = 'application/json'
Headers = $header
}
(Invoke-RestMethod @irmParams | Select-Object -ExpandProperty value).name
} else {
$Project
}
Write-Verbose "Projects to process: $($ProjectNames)"
$collection = @()
$ProjectNames | ForEach-Object {
Write-Verbose "Project: $($_)"
$currentProject = $_
$irmSearchParams = @{
Uri = "https://almsearch.dev.azure.com/$($OrganizationName)/$($currentProject)/_apis/search/codesearchresults?api-version=7.1-preview.1"
Method = 'POST'
ContentType = 'application/json'
Headers = $header
Body = $body
}
try {
(Invoke-RestMethod @irmSearchParams -ErrorAction Stop).results | ForEach-Object {
$fileUrl = "https://dev.azure.com/$($OrganizationName)/$($currentProject)/_git/$($_.repository.name)?path=$($_.path)"
$collection += [pscustomobject]@{
Project = $_.project.name
Repository = $_.repository.name
RepositoryType = $_.repository.type
Version = $_.versions.branchName -join ','
FileName = $_.fileName
FilePath = $_.path
FileUrl = $fileUrl
}
}
} catch {
Write-Warning "Error occurred processing project: $($_.Exception.Message)"
}
}
# Write the collection to a CSV file
$collection | Export-Csv -Path $OutputCsvPath -NoTypeInformation
Write-Output "Results have been saved to $OutputCsvPath"
}
How to Use the Script
- Save the script to a file, for example,
Search-AzureDevOpsCode.ps1
. - Open a PowerShell terminal.
- Navigate to the directory containing the script.
- Run the script with the required parameters. For example:
.\Search-AzureDevOpsCode.ps1 -OrganizationName "YourOrg" -SearchFilter "ext:cs AND MyClass" -personalToken "YourPersonalAccessToken" -OutputCsvPath ".\search_results.csv"
This will search for C# files containing “MyClass” across all projects in your organization and save the results to search_results.csv
.