Export Azure Advisor Service Retirement Recommendations to CSV with PowerShell
Export Azure Advisor service upgrade and retirement recommendations across subscriptions to CSV using Az.Advisor or Azure Resource Graph.
Azure Advisor can identify resources affected by upcoming service upgrades and retirements. These recommendations appear under the Reliability category in the portal; the API and PowerShell use HighAvailability, with recommendationSubCategory set to ServiceUpgradeAndRetirement.
The Azure Service Retirement Workbook is available in Azure Advisor and can be used to visualize service retirement recommendations. The workbook and the PowerShell scripts below use the same data source, bringing together service retirement recommendations, affected resources, and retirement details for review and migration planning. The queries below show how you can export the data to CSV for further analysis and automation.
Choose an option
Use Az.Advisor to stay with Advisor cmdlets and scan a small scope. Use Azure Resource Graph (ARG) for many subscriptions or recurring, automated exports; it can query across the tenant and page through large result sets.
Option 1: Az.Advisor
Prerequisites: the Az.Advisor module and a signed-in session via Connect-AzAccount.
This loops through subscriptions visible to the signed-in account and writes advisor-retirements.csv to the current directory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Save the current Az context so it can be restored when the script finishes
$originalContext = Get-AzContext
# Advisor filter: Reliability recommendations (the API calls this category HighAvailability)
$filter = "Category eq 'HighAvailability'"
# Loop through every subscription the signed-in account can see
$rows = foreach ($sub in Get-AzSubscription) {
# Point the Az session at this subscription; skip it if the switch fails
try { Set-AzContext -SubscriptionId $sub.Id -ErrorAction Stop | Out-Null }
catch { Write-Warning "Skipping $($sub.Name): $_"; continue }
# Pull all Reliability recommendations for the current subscription
foreach ($rec in Get-AzAdvisorRecommendation -Filter $filter) {
# ExtendedProperty holds the retirement details. Depending on the Az.Advisor
# version it is a JSON string, an object with AdditionalProperties, or a plain
# object, so normalize it into something we can read with dot notation
$ext = $rec.ExtendedProperty
if ($ext -is [string]) { $ext = $ext | ConvertFrom-Json }
elseif ($ext -and $ext.AdditionalProperties) { $ext = $ext.AdditionalProperties }
# Keep only Service Upgrade and Retirement recommendations; skip everything else
if (-not ($ext -and $ext.recommendationSubCategory -eq 'ServiceUpgradeAndRetirement')) { continue }
# Full ARM resource ID of the impacted resource, used to derive name, type and RG
$rid = $rec.ResourceMetadataResourceId
# Emit one row per recommendation; these become the CSV columns
[pscustomobject]@{
SubscriptionName = $sub.Name
SubscriptionId = $sub.Id
ResourceId = $rid
ResourceName = if ($rid) { ($rid -split '/')[-1] } # last segment of the ID
ResourceType = if ($rid -match '/providers/([^/]+/[^/]+)(?:/|$)') { $matches[1] } # e.g. Microsoft.Compute/virtualMachines
ResourceGroup = if ($rid -match '/resourceGroups/([^/]+)') { $matches[1] } # resource group name
Impact = $rec.Impact
Problem = $rec.ShortDescriptionProblem
Solution = $rec.ShortDescriptionSolution
RetiringFeature = $ext.retirementFeatureName # feature or service being retired
RetirementDate = $ext.retirementDate
DisplayName = $ext.displayName
LastUpdated = $rec.LastUpdated
RecommendationId = $rec.Name
LearnMoreLink = $rec.LearnMoreLink
}
}
}
# Put the session back on whichever subscription it was using before
if ($originalContext) { Set-AzContext -Context $originalContext | Out-Null }
# Sort by retirement date (soonest first, if dates are ISO-style text) and write the CSV
$rows | Sort-Object RetirementDate | Export-Csv .\advisor-retirements.csv -NoTypeInformation
Option 2: Azure Resource Graph
Prerequisites: the Az.ResourceGraph module and a signed-in Azure session.
This runs a tenant-scoped ARG query, pages through results, and writes advisor-retirements-arg.csv:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# One-time: install the Az.ResourceGraph module if you don't have it
# Install-Module Az.ResourceGraph -Scope CurrentUser
# Map subscription IDs to names so the CSV is readable (ARG only returns IDs)
$subNames = @{}
Get-AzSubscription | ForEach-Object { $subNames[$_.Id] = $_.Name }
# KQL against the advisorresources table: Reliability recommendations in the
# Service Upgrade and Retirement subcategory, with fields flattened into columns
$query = @"
advisorresources
| where type == 'microsoft.advisor/recommendations'
| where properties.category == 'HighAvailability'
| where properties.extendedProperties.recommendationSubCategory == 'ServiceUpgradeAndRetirement'
| extend rid = tostring(properties.resourceMetadata.resourceId)
| project subscriptionId,
ResourceId = rid,
ResourceName = tostring(split(rid, '/')[-1]),
ResourceType = extract('(?i)/providers/([^/]+/[^/]+)', 1, rid),
ResourceGroup = extract('(?i)/resourceGroups/([^/]+)', 1, rid),
Impact = tostring(properties.impact),
Problem = tostring(properties.shortDescription.problem),
Solution = tostring(properties.shortDescription.solution),
RetiringFeature = tostring(properties.extendedProperties.retirementFeatureName),
RetirementDate = tostring(properties.extendedProperties.retirementDate),
DisplayName = tostring(properties.extendedProperties.displayName),
LastUpdated = tostring(properties.lastUpdated),
RecommendationId = name,
LearnMoreLink = tostring(properties.learnMoreLink)
| order by RetirementDate asc
"@
# Page through results: ARG returns at most 1000 rows per call and hands back a SkipToken
$results = @()
$skipToken = $null
do {
$page = if ($skipToken) {
Search-AzGraph -Query $query -First 1000 -UseTenantScope -SkipToken $skipToken
} else {
Search-AzGraph -Query $query -First 1000 -UseTenantScope
}
$results += $page
$skipToken = $page.SkipToken
} while ($skipToken)
# Add the subscription name, put the columns in a friendly order, and write the CSV
$results |
Select-Object @{n='SubscriptionName'; e={ $subNames[$_.subscriptionId] }},
@{n='SubscriptionId'; e={ $_.subscriptionId }},
ResourceId, ResourceName, ResourceType, ResourceGroup, Impact, Problem, Solution,
RetiringFeature, RetirementDate, DisplayName, LastUpdated, RecommendationId, LearnMoreLink |
Export-Csv .\advisor-retirements-arg.csv -NoTypeInformation
Notes
- Upgrade-only recommendations have no retirement date. Filter out rows with a blank
RetirementDateif you only want true retirements. - Advisor only returns recommendations with impacted resources, so this is not a complete inventory of every Azure retirement.
- Retirement dates are sorted as text. Confirm the date format in your output before relying on the order.
- The ARG resource-group regex is case-insensitive because resource ID casing can vary, including lowercase
/resourcegroups/.
Use the CSV
Each row includes the subscription, affected resource, impact, recommendation details, and retirement date. Import the file into ITSM tooling to create or update work items, assign owners, and track remediation. It can also become input to existing automations, such as a scheduled PowerShell job or pipeline that routes notifications or enriches records or feed Excel and Power BI reports for prioritization.