Last active
May 16, 2025 13:41
-
-
Save rfennell/45eef15772e0e676dc9e82cabd7d5ad0 to your computer and use it in GitHub Desktop.
This script connects to an Azure DevOps organization and project using a Personal Access Token (PAT). It fetches all pipelines, gathers details about each pipeline (including type, source repository, branch, and latest run status), and exports the collected information to a CSV file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Retrieves Azure DevOps pipeline information and exports it to a CSV file. | |
.DESCRIPTION | |
This script connects to an Azure DevOps organization and project using a Personal Access Token (PAT). | |
It fetches all pipelines, gathers details about each pipeline (including type, source repository, branch, and latest run status), | |
and exports the collected information to a CSV file. | |
.PARAMETER organization | |
The name of the Azure DevOps organization. Default is "blackmarble-source". | |
.PARAMETER project | |
The name of the Azure DevOps project. Default is "bm". | |
.PARAMETER pat | |
The Personal Access Token (PAT) used for authentication with Azure DevOps REST API. | |
.PARAMETER outputCsv | |
The file path for the output CSV file. Default is "./azdo-pipelines.csv". | |
.NOTES | |
- Requires PowerShell and network access to Azure DevOps REST API. | |
- The PAT should have sufficient permissions to read pipelines and repositories. | |
- Handles both YAML and Classic pipelines. | |
- For YAML pipelines using Azure Repos Git, attempts to resolve the repository URL via the Azure DevOps API. | |
.EXAMPLE | |
.\get-azdo-builds.ps1 -organization "my-org" -project "my-project" -pat "my-pat" -outputCsv "./pipelines.csv" | |
Retrieves pipeline information from the specified Azure DevOps organization and project, and exports it to "pipelines.csv". | |
#> | |
# Azure DevOps organization and project details | |
param( | |
[string]$organization, | |
[string]$project, | |
[string]$pat, | |
[string]$outputCsv | |
) | |
# Base64-encode the PAT for authentication | |
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$pat")) | |
# Get all pipelines | |
$uri = "https://dev.azure.com/$organization/$project/_apis/pipelines?api-version=7.0" | |
Write-Host "Fetching list of pipelines from Azure DevOps..." | |
$pipelines = Invoke-RestMethod -Uri $uri -Headers @{Authorization = "Basic $base64AuthInfo"} | |
Write-Host ("Found {0} pipelines." -f $pipelines.count) | |
# Prepare an array to hold pipeline info | |
$pipelineInfoList = @() | |
foreach ($pipeline in $pipelines.value) { | |
$pipelineId = $pipeline.id | |
$pipelineName = $pipeline.name | |
Write-Host ("Processing pipeline: {0} (ID: {1})..." -f $pipelineName, $pipelineId) | |
# Get pipeline details (for repository info) | |
$pipelineDetailsUri = "https://dev.azure.com/$organization/$project/_apis/pipelines/$($pipelineId)?api-version=7.0" | |
$pipelineDetails = Invoke-RestMethod -Uri $pipelineDetailsUri -Headers @{Authorization = "Basic $base64AuthInfo"} | |
# Handle both YAML and Classic pipelines | |
$designerJsonRepo = $null | |
$repo = $null | |
$pipelineType = $null | |
if ($pipelineDetails.configuration.PSObject.Properties.Name -contains 'designerJson') { | |
$designerJson = $pipelineDetails.configuration.designerJson | |
if ($designerJson.PSObject.Properties.Name -contains 'repository') { | |
$designerJsonRepo = $designerJson.repository | |
$pipelineType = $designerJson.type | |
} | |
} | |
if ($pipelineDetails.configuration.PSObject.Properties.Name -contains 'repository') { | |
$repo = $pipelineDetails.configuration.repository | |
$pipelineType = 'yaml' | |
} | |
if ($designerJsonRepo) { | |
$sourceType = $designerJsonRepo.type | |
$sourceBranch = $designerJsonRepo.defaultBranch | |
$sourceUrl = $designerJsonRepo.url | |
} elseif ($repo) { | |
$sourceType = $repo.type | |
$sourceBranch = $repo.defaultBranch | |
# For YAML pipelines with Azure Repos Git, get the repo URL from the repo id | |
if ($sourceType -eq 'azurereposgit' -and $pipelineType -eq 'yaml') { | |
$repoId = $repo.id | |
# Call the Azure DevOps Repositories API to get the repo details | |
$repoApiUri = "https://dev.azure.com/$organization/$project/_apis/git/repositories/$($repoId)?api-version=7.0" | |
try { | |
$repoDetails = Invoke-RestMethod -Uri $repoApiUri -Headers @{Authorization = "Basic $base64AuthInfo"} | |
$sourceUrl = $repoDetails.webUrl | |
} catch { | |
$sourceUrl = 'Cannot resolve repo with ID ' + $repoId | |
} | |
} else { | |
$sourceUrl = $repo.url | |
} | |
} else { | |
$sourceType = '' | |
$sourceBranch = '' | |
$sourceUrl = '' | |
} | |
# Get latest run | |
$runsUri = "https://dev.azure.com/$organization/$project/_apis/pipelines/$($pipelineId)/runs?api-version=7.0&$top=1" | |
$runs = Invoke-RestMethod -Uri $runsUri -Headers @{Authorization = "Basic $base64AuthInfo"} | |
if ($runs.count -gt 0) { | |
$lastRun = $runs.value[0] | |
$lastRunTime = $lastRun.createdDate | |
$status = $lastRun.result | |
Write-Host (" Last run: {0} (Status: {1})" -f $lastRunTime, $status) | |
} else { | |
$lastRunTime = "Never" | |
$status = "N/A" | |
Write-Host " No runs found for this pipeline." | |
} | |
$pipelineInfo = [PSCustomObject]@{ | |
"Pipeline Name" = $pipelineName | |
"Definition Id" = $pipelineId | |
"Pipeline Type" = $pipelineType | |
"Source Type" = $sourceType | |
"Source Branch" = $sourceBranch | |
"Source URL" = $sourceUrl | |
"Last Run Time" = $lastRunTime | |
"Status" = $status | |
} | |
$pipelineInfoList += $pipelineInfo | |
} | |
# Export to CSV | |
Write-Host "Exporting pipeline information to $outputCsv..." | |
$pipelineInfoList | Export-Csv -Path $outputCsv -NoTypeInformation -Encoding UTF8 | |
Write-Host "Pipeline information exported to $outputCsv" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment