---
title: "Is Your Azure Service Retiring? Build a PowerShell Monitor"
description: "Build a PowerShell monitor that queries Azure Resource Graph to identify retiring services and alert your team before 90-day deadlines become 9-day emergencies."
canonical: "https://adamtheautomator.com/azure-service-retiring-build-powershell-monitor-2/"
---

# Is Your Azure Service Retiring? Build a PowerShell Monitor

> Build a PowerShell monitor that queries Azure Resource Graph to identify retiring services and alert your team before 90-day deadlines become 9-day emergencies.

Source: https://adamtheautomator.com/azure-service-retiring-build-powershell-monitor-2/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Is Your Azure Service Retiring? Build a PowerShell Monitor](https://adamtheautomator.com/wp-content/uploads/2026/02/featured_image-4.webp)

# Is Your Azure Service Retiring? Build a PowerShell Monitor

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)9 February 20267 min. read

Categories: [Cloud](/category/cloud/)

Tags:[Azure](/tag/azure/)[Azure Automation](/tag/azure-automation/)[Azure PowerShell](/tag/azure-powershell/)

Table of Contents

*   [Prerequisites](#prerequisites)
*   [Understanding Azure’s Retirement Data Model](#understanding-azures-retirement-data-model)
*   [Query Service Health for Retirement Announcements](#query-service-health-for-retirement-announcements)
*   [Query Advisor for Impacted Resources](#query-advisor-for-impacted-resources)
*   [Handling Pagination for Large Tenants](#handling-pagination-for-large-tenants)
*   [Enriching Results with the Azure EOL Repository](#enriching-results-with-the-azure-eol-repository)
*   [Building the Monitor Script](#building-the-monitor-script)
*   [Automating with Azure Automation](#automating-with-azure-automation)
*   [Alerting via Microsoft Teams](#alerting-via-microsoft-teams)
*   [From Reactive Emails to Proactive Governance](#from-reactive-emails-to-proactive-governance)

You deployed a service to Azure three months ago. It works. Traffic flows. Nobody complains. Then you get an email: “Your Azure service will be retired in 90 days. Migrate immediately.” And you have no idea which service they’re talking about because your environment has 400 resources spread across six subscriptions.

Sound familiar? Azure operates on a continuous lifecycle model where services get upgraded or deprecated without warning your entire team. The [Service Retirement workbook](https://learn.microsoft.com/en-us/azure/advisor/advisor-workbook-service-retirement) provides a centralized view, but checking a portal dashboard every week isn’t automation—it’s just a faster way to miss deadlines.

Here’s how to build a PowerShell monitor that queries [Azure Resource Graph (ARG)](https://learn.microsoft.com/en-us/azure/governance/resource-graph/overview), identifies retiring services, and alerts your team before those 90 days become 9.

If you are building service-retirement monitors as part of a larger cloud automation skill set, compare [Udemy technical training courses for PowerShell, cloud, and security skills](https://trk.udemy.com/c/1454808/3193860/39854) before paying for another course library.

## Prerequisites

You’ll need:

*   Azure PowerShell module (`Az.ResourceGraph`)
    
*   Reader access to the subscriptions you want to monitor
    
*   A service principal or [managed identity](https://learn.microsoft.com/en-us/azure/automation/enable-managed-identity-for-automation) (Azure’s way of giving automation accounts their own credentials) if running in Azure Automation
    

Verify your environment:

```powershell
Get-Module -ListAvailable Az.ResourceGraph
```

If it’s not installed:

```powershell
Install-Module -Name Az.ResourceGraph -Scope CurrentUser
```

Authenticate to Azure:

```powershell
Connect-AzAccount
```

* * *

**_Pro Tip: If you’re planning to run this as an automated runbook, use a system-assigned managed identity instead of interactive authentication. Grant the identity Reader access at the management group level to query across all subscriptions._**

* * *

## Understanding Azure’s Retirement Data Model

Azure doesn’t have a single “retirement API.” Retirement data is scattered across two distinct systems, and you need to query both to get the full picture.

| Data Source | What It Tells You | Table in ARG |
| --- | --- | --- |
| **Azure Service Health** | High-level retirement announcements (e.g., “API version X is deprecated”) | `ServiceHealthResources` |
| **Azure Advisor** | Specific resources impacted by retirements | `AdvisorResources` |

[Service Health](https://learn.microsoft.com/en-us/azure/service-health/service-health-portal-update) tracks the event itself—the announcement that something is retiring. [Azure Advisor](https://learn.microsoft.com/en-us/azure/advisor/advisor-how-to-use-service-upgrade-retirement-recommendations) scans your deployed resources and flags which ones are actually affected.

Both are queryable via [Azure Resource Graph](https://learn.microsoft.com/en-us/azure/service-health/azure-resource-graph-overview), Microsoft’s cross-subscription query engine that uses [Kusto Query Language (KQL)](https://learn.microsoft.com/en-us/kusto/query/). Think of ARG as SQL for your entire Azure environment—subscriptions, resource groups, compliance data, and service health events.

## Query Service Health for Retirement Announcements

Start by finding all active retirement events across your tenant. The `ServiceHealthResources` table contains health advisories, and retirements are a specific event subtype.

```powershell
$query = @"
ServiceHealthResources
| where type =~ 'Microsoft.ResourceHealth/events'
| extend eventType = properties.EventType
| extend eventSubType = properties.EventSubType
| where eventType == 'HealthAdvisory' and eventSubType == 'Retirement'
| project TrackingId=properties.TrackingId,
          Title=properties.Title,
          ImpactStartTime=properties.ImpactStartTime
"@

$retirementEvents = Search-AzGraph -Query $query
$retirementEvents | Format-Table
```

This returns every retirement announcement Azure has issued that’s still within the 60-day retention window. Yes, 60 days—not 90. [Retirement notifications in Service Health are retained for 60 days](https://github.com/Azure/EOL#service-health), so if you want historical tracking, you’ll need to export this data to [Log Analytics](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/log-analytics-overview) or a storage account.

The output includes:

*   **TrackingId**: Unique identifier for the event
    
*   **Title**: Human-readable description of the retirement
    
*   **ImpactStartTime**: When the retirement was announced
    

This tells you _what_ is retiring. It doesn’t tell you _which of your resources_ are impacted.

## Query Advisor for Impacted Resources

To find out which specific VMs, storage accounts, or app services need migration, query the `AdvisorResources` table. Advisor scans your environment and generates recommendations for resources that match retirement signatures.

```powershell
$impactQuery = @"
advisorresources
| where type == 'microsoft.advisor/recommendations'
| where properties.category == 'Reliability'
| where properties.extendedProperties.recommendationSubCategory == 'ServiceUpgradeAndRetirement'
| extend retirementFeatureName = properties.extendedProperties.retirementFeatureName
| extend retirementDate = properties.extendedProperties.retirementDate
| extend resourceId = properties.resourceMetadata.resourceId
| project retirementFeatureName, retirementDate, resourceId
"@

$impactedResources = Search-AzGraph -Query $impactQuery
$impactedResources | Format-Table
```

This returns every resource ID flagged for retirement, along with the feature name and deadline. The query uses `extendedProperties` fields that contain retirement-specific metadata—these properties may not be populated consistently across all retirement scenarios, but they provide the most detailed information when available.

* * *

**_Reality Check: The Advisor data isn’t exhaustive. It covers approximately 40 services and features—primarily infrastructure retirements like VM series or API versions. If you’re using a niche preview service, you might not get advance warning here._**

* * *

## Handling Pagination for Large Tenants

If your environment has hundreds of flagged resources, `Search-AzGraph` will truncate results at 1,000 records. You need to implement pagination using the `-SkipToken` parameter.

Here’s how to retrieve all results:

```powershell
$allResults = @()
$skipToken = $null

do {
    if ($skipToken) {
        $response = Search-AzGraph -Query $impactQuery -First 1000 -SkipToken $skipToken
    } else {
        $response = Search-AzGraph -Query $impactQuery -First 1000
    }

    $allResults += $response
    $skipToken = $response.SkipToken
} while ($skipToken)

$allResults | Format-Table
```

This loop continues querying until ARG stops returning a skip token, meaning you’ve retrieved the full dataset. Skip this step if you’re confident your tenant has fewer than 1,000 impacted resources. (You probably don’t know that number off the top of your head, which is exactly why you’re building this monitor.)

## Enriching Results with the Azure EOL Repository

Microsoft maintains an [open-source EOL repository on GitHub](https://github.com/Azure/EOL) containing a JSON list of all Azure retirements. This file includes migration links and human-readable descriptions that aren’t always present in the ARG query results. The repository serves as a single source of truth for retirement documentation—something that becomes critical when you’re trying to explain to management why that production VM needs to move by next quarter.

Download the list:

```powershell
$eolUrl = "https://raw.githubusercontent.com/Azure/EOL/main/service_list.json"
$eolData = Invoke-RestMethod -Uri $eolUrl
```

Now you can cross-reference the ARG results with the EOL data to add migration guidance:

```powershell
$enrichedResults = $allResults | ForEach-Object {
    $feature = $_.retirementFeatureName
    $eolEntry = $eolData | Where-Object { $_.RetiringFeature -eq $feature }

    [PSCustomObject]@{
        ResourceId = $_.resourceId
        RetirementFeature = $feature
        RetirementDate = $_.retirementDate
        MigrationLink = $eolEntry.Link
    }
}

$enrichedResults | Format-Table
```

This gives you actionable data: which resources are affected, when they retire, and where to find the migration guide. The migration link is what transforms this from “we have a problem” into “here’s how to fix it”—the difference between panic and a project plan.

## Building the Monitor Script

Combine everything into a single monitor script that runs on a schedule. Save this as `Monitor-AzureRetirements.ps1`—and yes, the filename matters when your future self tries to find it six months from now.

The script performs three core operations:

| Operation | Purpose | Output |
| --- | --- | --- |
| Query Service Health | Retrieve active retirement advisories | Retirement event metadata (tracking IDs, service names) |
| Query Advisor | Identify impacted resources | Resource IDs flagged for retirement |
| Export Results | Persist data for tracking | CSV file with resource details |

```powershell
#Requires -Modules Az.ResourceGraph

Connect-AzAccount

# Query for retirement advisories
$healthQuery = @"
ServiceHealthResources
| where type =~ 'Microsoft.ResourceHealth/events'
| extend eventType = properties.EventType
| extend eventSubType = properties.EventSubType
| where eventType == 'HealthAdvisory' and eventSubType == 'Retirement'
| project TrackingId=properties.TrackingId, Title=properties.Title
"@

$retirementEvents = Search-AzGraph -Query $healthQuery

# Query for impacted resources with pagination
$impactQuery = @"
advisorresources
| where type == 'microsoft.advisor/recommendations'
| where properties.category == 'Reliability'
| where properties.extendedProperties.recommendationSubCategory == 'ServiceUpgradeAndRetirement'
| extend retirementFeatureName = properties.extendedProperties.retirementFeatureName
| extend retirementDate = properties.extendedProperties.retirementDate
| extend resourceId = properties.resourceMetadata.resourceId
| project retirementFeatureName, retirementDate, resourceId
"@

$allResources = @()
$skipToken = $null

do {
    if ($skipToken) {
        $response = Search-AzGraph -Query $impactQuery -First 1000 -SkipToken $skipToken
    } else {
        $response = Search-AzGraph -Query $impactQuery -First 1000
    }
    $allResources += $response
    $skipToken = $response.SkipToken
} while ($skipToken)

# Output results
Write-Host "Found $($retirementEvents.Count) retirement events"
Write-Host "Found $($allResources.Count) impacted resources"

$allResources | Export-Csv -Path "AzureRetirements.csv" -NoTypeInformation
```

Run it:

```
.\Monitor-AzureRetirements.ps1
```

Check the output file:

```powershell
Import-Csv AzureRetirements.csv | Format-Table
```

## Automating with Azure Automation

Running this manually defeats the purpose. Deploy it as an Azure Automation runbook that executes daily. If you’re still logging into the portal to run scripts in 2026, you’re not automating—you’re just clicking with more steps.

Create a runbook:

```powershell
$automationAccount = "MyAutomationAccount"
$resourceGroup = "MyResourceGroup"

New-AzAutomationRunbook -Name "Monitor-Retirements" `
    -Type PowerShell `
    -AutomationAccountName $automationAccount `
    -ResourceGroupName $resourceGroup
```

Upload the script:

```powershell
Import-AzAutomationRunbook -Path ".\Monitor-AzureRetirements.ps1" `
    -Name "Monitor-Retirements" `
    -Type PowerShell `
    -AutomationAccountName $automationAccount `
    -ResourceGroupName $resourceGroup `
    -Force
```

Publish it:

```powershell
Publish-AzAutomationRunbook -Name "Monitor-Retirements" `
    -AutomationAccountName $automationAccount `
    -ResourceGroupName $resourceGroup
```

Schedule it to run daily:

```powershell
$schedule = New-AzAutomationSchedule -Name "DailyRetirementCheck" `
    -AutomationAccountName $automationAccount `
    -ResourceGroupName $resourceGroup `
    -StartTime (Get-Date).AddHours(1) `
    -DayInterval 1

Register-AzAutomationScheduledRunbook -RunbookName "Monitor-Retirements" `
    -ScheduleName "DailyRetirementCheck" `
    -AutomationAccountName $automationAccount `
    -ResourceGroupName $resourceGroup
```

The runbook will use the automation account’s system-assigned managed identity to authenticate. Grant the identity Reader access to your management group or subscriptions—and document which identity has which permissions, because the next person inheriting this automation will thank you.

| Approach | Execution | Notification | Scalability |
| --- | --- | --- | --- |
| **Manual Script** | Interactive login required | None | Doesn’t scale beyond a single operator |
| **Scheduled Runbook** | Runs automatically daily | Requires integration (Teams, email) | Scales across entire tenant |

* * *

**_Warning: If your runbook fails with an authentication error, verify the managed identity has Reader permissions. The error message won’t tell you that—it’ll just say “unauthorized.”_**

* * *

## Alerting via Microsoft Teams

Generating a CSV file in an automation account doesn’t notify anyone. Pipe the results to a [Microsoft Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook) webhook so your engineering team actually sees the alerts. A retirement notification sitting in a storage account is just a different kind of silence—louder than nothing, but not loud enough.

Create an Incoming Webhook in your Teams channel, then add this to the runbook:

```powershell
$teamsWebhook = "https://outlook.office.com/webhook/YOUR-WEBHOOK-URL"

if ($allResources.Count -gt 0) {
    $message = @{
        text = "Azure Retirement Alert: $($allResources.Count) resources require migration"
    } | ConvertTo-Json

    Invoke-RestMethod -Method Post -Uri $teamsWebhook -Body $message -ContentType "application/json"
}
```

Now every time the script finds impacted resources, it posts a notification to Teams. Your team sees the alert before the service disappears. The real value isn’t the webhook—it’s the 90-day head start it buys you.

## From Reactive Emails to Proactive Governance

You built a PowerShell monitor that queries Azure Resource Graph for retirement advisories, identifies specific impacted resources, handles pagination for large datasets, and sends alerts to Microsoft Teams—turning a reactive email-based process into proactive governance.

Here’s what the complete solution delivers:

| Component | Data Source | Output | Business Value |
| --- | --- | --- | --- |
| **Retirement Scanner** | Service Health events via ARG | Active retirement announcements | Know what’s retiring across Azure |
| **Resource Identifier** | Advisor recommendations via ARG | Specific resource IDs flagged for migration | Know which of your resources are impacted |
| **Migration Enrichment** | Azure EOL GitHub repository | Links to official migration guides | Know how to fix the problem |
| **Automation Layer** | Azure Automation scheduled runbook | Runs daily without manual intervention | Continuous monitoring at scale |
| **Alert Distribution** | Microsoft Teams webhook | Real-time notifications to engineering team | Act on retirements before deadlines |

Azure’s continuous lifecycle model means retirements are inevitable. The difference between a forced migration at 2 AM and a planned weekend maintenance window is whether you knew 90 days in advance or 9. This monitor ensures you always know first.

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fazure-service-retiring-build-powershell-monitor-2%2F&text=Is%20Your%20Azure%20Service%20Retiring%3F%20Build%20a%20PowerShell%20Monitor)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fazure-service-retiring-build-powershell-monitor-2%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fazure-service-retiring-build-powershell-monitor-2%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/04/featured_image-10.webp)

### [Automate SOC 2 Compliance with PowerShell and Azure](/automate-soc-2-compliance-powershell-azure/)

Learn how to use Azure Policy, the EPAC framework, and PowerShell to automate SOC 2 compliance evidence collection, enforce controls across subscriptions, and build a continuous audit trail.

![](https://adamtheautomator.com/wp-content/uploads/2026/02/featured_image-4.webp)

### [Is Your Azure Service Retiring? Build a PowerShell Monitor](/azure-service-retiring-build-powershell-monitor/)

Build a PowerShell monitor that queries Azure Resource Graph to identify retiring services and alert your team before 90-day deadlines become 9-day emergencies.

![](https://adamtheautomator.com/wp-content/uploads/2026/04/featured_image-15.webp)

### [Fix Azure Cost Reporting with a FinOps Tagging Strategy](/fix-azure-cost-reporting-finops-tagging-strategy-3/)

Build an Azure resource tagging taxonomy, enforce it with Azure Policy, and automate remediation of untagged resources to enable accurate FinOps cost allocation and chargebacks.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
