Going Beyond Azure DevOps Search: Scanning All Repos and Branches

  • Luis Rigueira
  • Jun 8, 2026
  • 5 min read

Sometimes Azure DevOps search is enough.

You type a keyword, get a few results, open the file, and move on.

But sometimes you need more out of it.

Recently, we had to identify which integration flows were using a specific SFTP endpoint/account reference. The obvious first step was to use Azure DevOps search and look for a specific keyword across the project repositories.

Azure DevOps search found some results, but with one important limitation: all the results returned by the general search were pointing to the main branch.

That may be perfectly fine if main is the only branch being actively used. But in real projects, especially with many integrations, that is not always the case. Some deployment changes or environment-specific configurations may still live in dev, release, sit, or feature branches. And also when you have dozens of repositories and multiple active branches, checking this manually quickly becomes painful.

So the question was simple: how can we make sure we are not missing references that exist outside main?

The requirement:

Find all integration flows that reference a specific SFTP account/endpoint.

The keyword was something very specific, for example:

<search-term>

The approach

Instead of relying only on the Azure DevOps UI search, we used a PowerShell script to:

  1. Get all repositories from an Azure DevOps project.
  2. Clone them locally using a shallow clone.
  3. Fetch all remote branches.
  4. Search each branch for a specific keyword.
  5. Export the results to a CSV file.

This gives a much better overview of where the reference exists.

The script we used:

$organization = "https://dev.azure.com/<your-organization>"
$project = "<your-project>"
$root = "C:\Temp\AzureDevOps-Search"
$searchTerm = "<search-term>"

Write-Host "Starting Azure DevOps repository scan..." -ForegroundColor Cyan

# Check Azure CLI
if (-not (Get-Command az -ErrorAction SilentlyContinue)) {
    Write-Host "Azure CLI is not installed or not available in PATH." -ForegroundColor Red
    exit
}

# Check Git
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
    Write-Host "Git is not installed or not available in PATH." -ForegroundColor Red
    exit
}

# Ensure Azure DevOps extension exists
az extension add --name azure-devops --only-show-errors 2>$null

# Create local folder
New-Item -ItemType Directory -Force -Path $root | Out-Null

# Configure Azure DevOps defaults
az devops configure --defaults organization=$organization project=$project

# Get all repositories from the project
$repos = az repos list --query "[].{name:name,webUrl:webUrl}" -o json | ConvertFrom-Json

if (-not $repos) {
    Write-Host "No repositories found. Check your Azure DevOps login, organization or project." -ForegroundColor Red
    exit
}

$results = @()

foreach ($repo in $repos) {
    $repoPath = Join-Path $root $repo.name

    Write-Host ""
    Write-Host "Processing repo: $($repo.name)" -ForegroundColor Cyan

    if (!(Test-Path $repoPath)) {
        Write-Host "  Cloning repo..."
        git clone --depth 1 --no-single-branch $repo.webUrl $repoPath
    }
    else {
        Write-Host "  Updating local repo..."
        git -C $repoPath fetch --all --depth=1
    }

    $branches = git -C $repoPath for-each-ref --format="%(refname:short)" refs/remotes/origin |
        Where-Object { $_ -ne "origin/HEAD" }

    foreach ($branch in $branches) {
        Write-Host "  Searching branch: $branch"

        $hits = git -C $repoPath grep -n -I -i --fixed-strings $searchTerm $branch -- 2>$null

        foreach ($hit in $hits) {
            if ($hit -match "^(?<branch>[^:]+):(?<file>[^:]+):(?<line>\d+):(?<text>.*)$") {
                $results += [pscustomobject]@{
                    Repo       = $repo.name
                    Branch     = $matches.branch -replace "^origin/", ""
                    File       = $matches.file
                    LineNumber = $matches.line
                    Match      = $matches.text.Trim()
                }
            }
        }
    }
}

$outputFile = Join-Path $root "search-results.csv"

$results |
    Sort-Object Repo, Branch, File, LineNumber -Unique |
    Export-Csv $outputFile -NoTypeInformation -Encoding UTF8

Write-Host ""
Write-Host "Scan completed." -ForegroundColor Green
Write-Host "Results exported to:"
Write-Host $outputFile -ForegroundColor Green

What the script gives you?

As you can see by this powershell, the output is a CSV file with useful details such:

  • Repo
  • Branch
  • File
  • LineNumber
  • Match
RepoBranchFileLineNumberMatch
<integration-code-repo><branch-name><deployment-file.yml>92<sftp-hostname-key-vault-reference>
<integration-code-repo><branch-name><deployment-file.yml>93<sftp-username-key-vault-reference>
<integration-code-repo><branch-name><deployment-file.yml>94<sftp-password-key-vault-reference>
<integration-infra-repo><branch-name><pipeline-file.yml>143<sftp-hostname-secret-name>
<integration-infra-repo><branch-name><pipeline-file.yml>144<sftp-password-secret-name>
<integration-infra-repo><branch-name><pipeline-file.yml>145<sftp-username-secret-name>

That means you can quickly answer questions like:

  • Which integration uses this endpoint?
  • In which repository?
  • In which branch?
  • In which deployment file?
  • Which exact reference was found?

This is especially useful when the same integration has multiple branches such as:

main
dev
sit
release/*
feature/*

Is it safe?

The script does not change anything in Azure DevOps.

It only performs read operations:

az repos list
git clone
git fetch
git grep
Export-Csv

There is no git push, no commit, no delete, and no update to repositories, pipelines, or branches.

The only thing it creates is a local folder with cloned repositories and a CSV file with the results.

Important note

This approach covers repository-based files, such as:

YAML pipelines
deployment scripts
ARM/Bicep templates
Logic App deployment files
configuration files

However, it may not catch references stored outside Git, such as:

Azure DevOps Variable Groups
Classic Release Pipelines
Key Vault secrets
manually configured resources

So, for a complete audit, those areas may need a separate check.

In conclusion…

Azure DevOps search is great for a quick check.

But when you need to create a reliable inventory across many repositories and branches, a local scripted search gives you much more confidence.

Author: Luis Rigueira

Luis Rigueira is a Enterprise Integration Consultant at DevScope

Leave a Reply

Your email address will not be published. Required fields are marked *

The Ultimate Cloud
Management Platform for Azure

Supercharge your Azure Cost Saving

Learn More
Turbo360 Widget

Back to Top