ACReaper.psm1
|
function ACReaper { param ( [switch]$dryRun, [string]$registry = "", [string]$repository = "", [int]$keepLatest = 0, [string[]]$protectedTags = @("latest", "stable") ) if (-not $registry) { $registry = Read-Host "Please enter the registry name" } if (-not $repository) { $repository = Read-Host "Please enter the repository name" } if ($keepLatest -eq 0) { $keepLatest = Read-Host "Please specify the number of the most recent images to keep" } $images = az acr manifest list-metadata -r $registry -n $repository --output json | ConvertFrom-Json $sortedImages = $images | Sort-Object { [datetime]$_.lastUpdateTime } -Descending $imagesWithTags = $sortedImages | Where-Object { $_.tags -and $_.tags.Count -gt 0 } $imagesToKeep = $imagesWithTags | Select-Object -First $keepLatest $imagesToDeleteInitial = $imagesWithTags | Select-Object -Skip $keepLatest $protectedImages = @() $imagesToDelete = @() $skippedProtectedImages = @() foreach ($img in $imagesToDeleteInitial) { $isProtected = $false foreach ($tag in $img.tags) { if ($protectedTags -contains $tag) { $isProtected = $true break } } if ($isProtected) { $skippedProtectedImages += $img } else { $imagesToDelete += $img } } Write-Output "=== Kept Images (Latest $keepLatest tags) ===" $keepCounter = 1 $totalKeepSize = 0 foreach ($img in $imagesToKeep) { $date = [datetime]$img.lastUpdateTime $gbSize = [math]::Round($img.imageSize / 1GB, 2) $totalKeepSize += $img.imageSize foreach ($tag in $img.tags) { if ($protectedTags -contains $tag) { Write-Output "$keepCounter. skip protected image: tag=${tag}, size=${gbSize}GB, date=${date}" } else { Write-Output "$keepCounter. keep image: tag=${tag}, size=${gbSize}GB, date=${date}" } $keepCounter++ } } if ($skippedProtectedImages.Count -gt 0) { Write-Output "`n=== Protected Images (skipped from deletion) ===" $protCounter = 1 foreach ($img in $skippedProtectedImages) { $date = [datetime]$img.lastUpdateTime $gbSize = [math]::Round($img.imageSize / 1GB, 2) foreach ($tag in $img.tags) { Write-Output "$protCounter. skip protected image: tag=${tag}, size=${gbSize}GB, date=${date}" $protCounter++ } $totalKeepSize += $img.imageSize } } $totalKeepSizeGB = [math]::Round($totalKeepSize / 1GB, 2) Write-Output "Total kept size: ${totalKeepSizeGB}GB`n" if ($dryRun) { Write-Output "(dry-run) No images will be deleted. Below are the candidates:" } else { Write-Output "Deleting images..." } $deleteCounter = 1 $totalRemoveSize = 0 foreach ($img in $imagesToDelete) { $date = [datetime]$img.lastUpdateTime $gbSize = [math]::Round($img.imageSize / 1GB, 2) foreach ($tag in $img.tags) { Write-Output "$deleteCounter. remove image: tag=${tag}, size=${gbSize}GB, date=${date}" $deleteCounter++ } if (-not $dryRun) { foreach ($tag in $img.tags) { az acr repository delete --name $registry --image "${repository}:${tag}" --yes } } $totalRemoveSize += $img.imageSize } $totalRemoveSizeGB = [math]::Round($totalRemoveSize / 1GB, 2) if ($dryRun) { Write-Output "`nTotal size to be removed (dry-run): ${totalRemoveSizeGB}GB" Write-Output "Dry-Run complete!" } else { Write-Output "`nTotal images removed in size: ${totalRemoveSizeGB}GB" Write-Output "Deletion completed!" } } Export-ModuleMember -Function ACReaper |