Azure DevOps keeps deleted repositories in a Recycle Bin. That’s useful when a repo was deleted by accident, but if you want to permanently remove 50 of them, there’s no bulk action in the UI. You delete them one by one. I wrote a Python script that does it in bulk through the Azure DevOps REST API.
The script is on GitHub. It’s the same idea as the TLS audit script: if the portal makes you repeat the same click many times, use the API.
What the script does
It reads a JSON file with the deleted repositories and calls the delete endpoint for each one. It asks for all configuration interactively, so nothing is hardcoded, and prints a summary of successful and failed deletions at the end.
How it works
The script uses the git/recycleBin endpoint. The delete function:
def delete_repository(base_url, organization, project, repository_id, api_version, headers, auth): """ Permanently delete a repository from the Azure DevOps Recycle Bin. """ # Construct the URL url = f"{base_url}/{organization}/{project}/_apis/git/recycleBin/repositories/{repository_id}?api-version={api_version}"
try: response = requests.delete(url, headers=headers, auth=auth) if response.status_code == 200: print(f"✅ Successfully deleted repository ID: {repository_id}") return True else: print(f"❌ Failed to delete repository ID: {repository_id}. Status Code: {response.status_code}. Response: {response.text}") return False except Exception as e: print(f"⚠️ Error deleting repository ID: {repository_id}. Error: {str(e)}") return FalseA successful delete returns 200. For anything else, the script logs the status code and response body.
Authentication is Basic Auth with a Personal Access Token (PAT) as the password:
headers = { 'Content-Type': 'application/json'}auth = ('', pat) # Empty username, PAT as passwordThe repository list comes from a JSON file:
def load_json(file_path): """ Load JSON data from a file. """ with open(file_path, 'r') as f: return json.load(f)It expects a value array with objects that have at least an id:
{ "count": 2, "value": [ { "id": "12345678-1234-1234-1234-123456789012", "name": "old-repo-1" }, { "id": "87654321-4321-4321-4321-210987654321", "name": "old-repo-2" } ]}Running the script
The script prompts for its inputs:
base_url = input("🔗 Enter Azure DevOps Base URL (e.g., https://dev.azure.com): ").strip()organization = input("🏢 Enter your Organization Name: ").strip()project = input("📁 Enter your Project Name: ").strip()api_version = input("🔍 Enter API Version (e.g., 7.1): ").strip()json_file_path = input("📄 Enter the path to your JSON file (e.g., deleted_repos.json): ").strip()
print("\n🔐 Enter your Azure DevOps Personal Access Token (PAT).")print(" Note: Your PAT will not be displayed as you type.")pat = getpass("PAT: ").strip()The PAT is read with getpass, so it isn’t shown on screen.
Then it loops over the repositories. Entries without an id are skipped and counted as failures:
for repo in repositories: repo_id = repo.get('id') repo_name = repo.get('name') if not repo_id: print(f"⚠️ Repository name '{repo_name}' does not have an 'id'. Skipping.") failure_count += 1 continue
success = delete_repository(base_url, organization, project, repo_id, api_version, headers=headers, auth=auth) if success: success_count += 1 else: failure_count += 1The summary at the end:
📋 Deletion Process Completed.✅ Successfully deleted: 48❌ Failed to delete: 2Getting the repository list
Query the Recycle Bin with the REST API and save the response to a file:
GET https://dev.azure.com/{organization}/{project}/_apis/git/recycleBin/repositories?api-version=7.1If you already have the repository IDs from somewhere else, you can also write the JSON file by hand using the structure above.
Get the script
git clone https://github.com/Timohone/azure-devops-repo-recycle-bin-cleanercd azure-devops-repo-recycle-bin-cleanerpip install requestspython delete_repositories.pyThe README explains how to create the PAT and prepare the JSON file.
The code is short and easy to adapt, for example to change the authentication method or add filtering. If you use Azure DevOps pipelines for infrastructure, the EPAC pipeline patterns post may also be useful.
Sources
- “Azure DevOps REST API reference,” Microsoft Learn, https://learn.microsoft.com/en-us/rest/api/azure/devops/
- “Repositories - Delete,” Azure DevOps REST API, Microsoft Learn, https://learn.microsoft.com/en-us/rest/api/azure/devops/git/repositories/delete
- “Use personal access tokens to authenticate,” Microsoft Learn, https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate