All scripts
Azure AI 233
Azure Container Registry Repository Report
Lists every repository in your Azure Container Registries, how many tagged and untagged images each one has, and when the last image was pushed, so you can spot registries that need a cleanup or a retention policy.
Get-AzureContainerRegistryReport.ps1
<#
.SYNOPSIS
Reports on Azure Container Registry repositories, image counts, and untagged images across a subscription.
.DESCRIPTION
Connects to Azure, loops through every Container Registry in the subscription (or a single
resource group if you pass one), and lists each repository along with how many tagged images
it has, when the most recent image was pushed, and how many manifests have no tag at all.
Untagged manifests are usually leftover build artifacts nobody is using but that are still
costing you storage, so this is a fast way to find registries that need a cleanup or a
retention policy.
.PARAMETER SubscriptionId
The Azure subscription to scan. If you don't pass one, the script uses whatever subscription
is currently active in your session.
.PARAMETER ResourceGroupName
Optional. Limit the report to registries in a single resource group instead of the whole
subscription.
.PARAMETER ExportPath
Optional. Path to a CSV file. If provided, the report is also exported there instead of just
printed to the console.
.EXAMPLE
.\Get-AzureContainerRegistryReport.ps1
Reports on every Container Registry in the current subscription.
.EXAMPLE
.\Get-AzureContainerRegistryReport.ps1 -ResourceGroupName "rg-prod-containers" -ExportPath "C:\Reports\acr-report.csv"
Reports on registries in a single resource group and saves the results to a CSV file.
.NOTES
Author: Shehryar Hassan
Requires the Az.Accounts and Az.ContainerRegistry modules, plus the Azure CLI (az) available
on PATH for the manifest level detail, since Az.ContainerRegistry doesn't expose per-tag
push dates directly.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$SubscriptionId,
[Parameter(Mandatory = $false)]
[string]$ResourceGroupName,
[Parameter(Mandatory = $false)]
[string]$ExportPath
)
if (-not (Get-AzContext)) {
Write-Host "No active Azure session found. Connecting now." -ForegroundColor Yellow
Connect-AzAccount | Out-Null
}
if ($SubscriptionId) {
Set-AzContext -SubscriptionId $SubscriptionId | Out-Null
}
$registries = if ($ResourceGroupName) {
Get-AzContainerRegistry -ResourceGroupName $ResourceGroupName
} else {
Get-AzContainerRegistry
}
if (-not $registries) {
Write-Host "No Container Registries found in scope." -ForegroundColor Yellow
return
}
$results = @()
foreach ($registry in $registries) {
Write-Host "Scanning registry: $($registry.Name)" -ForegroundColor Cyan
$repositories = az acr repository list --name $registry.Name --output json 2>$null | ConvertFrom-Json
if (-not $repositories) {
continue
}
foreach ($repo in $repositories) {
$manifests = az acr repository show-manifests --name $registry.Name --repository $repo --output json 2>$null | ConvertFrom-Json
if (-not $manifests) {
continue
}
$taggedCount = ($manifests | Where-Object { $_.tags -and $_.tags.Count -gt 0 }).Count
$untaggedCount = ($manifests | Where-Object { -not $_.tags -or $_.tags.Count -eq 0 }).Count
$lastPush = ($manifests | Sort-Object -Property lastUpdateTime -Descending | Select-Object -First 1).lastUpdateTime
$results += [PSCustomObject]@{
RegistryName = $registry.Name
ResourceGroup = $registry.ResourceGroupName
Repository = $repo
TaggedImages = $taggedCount
UntaggedImages = $untaggedCount
LastPushedUtc = $lastPush
}
}
}
$results | Sort-Object RegistryName, Repository | Format-Table -AutoSize
$staleUntagged = $results | Where-Object { $_.UntaggedImages -gt 0 }
if ($staleUntagged) {
Write-Host ""
Write-Host "$($staleUntagged.Count) repositories have untagged manifests worth reviewing for cleanup." -ForegroundColor Yellow
}
if ($ExportPath) {
$results | Export-Csv -Path $ExportPath -NoTypeInformation
Write-Host "Report exported to $ExportPath" -ForegroundColor Green
}
Read it before you run it, and test in a safe tenant first.