Delete an Azure DevOps pipeline whose builds are retained by a release
Posted on by Bertrand Thomas
Azure-devops
Powershell
Azure DevOps can refuse to delete a pipeline when its builds are retained by a release, but a small PowerShell script can clean up the leases and unblock the deletion.
One or more builds associated with the requested pipeline(s) are retained by a release. The pipeline(s) and builds will not be deleted.
$personalAccessToken = "xxxx"
$organization = "xxxx"
$project = "xxxx"
$pipelineName = "xxxxx"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalAccessToken)"))
$header = @{authorization = "Basic $token"}
# Get the pipeline definition
$url = "https://dev.azure.com/$organization/$project/_apis/build/definitions?api-version=6.0"
$definitions = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers $header
$targetDef = $definitions.value | Where-Object { $_.name -eq $pipelineName }
if (-not $targetDef) {
Write-Host "Pipeline '$pipelineName' not found!" -ForegroundColor Red
return
}
Write-Host "Found pipeline ID: $($targetDef.id) - $($targetDef.name)"
# Get all builds for this pipeline
$url = "https://dev.azure.com/$organization/$project/_apis/build/builds?definitions=$($targetDef.id)&api-version=6.0"
$builds = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers $header
$retainedBuilds = $builds.value | Where-Object { $_.retainedByRelease -eq $true }
if ($retainedBuilds.Count -eq 0) {
Write-Host "No retained builds found. You should now be able to delete the pipeline manually." -ForegroundColor Green
return
}
Write-Host "Found $($retainedBuilds.Count) retained builds. Cleaning them up..." -ForegroundColor Yellow
foreach ($build in $retainedBuilds) {
Write-Host "Processing build $($build.id)..."
# Get leases for this build
$url = "https://dev.azure.com/$organization/$project/_apis/build/builds/$($build.id)/leases?api-version=7.1-preview.1"
$leases = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers $header
# Delete every lease
foreach ($lease in $leases.value) {
$url = "https://dev.azure.com/$organization/$project/_apis/build/retention/leases?ids=$($lease.leaseId)&api-version=7.1-preview.2"
Invoke-RestMethod -Uri $url -Method Delete -ContentType "application/json" -Headers $header
Write-Host " → Lease $($lease.leaseId) deleted"
}
# Delete the build itself
$url = "https://dev.azure.com/$organization/$project/_apis/build/builds/$($build.id)?api-version=7.1-preview.7"
Invoke-RestMethod -Uri $url -Method Delete -ContentType "application/json" -Headers $header
Write-Host " → Build $($build.id) deleted" -ForegroundColor Green
}
# Finally delete the pipeline
$url = "https://dev.azure.com/$organization/$project/_apis/build/definitions/$($targetDef.id)?api-version=6.0"
Invoke-RestMethod -Uri $url -Method Delete -ContentType "application/json" -Headers $header
Write-Host "Pipeline '$pipelineName' has been deleted!" -ForegroundColor Green