Skip to content

Export Azure DevOps Code Search results

Published: at 17:43

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:

  1. Cross-project search: Search across multiple projects within your organization.
  2. Syntax highlighting: Results are displayed with proper syntax highlighting for easy readability.
  3. Advanced filters: Use filters like file extensions, specific repositories, or even particular branches to narrow down your search.
  4. 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:

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:

  1. Search across an entire organization or a specific project
  2. Use the same search filters available in the web UI
  3. Export results including project name, repository, file path, and more
  4. 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

  1. Save the script to a file, for example, Search-AzureDevOpsCode.ps1.
  2. Open a PowerShell terminal.
  3. Navigate to the directory containing the script.
  4. 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.