A PowerShell 7 script that automatically extracts backup and retention policies for all SQL databases across all Azure subscriptions.
The script collects the following data per database:
| Field | Description |
|---|---|
| PITR_RetentionDays | Number of days available for point-in-time restore |
| PITR_DiffBackupInterval | Differential backup frequency (e.g. 24 Hours) |
| LTR_WeeklyRetention | Weekly long-term retention (ISO 8601, e.g. P4W) |
| LTR_MonthlyRetention | Monthly long-term retention |
| LTR_YearlyRetention | Yearly long-term retention |
| LTR_WeekOfYear | Which week of the year the yearly backup is taken |
| BackupStorageRedund | Local, Zone or Geo |
| EarliestRestoreDate | Earliest possible restore point |
PT0S is an ISO 8601 duration string meaning "Period: 0 Seconds" — the field is not configured.
PT0S → No backup
P7D → 7 days
P4W → 4 weeks
P12M → 12 months
P1Y → 1 year
Databases with PT0S on all LTR fields are protected by PITR only (typically 7 days).
# PowerShell 7+
pwsh --version
# Az module
Install-Module -Name Az -Scope CurrentUser -Force
#Requires -Version 7.0
param(
[string]$OutputCsv = "sql_backup_report_$(Get-Date -Format 'yyyyMMdd_HHmm').csv",
[switch]$Debug
)
Connect-AzAccount -ErrorAction Stop | Out-Null
$subscriptions = Get-AzSubscription | Where-Object { $_.State -eq 'Enabled' }
Write-Host "✅ Found $($subscriptions.Count) subscription(s)" -ForegroundColor Green
$report = [System.Collections.Generic.List[PSCustomObject]]::new()
foreach ($sub in $subscriptions) {
Write-Host "`n📦 [$($sub.Name)]" -ForegroundColor Yellow
Set-AzContext -SubscriptionId $sub.Id -ErrorAction Stop | Out-Null
$servers = Get-AzSqlServer -ErrorAction SilentlyContinue
if (-not $servers) { Write-Host " ↳ No SQL servers" -ForegroundColor DarkGray; continue }
foreach ($server in $servers) {
$rg = $server.ResourceGroupName
$serverName = $server.ServerName
Write-Host " 🖥 $serverName [$rg]" -ForegroundColor White
$databases = Get-AzSqlDatabase -ServerName $serverName -ResourceGroupName $rg -ErrorAction SilentlyContinue |
Where-Object { $_.DatabaseName -ne 'master' }
foreach ($db in $databases) {
Write-Host " └─ $($db.DatabaseName)" -ForegroundColor DarkCyan
# PITR + Differential frequency
$pitrDays = 'N/A'
$diffInterval = 'N/A'
try {
$pitr = Get-AzSqlDatabaseBackupShortTermRetentionPolicy `
-ServerName $serverName `
-ResourceGroupName $rg `
-DatabaseName $db.DatabaseName `
-ErrorAction Stop
$pitrDays = $pitr.RetentionDays
$diffInterval = "$($pitr.DiffBackupIntervalInHours) Hours"
if ($Debug) {
Write-Host " PITR: $($pitr | ConvertTo-Json -Compress)" -ForegroundColor DarkMagenta
}
}
catch {
Write-Host " ⚠️ PITR error: $_" -ForegroundColor DarkYellow
}
# LTR
$ltr = $null
try {
$ltr = Get-AzSqlDatabaseLongTermRetentionPolicy `
-ServerName $serverName `
-ResourceGroupName $rg `
-DatabaseName $db.DatabaseName `
-ErrorAction Stop
}
catch {
Write-Host " ⚠️ LTR error: $_" -ForegroundColor DarkYellow
}
$report.Add([PSCustomObject]@{
SubscriptionName = $sub.Name
SubscriptionId = $sub.Id
ResourceGroup = $rg
ServerName = $serverName
Location = $server.Location
DatabaseName = $db.DatabaseName
Edition = $db.Edition
ServiceObjective = $db.CurrentServiceObjectiveName
Status = $db.Status
MaxSizeGB = [math]::Round($db.MaxSizeBytes / 1GB, 2)
PITR_RetentionDays = $pitrDays
PITR_DiffBackupInterval = $diffInterval
LTR_WeeklyRetention = $ltr?.WeeklyRetention ?? 'PT0S'
LTR_MonthlyRetention = $ltr?.MonthlyRetention ?? 'PT0S'
LTR_YearlyRetention = $ltr?.YearlyRetention ?? 'PT0S'
LTR_WeekOfYear = $ltr?.WeekOfYear ?? 'N/A'
BackupStorageRedund = $db.BackupStorageRedundancy ?? 'Unknown'
CreationDate = $db.CreationDate?.ToString('yyyy-MM-dd') ?? ''
EarliestRestoreDate = $db.EarliestRestoreDate?.ToString('yyyy-MM-dd HH:mm') ?? ''
ReportedAt = (Get-Date -Format 'yyyy-MM-dd HH:mm')
})
}
}
}
$sorted = $report | Sort-Object SubscriptionName, ServerName, DatabaseName
Write-Host "`n📊 Total: $($sorted.Count) databases" -ForegroundColor Cyan
$sorted | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8
Write-Host "✅ Saved: $OutputCsv`n" -ForegroundColor Green
$sorted | Format-Table SubscriptionName, ServerName, DatabaseName,
PITR_RetentionDays, PITR_DiffBackupInterval,
LTR_WeeklyRetention, LTR_YearlyRetention,
BackupStorageRedund -AutoSize
# Standard run
pwsh ./Get-AzSqlBackupReport.ps1
# With debug output (shows raw PITR object)
pwsh ./Get-AzSqlBackupReport.ps1 -Debug
# Custom output filename
pwsh ./Get-AzSqlBackupReport.ps1 -OutputCsv "my_report.csv"
Automatically enabled on all Azure SQL databases. Default is 7 days, maximum is 35 days. Recommended minimum for production databases: 14 days.
Must be configured manually. Used for compliance and long-term storage for up to 10 years.
# Example: Set weekly LTR retention to 4 weeks
Set-AzSqlDatabaseBackupLongTermRetentionPolicy `
-ServerName "my-server" `
-ResourceGroupName "my-rg" `
-DatabaseName "my-db" `
-WeeklyRetention "P4W"
| Value | Description | Recommendation |
|---|---|---|
Local |
Same datacenter only | Dev / test |
Zone |
Availability zones | Production |
Geo |
Secondary Azure region | Business critical |
PITR returns N/A
Verify that the service principal or user account has the role permission Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies/read.
Run with -Debug to see the exact error object printed directly in the terminal.
Last updated: 2026-04-13