Public/utils/Get-AzureDevOpsLibrary.ps1
|
<#
.SYNOPSIS Clones or updates all Git repositories from an Azure DevOps project. .DESCRIPTION The Get-AzureDevOpsLibrary function retrieves all repositories from a specified Azure DevOps project and clones them to a local directory. If repositories already exist locally, it will pull the latest changes instead. This provides a complete local backup of your Azure DevOps repository library. .PARAMETER Organization The Azure DevOps organization name (from the URL: dev.azure.com/YOUR_ORGANIZATION). .PARAMETER Project The name of the Azure DevOps project containing the repositories. .PARAMETER Username Your Azure DevOps username or email address used for authentication. .PARAMETER PersonalAccessToken A Personal Access Token (PAT) with at least 'Code (Read)' permissions. To create a PAT: User Settings → Personal Access Tokens → New Token .PARAMETER DestinationPath The local directory path where repositories will be cloned. Defaults to "C:\AzureDevOps\<ProjectName>". The directory will be created if it doesn't exist. .PARAMETER Force If specified, will re-clone repositories even if they already exist locally. .EXAMPLE Get-AzureDevOpsLibrary -Organization "contoso" -Project "MyProject" -Username "john@contoso.com" -PersonalAccessToken "abc123..." Clones all repositories from the MyProject project to C:\AzureDevOps\MyProject .EXAMPLE Get-AzureDevOpsLibrary -Organization "contoso" -Project "MyProject" -Username "john@contoso.com" -PersonalAccessToken "abc123..." -DestinationPath "D:\Repos" Clones all repositories to the specified destination path D:\Repos .EXAMPLE Get-AzureDevOpsLibrary -Organization "contoso" -Project "MyProject" -Username "john@contoso.com" -PersonalAccessToken "abc123..." -Force Re-clones all repositories even if they already exist locally .NOTES Author: Azure DevOps Repository Manager Requires: Git must be installed and available in PATH API Version: 7.0 .LINK https://learn.microsoft.com/en-us/rest/api/azure/devops/git/repositories/list #> function Get-AzureDevOpsLibrary { [CmdletBinding()] param( [Parameter(Mandatory = $true, HelpMessage = "Azure DevOps organization name")] [ValidateNotNullOrEmpty()] [string]$Organization, [Parameter(Mandatory = $true, HelpMessage = "Azure DevOps project name")] [ValidateNotNullOrEmpty()] [string]$Project, [Parameter(Mandatory = $true, HelpMessage = "Your Azure DevOps username/email")] [ValidateNotNullOrEmpty()] [string]$Username, [Parameter(Mandatory = $true, HelpMessage = "Personal Access Token with Code (Read) permissions")] [ValidateNotNullOrEmpty()] [string]$PersonalAccessToken, [Parameter(Mandatory = $false, HelpMessage = "Local destination path for cloned repositories")] [string]$DestinationPath = "C:\AzureDevOps\$Project", [Parameter(Mandatory = $false, HelpMessage = "Force re-clone of existing repositories")] [switch]$Force ) begin { # Load System.Web assembly for URL encoding Add-Type -AssemblyName System.Web # Verify Git is available try { $null = git --version } catch { throw "Git is not installed or not available in PATH. Please install Git first." } # Create destination directory if it doesn't exist if (-not (Test-Path $DestinationPath)) { New-Item -ItemType Directory -Path $DestinationPath -Force | Out-Null Write-Host "Created directory: $DestinationPath" -ForegroundColor Green } # Encode PAT for Basic Authentication $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$PersonalAccessToken")) # API endpoint $uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories?api-version=7.0" } process { Write-Host "Fetching repository list from Azure DevOps..." -ForegroundColor Cyan Write-Host "Organization: $Organization" -ForegroundColor Gray Write-Host "Project: $Project" -ForegroundColor Gray Write-Host "" try { # Get list of repositories $headers = @{ Authorization = "Basic $base64AuthInfo" } $response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers -ErrorAction Stop $repos = $response.value if ($repos.Count -eq 0) { Write-Warning "No repositories found in project '$Project'" return } Write-Host "Found $($repos.Count) repositories" -ForegroundColor Green Write-Host "" $successCount = 0 $failCount = 0 # Clone each repository foreach ($repo in $repos) { $repoName = $repo.name $cloneUrl = $repo.remoteUrl $repoPath = Join-Path $DestinationPath $repoName Write-Host "Processing: $repoName" -ForegroundColor Yellow if ((Test-Path $repoPath) -and -not $Force) { Write-Host " Repository already exists. Pulling latest changes..." -ForegroundColor Gray Push-Location $repoPath try { $gitOutput = git pull 2>&1 if ($LASTEXITCODE -eq 0) { Write-Host " ✓ Updated successfully" -ForegroundColor Green $successCount++ } else { Write-Host " ✗ Failed to update: $gitOutput" -ForegroundColor Red $failCount++ } } catch { Write-Host " ✗ Failed to update: $_" -ForegroundColor Red $failCount++ } finally { Pop-Location } } else { if ((Test-Path $repoPath) -and $Force) { Write-Host " Removing existing repository (Force mode)..." -ForegroundColor Gray Remove-Item -Path $repoPath -Recurse -Force } Write-Host " Cloning repository..." -ForegroundColor Gray # Remove any existing username from URL and add your username + PAT # URL-encode the username to handle special characters like @ # Format: https://encoded-username:PAT@dev.azure.com/org/project/_git/repo $cleanUrl = $cloneUrl -replace "https://[^@]*@", "https://" $encodedUsername = [System.Web.HttpUtility]::UrlEncode($Username) $authenticatedUrl = $cleanUrl -replace "https://", "https://${encodedUsername}:$PersonalAccessToken@" try { $gitOutput = git clone $authenticatedUrl $repoPath 2>&1 if ($LASTEXITCODE -eq 0) { Write-Host " ✓ Cloned successfully" -ForegroundColor Green $successCount++ } else { Write-Host " ✗ Failed to clone: $gitOutput" -ForegroundColor Red $failCount++ } } catch { Write-Host " ✗ Failed to clone: $_" -ForegroundColor Red $failCount++ } } Write-Host "" } # Summary Write-Host "========================================" -ForegroundColor Cyan Write-Host "Summary:" -ForegroundColor Cyan Write-Host " Total repositories: $($repos.Count)" -ForegroundColor White Write-Host " Successful: $successCount" -ForegroundColor Green if ($failCount -gt 0) { Write-Host " Failed: $failCount" -ForegroundColor Red } Write-Host " Location: $DestinationPath" -ForegroundColor White Write-Host "========================================" -ForegroundColor Cyan } catch { Write-Error "Failed to retrieve repositories: $_" Write-Host "Please verify:" -ForegroundColor Yellow Write-Host " - Your PAT has 'Code (Read)' permissions" -ForegroundColor Yellow Write-Host " - Organization and Project names are correct" -ForegroundColor Yellow Write-Host " - Your PAT has not expired" -ForegroundColor Yellow } } } # Example usage (uncomment to use): # Get-AzureDevOpsLibrary -Organization "your-org" -Project "your-project" -Username "your-email@domain.com" -PersonalAccessToken "your-pat" |