Delete Retention Leases in Azure DevOps — the fast and easy way

Amit Karni
Israeli Tech Radar
Published in
3 min readJul 29, 2024

In a recent project, I was tasked with streamlining a messy Azure DevOps infrastructure. The setup had many outdated and unused pipelines and releases, causing confusion and inefficiency.

During this task, I discovered that deleting old pipelines in Azure DevOps is challenging due to limitations in the default retention policy configuration.
Manually deleting retention policies for each build run through the UI is slow, tedious, and error-prone.
You have to navigate to each pipeline, access the retention leases for each historical build run, and delete them one by one. This process is especially problematic for high number of pipelines with hundreds or thousands of historical retained runs.

Challenges:

  1. No Bulk Deletion: There is no option to bulk delete old builds or their retention leases.
  2. Time-Consuming: Each build’s history must be accessed and cleaned up individually.
  3. Prone to Errors: The manual process can easily result in missed builds, leading to incomplete cleanup.

Solution: Automated Script

To address these challenges, the following Python script automates the process of deleting retention leases for all builds created before a specific date.
This allows for efficient and comprehensive cleanup, enabling you to delete the pipelines without long and exhausting manual intervention.

Benefits:

  • Time-Saving: Automates the tedious process of manual deletion.
  • Comprehensive Cleanup: Ensures all relevant retention leases are deleted.
  • Review Before Action: Allows you to review builds before deletion.

Python Script to Delete Retention Leases

import requests
from datetime import datetime
import base64

AZURE_DEVOPS_PAT = "<Azure DevOps Personal Access Token>"
BASE_URL = "https://dev.azure.com/org/project"
BUILDS_BEFORE = "2024-01-01T00:00:00.0000000Z" # get builds before this date - change it to the date you want
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Basic {base64.b64encode(f':{AZURE_DEVOPS_PAT}'.encode()).decode()}"
}

def get_builds_before():
builds = []
url = f"{BASE_URL}/_apis/build/builds?maxTime={BUILDS_BEFORE}&api-version=7.1"

while url:
response = requests.get(url, headers=HEADERS)
response.raise_for_status()
data = response.json()
builds.extend(data['value'])
url = data.get('continuationToken')
if url:
url = f"{BASE_URL}/_apis/build/builds?{url}&maxTime={BUILDS_BEFORE}&api-version=7.1"

return builds

def show_builds_and_get_approval(builds):
print("\nBuilds that will have their leases deleted:")
for build in builds:
print(f"Build ID: {build['id']}, Date: {build['startTime']}, Definition: {build['definition']['name']}")

while True:
approval = input("\nDo you want to proceed with deleting leases for these builds? (yes/no): ").lower()
if approval in ['yes', 'no']:
return approval == 'yes'
print("Please enter 'yes' or 'no'.")

def delete_retention_leases(builds):
for build in builds:
leases_url = f"{BASE_URL}/_apis/build/builds/{build['id']}/leases?api-version=7.1"
response = requests.get(leases_url, headers=HEADERS)
response.raise_for_status()
leases = response.json()['value']

for lease in leases:
delete_url = f"{BASE_URL}/_apis/build/retention/leases?ids={lease['leaseId']}&api-version=7.1"
response = requests.delete(delete_url, headers=HEADERS)
response.raise_for_status()
print(f"Deleted lease {lease['leaseId']} for build {build['id']}")

try:
print(f"Fetching builds before {BUILDS_BEFORE}...")
builds_to_clean = get_builds_before()
print(f"Found {len(builds_to_clean)} builds before {BUILDS_BEFORE}")

if show_builds_and_get_approval(builds_to_clean):
print("\nDeleting retention leases...")
delete_retention_leases(builds_to_clean)
print("Retention lease deletion process completed.")
else:
print("Operation cancelled by user.")
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred: {http_err}")
except Exception as err:
print(f"An error occurred: {err}")

How It Works

  • Fetch Builds: The script retrieves all builds created before January 1, 2024. (you can change it with your own needs)
  • Review Builds: It displays the builds that will be affected for your approval.
  • Delete Leases: It deletes retention leases for the approved builds using the Azure DevOps API.

This script streamlines the cleanup process, making it efficient and error-free, giving you better control over your Azure DevOps environment.
This addresses the manual process’s inefficiencies by automating the deletion of retention leases and allowing for bulk operations that are not possible through the Azure DevOps UI alone.

--

--

Amit Karni
Israeli Tech Radar

Senior DevOps Engineer with a passion for keeping up with new technologies. https://www.linkedin.com/in/amit-karni