Post

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.

Export Azure Advisor Service Retirement Recommendations to CSV with PowerShell

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 RetirementDate if 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.

This post is licensed under CC BY 4.0 by the author.