We have Azure Devops Services coupled with several internal build servers, so when troubleshooting builds, especially sporadic issues, it helps to know which build server the build has run on in order to understand if it is a server specific issue or not. We happen to use Azure Devops Services for our Azure Devops installation and internal build servers.
The powershell script below uses the Azure Devops rest api to retrieve the build information. You may need to adjust the proxy parameter for the invoke-restmethod lines as well as update some of the variables to match your environment.
The list of build runs i shown in the powershell gridview. There is a ShowInExcel commandline switch in case you prefer to view data with Excel. With this switch enabled, the data will be shown in a gridview and then saved as a csv file, finally the invoke-item command is used to trigger the opening of the .csv file with the assiocated application. Excel may be overkill in this situation but I left it as an example.
[CmdletBinding()]
param(
[System.String]$project="---- Default AZDOS PROJECT ----",
[System.String]$buildIdNumber="--- Default BUILD DEFINITION ID ---",
[System.String]$maxBuilds="10",
[switch]$ShowInExcel
)
$pat = '----- YOUR AZDOS PAT TOKEN -----'
$header = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($pat)"))}
$baseBuildUrl = "https://dev.azure.com/----- COLLECTION ----/$project/_apis/build/builds"
$buildListUrl = "$($baseBuildUrl)?api-version=6.0&definitions=$buildIdNumber&queryOrder=finishTimeDescending&maxBuildsPerDefinition=$maxBuilds"
Write-output $buildListUrl
try
{
#Get build data
$builds=Invoke-RestMethod -uri $buildListURL -Method Get -Header $header -Proxy $env:HTTP_PROXY #-OutFile $outFileName
$builds.value[0].buildNumber
$List = New-Object System.Collections.ArrayList
foreach ($build in $builds.value)
{
#$build=$builds.value[2]
write-host $build.status
if ($build.status -ne "notStarted"){
$timeLineUrl="$baseBuildUrl/$($build.id)/Timeline?api-version=2.0"
$timeline=Invoke-RestMethod -uri $timeLineUrl -Method get -Header $header -Proxy $env:HTTP_PROXY
# Can't get the duration if the build isn't done!
if ($build.status -ne "completed"){
$duration = $build.status
} else {
$duration = "{0:hh}:{0:mm}:{0:ss}" -f $(New-TimeSpan -end $(get-date $build.finishTime) -start $(get-date $build.startTime))
}
$Hash = [ordered]@{
date = get-date -date $build.startTime -Format "yyyy/MM/dd"
time = get-date -date $build.startTime -Format "HH:mm"
duration = $duration
agent = $timeline.records[2].workerName #arbitrary choice of a step in the collection
result = $build.result
successfulSteps = ($timeline.records.result -eq "succeeded").count
buildId = $build.id
buildNumber = $build.buildNumber
branch = $build.sourceBranch.Replace("refs/heads/","") # still some builds don't use branch name as build number
}
$List.Add( $([pscustomobject]$Hash) )
}
}
if ($ShowInExcel) {
$file="$($env:TEMP)\$($build.definition.name)-builds-$(get-date -Format "yyMMdd.HHmm").csv"
# Show results in a gridview window and then save to a temporary file
$List | Out-GridView -PassThru -Title "$($build.definition.name) builds" | Export-Csv -NoType -UseCulture -path $file
write-host "Data saved in $file."
write-host "Opening in Excel"
invoke-item $file
}
else {
$List | Out-GridView -Title "$($build.definition.name) builds"
}
}
catch [Exception]
{
Write-Host $_.Exception.Message
Write-Host $_.Exception.Response
}