Created
March 13, 2025 12:39
-
-
Save jfoshee/43450585b63b8c60a0a5c1b0ff3d2389 to your computer and use it in GitHub Desktop.
Delete retained builds for a specific build definition in Azure DevOps.
This file contains 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
using System.Net.Http.Json; | |
/// <summary> | |
/// Delete retained builds for a specific build definition in Azure DevOps. | |
/// </summary> | |
static async Task CleanRetainedBuildsAsync() | |
{ | |
// Configuration | |
string organization = "my-org"; | |
string project = "my-proj"; | |
int buildDefinitionId = 1234; | |
Console.WriteLine($"Cleaning retained builds for build definition {buildDefinitionId} in project {project} of organization {organization}"); | |
// Personal Access Token (PAT) with permissions for Build (Read & execute) and Release (Read, write & execute) | |
string pat = "my-pat"; | |
// Setup HttpClient with basic auth (username is blank) | |
var client = new HttpClient(); | |
client.DefaultRequestHeaders.Authorization = | |
new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", | |
Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($":{pat}"))); | |
string baseUri = $"https://dev.azure.com/{organization}/{project}"; | |
// Get all builds for the build definition | |
string buildsUri = $"{baseUri}/_apis/build/builds?definitions={buildDefinitionId}&api-version=7.0"; | |
var buildsResponse = await client.GetFromJsonAsync<BuildsResponse>(buildsUri); | |
if (buildsResponse is null || buildsResponse.Count == 0) | |
{ | |
Console.WriteLine("No builds found."); | |
return; | |
} | |
foreach (var build in buildsResponse.Value) | |
{ | |
if (!build.RetainedByRelease) | |
continue; | |
Console.WriteLine($"Processing retained build: {build.Id}"); | |
// Get lease details for the build | |
string leaseDetailsUri = $"{baseUri}/_apis/build/builds/{build.Id}/leases?api-version=7.1"; | |
var leaseResponse = await client.GetFromJsonAsync<LeaseResponse>(leaseDetailsUri); | |
if (leaseResponse?.Count > 0) | |
{ | |
foreach (var lease in leaseResponse.Value) | |
{ | |
Console.WriteLine($"Deleting lease {lease.LeaseId} for build {build.Id}"); | |
string deleteLeaseUri = $"{baseUri}/_apis/build/retention/leases?ids={lease.LeaseId}&api-version=7.1"; | |
var deleteLeaseResult = await client.DeleteAsync(deleteLeaseUri); | |
if (!deleteLeaseResult.IsSuccessStatusCode) | |
{ | |
Console.WriteLine($"Failed to delete lease {lease.LeaseId} for build {build.Id}: {deleteLeaseResult.StatusCode}"); | |
} | |
} | |
} | |
// Now delete the build | |
Console.WriteLine($"Deleting build {build.Id}"); | |
string deleteBuildUri = $"{baseUri}/_apis/build/builds/{build.Id}?api-version=7.1"; | |
var deleteBuildResult = await client.DeleteAsync(deleteBuildUri); | |
if (!deleteBuildResult.IsSuccessStatusCode) | |
{ | |
Console.WriteLine($"Failed to delete build {build.Id}: {deleteBuildResult.StatusCode}"); | |
} | |
} | |
} | |
await CleanRetainedBuildsAsync(); | |
record Build(int Id, bool RetainedByRelease); | |
record BuildsResponse(int Count, List<Build> Value); | |
record Lease(int LeaseId); | |
record LeaseResponse(int Count, List<Lease> Value); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment