Azure Observability

Service Health and Change Analysis: Correlating Outages, Planned Maintenance and What Changed

The site is down. Your dashboards are red, the pager is screaming, and a Teams channel of forty people is asking the same question in eight different ways: is it us, or is it Azure? That single question governs everything that happens next. If it is Azure, your job is to communicate, wait, and maybe fail over — touching your own deployment would be actively harmful. If it is you, every minute spent waiting on Microsoft is a minute the outage continues. And the maddening truth of most incidents is that the data to answer it already exists in your subscription before you ever open a support ticket — it just lives in three different blades that most engineers never wire together until they are already on fire.

This article is about those three blades and the mental model that fuses them. Azure Service Health tells you what Microsoft is doing to the platform — active service issues (outages), planned maintenance, and health advisories — scoped to your subscriptions, regions and services rather than a generic global status page. Azure Resource Health drops one level down: is this specific resource — this VM, this SQL database, this App Service plan — healthy from the platform’s point of view, and why did it become unhealthy? Change Analysis answers the third question, the one that resolves most self-inflicted incidents: what changed? It mines the Activity Log, Azure Resource Manager (ARM) property diffs, and in-guest configuration to show every change in a time window, ranked by relevance to the resource that just broke. Wire those three to the Activity Log and an action group, and “is it us or Azure?” becomes a ninety-second lookup instead of a forty-person guessing game.

By the end you will stop opening a support ticket as your first move. You will know which blade answers each of the three questions an incident forces, how to read a Service Health tracking ID and a Resource Health transition, how to correlate a Change Analysis diff against an outage window, and how to stand it all up before the incident with alert rules that page you the moment Azure declares an issue in a region you live in. This is a concept-first article — mental models first, then comparison grids, an architecture walkthrough, and a short playbook you keep open during the call.

What problem this solves

Every incident forces three questions, in order: is it Azure?, is it me?, and what changed? Teams that cannot answer them quickly default to the worst behaviour. They restart things at random, which sometimes “fixes” the symptom and teaches the wrong lesson. They scale up, masking a real bug for a day. They open a Severity-A ticket and wait — for an outage a thirty-second look at Service Health would have confirmed is Microsoft’s, ETA already published. Or worse, they assume it is Azure when it is actually the config change someone shipped ninety minutes ago, and sit on their hands while their own bug burns.

The root cause is that the three answers live in three places that feel unrelated until enough incidents fuse them. The generic status.azure.com page is nearly useless mid-incident: broad, already-confirmed events, often lagging, knowing nothing about whether you run anything in the affected region. Service Health is the personalised version — same data, filtered to your tenant’s footprint — and the blade most teams forget exists. Resource Health is even more forgotten, yet it is the fastest way to learn the platform already knows your VM is unhealthy and why. And Change Analysis resolves the plurality of incidents, because something always changed — a deployment, an app setting, an NSG rule, an expired certificate, a scale-in that removed the last healthy instance — and correlating that change to the failure window is the whole game.

Who hits this: every team on Azure with a pager. It bites hardest on teams without a pre-wired Service Health alert (they learn about regional outages from their users, not Azure), on busy subscriptions where the Activity Log is an unread firehose, and on teams that conflate “my app returned an error” with “Azure is down” because they never separated platform health from application health. The fix is not a tool you buy — it is a model you internalise and three alert rules you deploy once.

To frame the whole field before the deep dive, here is each question an incident forces, the blade that answers it, and what a “yes” there means for your next move:

The question Primary blade What a hit there tells you Your next move if it hits
Is it Azure? Service Health → Service issues Microsoft declared an outage in your region/service Communicate + wait for ETA; consider failover; do NOT touch your deploy
Is it the platform for this resource? Resource Health Platform marks this resource Unavailable/Degraded Read the transition reason; if platform-initiated, escalate with the resource ID
What changed? Change Analysis A config/deployment/property change lines up with the failure Roll back / inspect that change first
Is maintenance the cause? Service Health → Planned maintenance A maintenance window overlaps the symptom Expect brief restarts; verify HA absorbed it
Did Azure advise me already? Service Health → Health advisories A deprecation/quota/config notice predates the symptom Action the advisory; it may be the root cause

Learning objectives

By the end of this article you can:

Prerequisites & where this fits

You should understand the Azure resource hierarchy — management groups contain subscriptions, which contain resource groups, which contain resources — because Service Health and Activity Log alerts are scoped at the subscription level, and how you scope them decides what noise you get. You should know how to run az in Cloud Shell, read JSON output, and create a basic Azure Monitor action group (the thing that emails, SMSes or webhooks you). Familiarity with the Activity Log as the subscription-level record of control-plane operations is assumed; if you have ever filtered it for a failed deployment, you have the foundation.

This sits in the Observability & Incident Management track, deliberately upstream of application-level debugging. It pairs with Azure Monitor and Application Insights: Full-Stack Observability — once Service Health says the platform is fine, Application Insights is where you prove the fault is in your code. It complements The Azure Monitor Data Platform Explained: Metrics, Logs, Traces and Where Each Lives: Service Health and Resource Health are platform signals that flow through the Activity Log, not the metrics or logs pipeline. When the answer is application-level, articles like Troubleshooting Azure App Service: 502/503 Errors, Cold Starts & Restart Loops take over. The alerting muscle you build here is the same one from How to Create Your First Metric Alert and Action Group for Email, SMS and Push.

A quick map of which signal sits at which layer, so you reach for the right one without thinking:

Layer The signal What it answers Where it surfaces Latency to you
Azure platform (global/regional) Service Health Is Microsoft having an outage / maintenance that touches me? Service Health blade + Activity Log (ServiceHealth) Minutes (personalised); slower on public status page
Azure platform (per resource) Resource Health Is this resource healthy from the platform’s view? Resource Health blade + Activity Log (ResourceHealth) Seconds to minutes
Your control-plane actions Activity Log + Change Analysis What did anyone change, and when? Activity Log; Change Analysis blade Near real-time (ARM); minutes for property diffs
Your application Metrics / Logs / App Insights Is my code/dependency failing? Azure Monitor metrics, Log Analytics, App Insights Seconds to a couple of minutes

The rule of thumb that orders everything below: climb from the platform down to your code. Service Health (is Azure broken?), then Resource Health (is this resource broken by the platform?), then Change Analysis (did I break it?), and only then application telemetry. Most teams do it backwards — start in their own logs and waste the first hour — which is the habit this article exists to break.

Core concepts

Five mental models make every later step obvious.

Platform health is not application health, and different systems report them. Your Application Insights availability test going red means a request failed — not whose fault it is. Service Health and Resource Health report the platform’s opinion of its own health, independent of your code. An app can throw 500s while every Azure signal is green (your bug), and the platform can declare a regional storage outage while your app limps along on cached data (Azure’s problem, partially absorbed). Treating these as one signal is the original sin of slow response: read the platform signal first and let it tell you which world you are in.

Service Health is the personalised projection of Azure Status. The public, anonymous page at status.azure.com shows broad, confirmed, multi-customer events. Service Health is the same incident data filtered through your tenant’s footprint — only events touching the subscriptions, regions and service types you use, often earlier and at finer granularity than the public page. When they disagree, Service Health is the more trustworthy view for you, because the public page is intentionally conservative and lagging. Never let “the status page is all green” end your investigation.

Every Service Health event is one of three types, and the type dictates your response. A service issue is an active, unplanned outage on Microsoft’s side. Planned maintenance is scheduled platform work that may briefly affect your resources (a node reboot, a control-plane upgrade). A health advisory is a notice requiring your attention — a deprecation, a quota or configuration condition — and is frequently the root cause of an incident weeks later. Each carries a globally unique tracking ID (the string you quote to support) and moves from Active/Investigating to Resolved, with a final Post-Incident Review (PIR) for major events.

Resource Health is a state machine the platform drives per resource. A resource is Available, Unavailable, Degraded, or Unknown, and each transition carries a reason: Platform Initiated (a host failure, a planned reboot), Customer Initiated (you stopped/redeployed/recycled it), or Unknown. It is the fastest way to learn the platform already knows your VM is down and why — before you tail a log. The most valuable read is the transition: “Available 02:11, Unavailable 02:14, Platform Initiated” tells you in one line that this is Azure’s fault and when it started.

Change Analysis is differential, not just a log. The Activity Log lists operations — “someone called restart”, “a deployment ran”. Change Analysis computes the before/after diff of resource properties (an NSG rule flipped Allow→Deny, an app setting whose value changed, a SKU that scaled down), reaches into supported in-guest config, and ranks changes by likely relevance to the resource you are investigating. The shift is from “what operations happened” to “what is different now versus before it broke” — because in most self-inflicted incidents a specific property changed at a specific time, and lining that timestamp up against the failure window is the fastest confirmation you will get.

The vocabulary in one table

Before the deep sections, pin down every moving part. The glossary at the end repeats these for lookup; this table is the model side by side:

Term One-line definition Where it lives Why it matters in an incident
Service Health Personalised Azure platform status for your tenant Service Health blade; Activity Log ServiceHealth Answers “is it Azure?” scoped to what you run
Azure Status Public, anonymous, global status page status.azure.com Conservative/lagging; not tenant-aware
Resource Health The platform’s health verdict for one resource Resource Health blade; Activity Log ResourceHealth Answers “is the platform breaking this resource?”
Change Analysis Ranked differential view of what changed and when Change Analysis blade; App Service & VM diagnostics Answers “what changed?” — resolves most self-inflicted incidents
Activity Log Subscription record of control-plane operations Monitor → Activity Log The substrate health + change records flow through
Tracking ID Globally unique ID for a Service Health event On every Service Health event The string you quote to support
Service issue An active, unplanned Microsoft-side outage Service Health event type Communicate + wait/failover; don’t touch your deploy
Planned maintenance Scheduled platform work that may affect you Service Health event type Expect brief restarts; verify HA absorbed it
Health advisory A notice requiring your attention/action Service Health event type Often the hidden root cause weeks later
PIR (Post-Incident Review) Microsoft’s root-cause write-up for a major event Service Health, after resolution The authoritative “what happened” for your RCA
Action group The notification target (email/SMS/webhook) Azure Monitor Turns an alert into a page that reaches a human
reasonType Classifier on a Resource Health transition Resource Health event Platform- vs Customer- vs Unknown-initiated

Service Health: your personalised platform status

Service Health is the blade you open first, every time, before you touch anything you own. It is organised into the three event types, plus a Resource Health entry point and a Health alerts management page. Treat it as the top of your funnel: if Service Health has an active issue covering your region and service, your job changes from “debug” to “communicate and decide on failover.”

Reading a Service Health event

Every event carries a consistent shape; reading it fast is the skill. The fields that matter, what they tell you, and the trap in each:

Field What it tells you How to use it The trap
Event type Service issue / Planned maintenance / Health advisory Decides your whole response posture Assuming “issue” when it is an advisory you ignored
Tracking ID Globally unique event identifier Quote to support; paste in incident channel Reusing a stale ID from a prior, resolved event
Impacted services Which Azure services are affected Confirm it matches the service you run Adjacent service named, not yours — not your cause
Impacted regions Which regions are affected Confirm it overlaps where you deploy Affected region is one you do not use
Status / stage Investigating → Identified → Mitigating → Resolved Sets expectations; do not wait silently Treating “Investigating” as “nothing is wrong yet”
Issue summary / updates Microsoft’s running commentary + ETA Communicate the ETA to stakeholders Acting on the first update before it is “Identified”
Last update / next update When the next official update is due Time your own comms to it Refreshing the page instead of trusting the cadence

Querying Service Health from the CLI during an incident

The portal is fine, but during a call you often want a one-liner you can paste. Service Health events flow through the Activity Log under the ServiceHealth category, so you can pull active platform events without leaving the terminal:

# Active platform service-health events in the last 6 hours for this subscription
az monitor activity-log list \
  --offset 6h \
  --query "[?category.value=='ServiceHealth'].{time:eventTimestamp, level:level, status:status.value, title:properties.title}" \
  -o table

To see only the unplanned outages (service issues) and grab their tracking IDs to quote:

# Service issues only, with the tracking ID and impacted services if present
az monitor activity-log list --offset 6h \
  --query "[?category.value=='ServiceHealth' && properties.incidentType=='Incident'].{time:eventTimestamp, tracking:properties.trackingId, title:properties.title, region:properties.region}" \
  -o table

A note on what incidentType values you will see and how they map to the three event types — this is the join between the CLI view and the portal labels:

incidentType (Activity Log) Portal event type Meaning
Incident Service issue Active unplanned outage
Maintenance Planned maintenance Scheduled platform work
Informational / ActionRequired Health advisory Notice requiring your attention
Security Security advisory Security-relevant notice for your services

Why the public status page is not enough

The single most expensive Service Health mistake is ending an investigation because status.azure.com is green. The public page is global, conservative, and cannot know whether you run anything in the affected region; Service Health, filtered to your tenant, frequently shows a relevant event earlier and at finer granularity (a single zone, a single service) that never rises to the public page’s threshold. The comparison that should settle which one you trust:

Property Public Azure Status Service Health (your tenant)
Scope Global, all customers Your subscriptions / regions / services
Personalisation None Filtered to your footprint
Granularity Broad, customer-wide events Down to specific zones/services you use
Latency Conservative, can lag Often earlier for events that touch you
Alerting Not actionable per-tenant Drives Service Health alerts → action groups
Trust during your incident Lower (may show nothing) Higher (knows what you run)

Resource Health: is this resource healthy?

When Service Health is green but one workload misbehaves, Resource Health is the next stop and it is criminally underused. It is the platform’s own verdict on a single resource — your VM, SQL database, App Service plan, load balancer — and it answers what your logs cannot: does Azure itself think this thing is healthy? The value is speed: instead of inferring “the VM might be down” from failed pings, you read the platform saying “Unavailable since 02:14, Platform Initiated” directly.

The health states and what each means

Resource Health reports one of four states — the meaning differs by resource type but follows a consistent pattern:

State What it means Typical cause What you should do
Available Platform considers the resource healthy Normal operation If your app still fails, the fault is yours (app/config)
Unavailable Platform cannot reach/serve the resource Host failure, platform reboot, or you stopped it Read reasonType; if Platform Initiated, it is Azure’s
Degraded Working but impaired (reduced performance/redundancy) Partial host issue, throttling, failover in progress Expect reduced capacity; corroborate with metrics
Unknown Platform has not received recent health signals Networking to the host lost; resource just created/stopped Wait briefly; persistent Unknown can itself be a symptom

Reading a transition and its reason

The most valuable artifact is the transition — the moment a resource moved between states, with a timestamp and a classified reason. That reasonType is the fork that tells you whose problem it is:

reasonType Meaning Whose fault Example
Platform Initiated Microsoft’s platform caused the change Azure Unplanned host failure; planned node reboot during maintenance
Customer Initiated An action you took caused it You You stopped the VM; a redeploy; a config change recycled it
Unknown Platform cannot classify the cause Indeterminate Health signal lost; treat as needs-investigation

Pull Resource Health history for a resource from the CLI — these timestamps are what you line up against your failure window and Change Analysis diff:

# Resource Health events for one resource over the last 12 hours
RES_ID=$(az vm show -g rg-shop-prod -n vm-web-01 --query id -o tsv)
az monitor activity-log list --offset 12h \
  --query "[?category.value=='ResourceHealth' && resourceId=='$RES_ID'].{time:eventTimestamp, status:properties.currentHealthStatus, previous:properties.previousHealthStatus, reason:properties.cause}" \
  -o table

A worked read: previousHealthStatus=Available, currentHealthStatus=Unavailable, cause=PlatformInitiated at 02:14 tells you (a) the resource was fine three minutes before the page, (b) the platform broke it, and © the exact start time to correlate — three facts in one line, redirecting you away from your own logs and toward Service Health or a support escalation with the resource ID and timestamp in hand.

Where Resource Health helps and where it does not

Resource Health is precise about platform health and silent about everything else. It is great for confirming the platform stopped or rebooted your VM, spotting a Platform-Initiated reboot during maintenance, distinguishing “you stopped it” from “Azure stopped it”, and giving a fast verdict before you tail any logs. It will not tell you that your app threw an exception, that a downstream API is failing, that your code deadlocked while the host is fine, or that a config change degraded behaviour without breaking the host.

The key reading: Resource Health = Available does not mean your app is healthy. It means the platform is doing its job; the fault, if any, is above it — your code, config, a dependency. That distinction sends you to Change Analysis next instead of opening a support ticket.

Change Analysis: what changed?

If Service Health is green and Resource Health says Available, the incident is almost certainly self-inflicted — and Change Analysis finds the culprit fastest. Its premise: something changed right before this broke, and I can see exactly what and when. It draws on three sources and presents them as ranked, time-stamped diffs rather than a raw operation log.

The three layers Change Analysis sees

Change Analysis composes signals from three depths; which layer a change came from tells you how to undo it:

Layer What it captures Source Latency Example change it surfaces
ARM operations Control-plane create/update/delete on resources Activity Log / Resource Graph Near real-time A deployment updated the App Service; a SKU scaled down
Resource property diffs Before/after values of resource properties ARM resource snapshots Minutes NSG rule flipped Allow→Deny; app setting value changed
In-guest configuration File/setting changes inside supported compute Change Analysis agent (where enabled) Minutes A config file edited inside a VM/App Service

The reading discipline: an ARM operation tells you who did what action; a property diff tells you what value is now different; an in-guest change tells you what someone edited inside the box. Most “we changed nothing” incidents resolve at the property-diff layer, because the operation looked routine (“update site config”) but the diff reveals the one value that mattered.

Driving Change Analysis

The portal entry points are the Change Analysis blade (tenant-wide) and the Diagnose and solve problems → Changes view inside App Service and VM resources. To list changes programmatically, lean on the Activity Log for the operation layer mid-incident:

# State-changing operations on one resource in the failure window — the shortlist you scan
RES_ID=$(az webapp show -g rg-shop-prod -n app-shop-prod --query id -o tsv)
az monitor activity-log list --offset 2h \
  --query "[?resourceId=='$RES_ID' && operationName.value contains 'write'].{time:eventTimestamp, op:operationName.localizedValue, by:caller}" \
  -o table

For property-level diffs and ranked changes, the dedicated Change Analysis blade is authoritative — it shows the old value → new value per property, which the raw Activity Log does not. Treat the CLI as your fast “what operations happened” pass and the blade as the “what value is different” deep dive.

Correlating a change to an outage

The correlation itself is a discipline, not a tool — the procedure that turns three timestamps into a verdict:

Step What you do What you are looking for
1. Fix the failure window From your alert/metrics, note when the symptom started A precise “broke at HH:MM” anchor
2. Pull Resource Health transitions Note any Available→Unavailable/Degraded and its reason A platform transition near the anchor (rules Azure in/out)
3. List changes in a window straddling the anchor Change Analysis / Activity Log for the 30–60 min before A change whose timestamp precedes and is close to the anchor
4. Inspect the top-ranked diff Open the property diff for the closest relevant change A value that plausibly causes this symptom
5. Decide Roll back that change, or rule it out and move on A confirmed cause or a clean exclusion

The trap: correlation in time is not proof of cause — a change an hour before the symptom, or on an unrelated resource, is coincidence. The ranking and the plausibility of the diff (does flipping this value actually produce this symptom?) elevate correlation to confirmation. A scale-in that removed your last healthy instance at 02:13 is a culprit for an 02:14 outage; a tag edit at 01:10 is noise.

Wiring it up before the incident: alerts and action groups

Everything above is reactive. The real win is proactive — Azure can page you the moment it declares an issue, so you learn about a regional outage from Microsoft, not your users. Two alert types, one action group, deployed once.

Service Health alerts

A Service Health alert is an Activity Log alert filtered to the ServiceHealth category, scoped to the event types, services and regions you care about, routed to an action group. The conditions you can filter on:

Condition Values Why you filter on it
Event type Service issue / Planned maintenance / Health advisory / Security Page hard on service issues; digest advisories
Services The Azure services you run (e.g. App Service, SQL DB) Suppress noise from services you do not use
Regions Regions you deploy in Ignore outages in regions you have no footprint in
Action group Email / SMS / webhook / ITSM / Logic App Where the page actually lands

Create one from the CLI by targeting the ServiceHealth Activity Log category:

# An action group that emails + SMSes the on-call
az monitor action-group create \
  --name ag-oncall --resource-group rg-monitoring \
  --short-name oncall \
  --action email primary h.vinod@example.com \
  --action sms primary 91 9000000000

# A Service Health alert for service issues in two regions, routed to that group
az monitor activity-log alert create \
  --name alert-service-health \
  --resource-group rg-monitoring \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --condition category=ServiceHealth and properties.incidentType=Incident \
  --action-group ag-oncall \
  --description "Page on-call on any Azure service issue in our regions"

The same as Bicep — how you should actually ship it, version-controlled and consistent across subscriptions:

resource ag 'Microsoft.Insights/actionGroups@2023-01-01' = {
  name: 'ag-oncall'
  location: 'global'
  properties: {
    groupShortName: 'oncall'
    enabled: true
    emailReceivers: [
      { name: 'primary', emailAddress: 'h.vinod@example.com', useCommonAlertSchema: true }
    ]
    smsReceivers: [
      { name: 'primary', countryCode: '91', phoneNumber: '9000000000' }
    ]
  }
}

resource serviceHealthAlert 'Microsoft.Insights/activityLogAlerts@2020-10-01' = {
  name: 'alert-service-health'
  location: 'global'
  properties: {
    enabled: true
    scopes: [ subscription().id ]
    condition: {
      allOf: [
        { field: 'category', equals: 'ServiceHealth' }
        { field: 'properties.incidentType', equals: 'Incident' } // service issues
      ]
    }
    actions: {
      actionGroups: [ { actionGroupId: ag.id } ]
    }
  }
}

Resource Health alerts

A Resource Health alert pages you when a resource transitions health state — a production VM going from Available to Unavailable. The conditions worth filtering on so you page on real problems, not your own deploys:

Condition Typical setting Reason
Current status Unavailable, Degraded Page on bad states, not on recovery
Previous status Available Catch the transition into trouble
Reason type Platform Initiated (or any) Page hard on Azure-caused; optionally include all
Resource scope Production subscription / RG Avoid paging on dev churn
az monitor activity-log alert create \
  --name alert-resource-health-prod \
  --resource-group rg-monitoring \
  --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/rg-shop-prod" \
  --condition category=ResourceHealth \
  --action-group ag-oncall \
  --description "Page when a prod resource changes health state"

A design note that saves you from alert fatigue: scope Resource Health alerts to production resource groups, filter on previous=Available so you catch the drop and not the noisy recovery, and decide deliberately about Customer Initiated transitions — including them pages you on every redeploy (usually wrong for on-call, useful in an audit channel). The two alert types compared:

Aspect Service Health alert Resource Health alert
Fires on Microsoft platform events for your tenant A specific resource changing health state
Scope Subscription (services/regions) Resource / resource group / subscription
Best for “Azure has an outage in my region” This VM/DB just went Unavailable”
Underlying category Activity Log ServiceHealth Activity Log ResourceHealth
Tuning lever Event type + service + region Current/previous status + reason type + scope
Risk if untuned Noise from services/regions you do not use Paging on your own redeploys

Architecture at a glance

Picture the signal flow left to right, the order you read it in during an incident. On the far left is the Azure platform — the regional control plane and the physical hosts your resources run on. An unplanned problem or scheduled maintenance becomes a Service Health event scoped to your tenant; an affected host or resource emits a Resource Health transition for that exact resource. Both, plus every control-plane operation anyone performs (the deployments, config changes and scale operations that Change Analysis mines), converge into one place: the subscription Activity Log. The Activity Log is the spine — Service Health, Resource Health and change records are all categories within it, which is why a single alerting mechanism watches all of them.

From the Activity Log, two paths fan out. The proactive path runs through Activity Log alert rules — one on ServiceHealth, one on ResourceHealth — into an action group that pages a human the instant Azure declares trouble, before any user notices. The reactive path is the human at 02:14 working the blades in order: Service Health to rule Azure in or out, Resource Health to check this resource, Change Analysis to find the diff that broke it, then Application Insights for application-level proof. The numbered badges mark where each question gets answered and where a misread sends you down the wrong path — trusting the public status page over Service Health, or paging on your own redeploys because a Resource Health alert was left untuned.

Left-to-right architecture of Azure incident correlation: the Azure platform emits Service Health events and per-resource Resource Health transitions which, alongside control-plane change records mined by Change Analysis, all converge into the subscription Activity Log; from there a proactive path runs through Service Health and Resource Health alert rules into an action group that pages the on-call, while a reactive path walks the on-call through Service Health, Resource Health, Change Analysis and finally Application Insights, with numbered badges marking where each incident question is answered and where a misread costs time

The shape to remember: platform signals and your own change records share one substrate (the Activity Log), and from it you build both the automatic page and the manual playbook. Get that picture; the rest is detail.

Real-world scenario

Northwind Retail runs its e-commerce platform on Azure across two regions — Central India (primary) and South India (DR) — with the storefront on App Service, the catalogue on Azure SQL Database, and Front Door in front. One Tuesday at 02:14 their synthetic monitor went red: intermittent 503s on the storefront in Central India. The on-call SRE, Priya, was paged — not by Azure (Northwind had no Service Health alert) but by their own Application Insights availability test. The Teams channel lit up with the inevitable question: is it us or Azure?

Priya’s first instinct, like most teams, was to dive into App Service logs. Twenty minutes evaporated reading 503s that said the front end had no healthy worker but not why. Then her manager asked the question that reset the investigation: “What does Service Health say?” Priya opened the blade. There was an active service issue on Azure SQL Database capacity, scoped in her view to a dependency her primary region leaned on — but it did not explain storefront 503s, which were App Service, not SQL. Service Health had ruled out a clean “Azure SQL is down in your region” story.

So she climbed the funnel properly. Resource Health on the App Service plan: Available. That single green verdict redirected her — if the platform says the plan is healthy, the 503 (“no healthy worker”) is caused by something above the platform. She opened Change Analysis on the App Service, set the window to 01:30–02:14, and there was the culprit, ranked at the top: at 02:11 an autoscale rule had executed a scale-in, with a property diff showing the instance count dropping from 3 to 1 — three minutes before the symptom. A second change a minute later showed a slot swap that had not warmed the remaining instance. The 503s were not Azure; they were a scale-in that removed capacity at the exact moment a swap left the lone survivor cold.

The fix took four minutes: bump the autoscale minimum instance count from 1 to 2 so a scale-in could never zero out healthy capacity, and re-enable slot warm-up. The 503s cleared. The second half of the incident — from “what does Service Health say?” to resolved — took eleven minutes; the wasted first half had taken twenty. The retro produced two permanent changes: a Service Health alert and a Resource Health alert wired to the on-call action group, and a standing rule that the playbook starts at Service Health, never in application logs. Three months later a genuine storage health advisory arrived days before it would have bitten — they actioned it in business hours, and it never became an incident. The lesson is the thesis of this article: climb from the platform down, and wire Azure to tell you first.

Advantages and disadvantages

Standing up Service Health, Resource Health and Change Analysis as a correlation discipline is high-leverage and nearly free, but not a silver bullet. The honest trade-off:

Advantages Disadvantages / limits
Answers “is it us or Azure?” in under two minutes, before a support ticket Tells you whose layer broke, not the application root cause itself
Personalised Service Health beats the lagging public status page Service Health depends on Microsoft declaring an event — there can be a detection lag
Resource Health gives a platform verdict before you read any logs Resource Health is silent on app/config faults; Available ≠ healthy app
Change Analysis resolves the plurality of self-inflicted incidents Property-diff/in-guest coverage varies by resource type and requires enablement
Alerts mean Azure pages you, not your users Untuned alerts cause fatigue (region/service/redeploy noise)
All built on the Activity Log — no new pipeline to run Activity Log retention is ~90 days; long-term needs a Log Analytics sink
Free to enable; no per-signal cost for the health blades Correlation in time is not proof of cause — still needs judgement

When each matters: Service Health earns its keep on the rare-but-brutal regional event, where “it’s Azure, ETA published” saves hours of self-debugging. Resource Health matters most for single-resource incidents — a VM that mysteriously rebooted — where the platform verdict instantly tells you if Microsoft did it. Change Analysis is the everyday workhorse, because most incidents are self-inflicted and the diff is right there. The discipline as a whole is the cheapest possible upgrade from “guess and restart” to “look and know.”

Hands-on lab

This lab stands up the proactive half — a Service Health alert and a Resource Health alert routed to an action group — then practises the reactive correlation flow against a change you make yourself. It is free-tier friendly: action groups and Activity Log alerts have no fixed cost, and the only resource you create is a cheap web app you delete at the end.

1. Set your subscription and a working resource group.

az account set --subscription "<your-subscription>"
az group create --name rg-shealth-lab --location centralindia

2. Create an action group that emails you. Replace the address with your own.

az monitor action-group create \
  --name ag-lab-oncall --resource-group rg-shealth-lab \
  --short-name laboncall \
  --action email me your.name@example.com

Expected: JSON describing the action group with one emailReceiver. You will get a confirmation email asking you to acknowledge being added.

3. Create a Service Health alert for service issues. Scoped to the whole subscription (Service Health is subscription-level).

SUB_ID=$(az account show --query id -o tsv)
az monitor activity-log alert create \
  --name lab-alert-servicehealth \
  --resource-group rg-shealth-lab \
  --scope "/subscriptions/$SUB_ID" \
  --condition category=ServiceHealth \
  --action-group ag-lab-oncall \
  --description "Lab: notify on any Service Health event"

Expected: a JSON object with enabled: true and your condition. (It fires only when Azure actually emits a Service Health event for your tenant — you may not see it trigger during the lab, which is the point: it is armed for the real thing.)

4. Create a small web app to generate a Resource Health-eligible resource.

az appservice plan create --name plan-lab --resource-group rg-shealth-lab --sku B1 --is-linux
az webapp create --name app-shealth-lab-$RANDOM --resource-group rg-shealth-lab \
  --plan plan-lab --runtime "NODE:20-lts"

5. Create a Resource Health alert scoped to the lab resource group.

az monitor activity-log alert create \
  --name lab-alert-resourcehealth \
  --resource-group rg-shealth-lab \
  --scope "/subscriptions/$SUB_ID/resourceGroups/rg-shealth-lab" \
  --condition category=ResourceHealth \
  --action-group ag-lab-oncall \
  --description "Lab: notify on resource health transitions in the lab RG"

6. Make a change, then correlate it. Restart the web app to generate a control-plane operation and a (Customer Initiated) health blip, then read both back exactly as you would mid-incident.

APP=$(az webapp list -g rg-shealth-lab --query "[0].name" -o tsv)
az webapp restart --name "$APP" --resource-group rg-shealth-lab

# The change you just made, from the Activity Log (operation layer)
az monitor activity-log list --resource-group rg-shealth-lab --offset 30m \
  --query "[?operationName.value contains 'restart' || operationName.value contains 'write'].{time:eventTimestamp, op:operationName.localizedValue, by:caller}" \
  -o table

# The resulting Resource Health transition (note the Customer Initiated reason)
RES_ID=$(az webapp show -g rg-shealth-lab -n "$APP" --query id -o tsv)
az monitor activity-log list --offset 30m \
  --query "[?category.value=='ResourceHealth' && resourceId=='$RES_ID'].{time:eventTimestamp, status:properties.currentHealthStatus, reason:properties.cause}" \
  -o table

Expected: the first query shows your restart operation with your account as caller; the second, after a short delay, shows a health transition whose reason is Customer Initiated — proving the platform correctly attributes the blip to you, not itself. That is the exact signal that, in a real incident, tells you “this was a change we made,” not an Azure fault.

7. Explore the portal views. Open Service Health (search it in the portal) and click through Service issues, Planned maintenance and Health advisories — note any real events scoped to your tenant. Then open the web app → Resource Health to see its current state and history, and Diagnose and solve problems → Changes to see Change Analysis surface your restart.

8. Teardown.

az group delete --name rg-shealth-lab --yes --no-wait

The takeaway: the alerts are armed for real platform events, and you have run the exact correlation flow — change → operation → health transition → reason — that resolves “is it us or Azure?” on a live call.

Common mistakes & troubleshooting

The failure modes here are mostly process failures — wrong place, wrong order, untuned alerts. The most common, with symptom, root cause, how to confirm, and fix:

# Symptom Root cause How to confirm Fix
1 “We learned about the outage from our users” No Service Health alert wired No activityLogAlert with category ServiceHealth exists Deploy the Service Health alert → action group (above)
2 Investigation ended because status page was green Trusting public Azure Status over Service Health Public page green but Service Health shows a tenant event Always open Service Health; it knows your footprint
3 Hour wasted in app logs for an Azure outage Started in application telemetry, not at the platform Service Health had an active issue the whole time Adopt the climb-down order: Service → Resource → Change → App
4 “Nothing changed” but the app broke Looked at operations, not property diffs Activity Log shows a routine “write”; the diff shows the real value change Use Change Analysis property diffs, not just the op log
5 Paged constantly, alerts ignored Resource Health alert includes Customer Initiated + dev scope Alert scope is whole subscription; reason filter is “any” Scope to prod RGs; filter previous=Available; reconsider Customer Initiated
6 Service Health alert never fires Filtered to a region/service you do not run in Alert condition lists wrong regions/services Match the alert’s services/regions to your real footprint
7 Resource Health says Available but app is down Conflating platform health with app health App Insights shows failing requests; platform is green The fault is yours — go to Change Analysis / App Insights
8 Can’t find the change that broke it Looked only at the failing resource A dependency (NSG, Key Vault, DNS) changed, not the app Widen Change Analysis to the resource group / dependencies
9 Correlated a change that was actually innocent Treated time-proximity as proof The “change” was a tag edit an hour earlier on another resource Weigh ranking + diff plausibility, not just timestamp
10 Old incident’s tracking ID quoted to support Reused a stale, resolved event ID The tracking ID maps to a Resolved event Pull the active event’s tracking ID from Service Health
11 Maintenance caused a blip nobody expected Ignored Planned maintenance notices A maintenance window overlapped the symptom time Subscribe to Planned maintenance; verify HA absorbs reboots
12 Lost the evidence after 90 days for the RCA Relied on Activity Log default retention Activity Log only goes back ~90 days Route Activity Log to Log Analytics for long-term retention

Two distinctions that save the most time during a live call. First, when both a platform issue and your own change could explain a Resource Health transition near the symptom, the reasonType is the tie-breaker — Platform Initiated points at Azure, Customer Initiated at you — cross-checked against Service Health. Second, “Service Health shows nothing, so it’s not Azure” is a trap: detection lag is possible, so corroborate with Resource Health on the affected resource before fully ruling Azure out.

Best practices

Security notes

Service Health, Resource Health and Change Analysis are read-mostly, but they touch sensitive surfaces — treat them with least-privilege discipline.

Concern Risk Mitigation
Who can read Service/Resource Health Health and change data reveals your topology and failure patterns Grant Reader at the right scope; do not over-grant Contributor just for visibility
Who can manage alert rules An untrusted actor could disable the Service Health alert, blinding you Restrict write on Microsoft.Insights/activityLogAlerts to platform owners via RBAC
Action group targets leak channels Emails/SMS/webhook URLs in action groups are a soft target Store webhook secrets in Key Vault; review action group receivers periodically
Change Analysis exposes config Property diffs can surface setting names (and sometimes values) Keep secrets in Key Vault references, never as plaintext app settings, so diffs never reveal them
In-guest change agent footprint The Change Analysis agent reads in-guest config on supported compute Enable only where needed; it follows the resource’s existing identity/permissions
Audit trail integrity The Activity Log is your forensic record Export to an immutable Log Analytics / storage sink; restrict who can change diagnostic settings

Two rules worth stating plainly. Least privilege for visibility: reading health and change data needs only Reader — never hand out Contributor or Owner so someone can watch a dashboard. Keep secrets out of properties: because Change Analysis shows property diffs, any secret placed directly into an app setting can appear in a change record; use Key Vault references so the diff shows @Microsoft.KeyVault(...), never the secret. The deep dive on getting that right is Azure Key Vault: Managing Secrets, Keys and Certificates Securely.

Cost & sizing

The good news dominates: the core of this article is effectively free. The health blades carry no per-view charge, and Activity Log alerts and action groups have no fixed monthly fee. What you pay for is downstream and small:

Component What drives the cost Rough figure Notes / free allowance
Service Health / Resource Health blades Nothing — included Free No per-event or per-view charge
Activity Log (90-day retention) Nothing — included Free Default retention; no charge to query
Activity Log alert rules Nothing — included Free Activity Log alerts are not metered like metric alerts
Action group notifications Email free; SMS/voice/push metered Email: free; SMS: a few ₹/USD-cents each Generous free email; SMS/voice priced per message/region
Webhook / Logic App / ITSM action The downstream resource’s own cost Varies A Logic App run or webhook target bills separately
Change Analysis (operation/diff) Included for ARM-level Free Built on Activity Log + Resource Graph
Activity Log → Log Analytics export Ingestion + retention beyond 90 days Per-GB ingested + retention Only if you need long-term history; size by log volume

Sizing guidance: the only meaningful spend is the Log Analytics sink if you route the Activity Log there for retention beyond ~90 days — modest versus application logs, sized by your control-plane change rate, not your request rate. For notifications, prefer email and push (free) for routine pages and reserve SMS/voice (metered) for true wake-someone-up severity. The health blades themselves scale with your footprint at no cost. Fold the Log Analytics sink into your monitoring budget as covered in Azure Cost Management Budgets: Setup with Email and Action Group Alerts.

Interview & exam questions

1. What is the difference between Azure Service Health and the public Azure Status page? Azure Status is a global, anonymous, conservative page of broad confirmed events. Service Health is the personalised projection of the same data, filtered to your tenant’s subscriptions, regions and services, often surfacing relevant events earlier and at finer granularity. During your own incident it is the more trustworthy view because it knows what you run. (AZ-104; AZ-305.)

2. What are the three Service Health event types and how does your response differ for each? Service issue (an active unplanned outage — communicate and wait/failover, do not touch your deploy), planned maintenance (scheduled platform work — verify HA absorbs it, expect brief restarts), and health advisory (a notice requiring your action — action it before it becomes an incident). Confusing them is the most common Service Health mistake.

3. What does Resource Health tell you that your application logs cannot? Whether the platform itself considers a resource healthy, and why it transitioned. It reports Available/Unavailable/Degraded/Unknown with a reasonType of Platform-, Customer-, or Unknown-Initiated — so you learn in one line that Azure rebooted your VM, before reading any logs. (AZ-104.)

4. A VM is Unavailable in Resource Health with reason “Platform Initiated.” Whose problem is it and what do you do? It is Azure’s — the platform caused the transition (host failure or planned reboot). Escalate with the resource ID and transition timestamp, check Service Health for a correlated maintenance/issue, and rely on your HA (availability zones/sets) rather than touching the workload.

5. Resource Health says Available but your app is returning 503s. What does that tell you? The fault is above the platform — your code, config, or a dependency — because the platform considers the resource healthy. Next is Change Analysis for what changed, then Application Insights for the root cause. Available never means “the app is healthy.”

6. What does Change Analysis add over reading the Activity Log directly? The Activity Log lists operations; Change Analysis computes before/after property diffs (and in-guest config on supported compute) and ranks changes by relevance. A routine-looking “update site config” operation hides the one value that changed — the diff reveals it. (AZ-104.)

7. How do you make Azure page you when it declares an outage in your region? Create an Activity Log alert on category ServiceHealth, filtered to your services/regions and (typically) incidentType=Incident, routed to an action group. Ship it as Bicep for consistency across subscriptions. This is the highest-leverage proactive step.

8. How do you avoid alert fatigue from Resource Health alerts? Scope to production resource groups, fire on previous=Available to catch the drop not the recovery, match services/regions to your footprint, and decide deliberately about Customer Initiated transitions — including them pages you on every redeploy, usually wrong for on-call.

9. What is a tracking ID and why does it matter? A globally unique identifier on each Service Health event — the join key between your incident channel, support case, and the eventual Post-Incident Review (PIR). Quoting the active event’s tracking ID (not a stale resolved one) lets Microsoft and your team talk about the same event.

10. Walk me through the order you investigate “is it us or Azure?” Climb down from the platform: Service Health (is Azure broken in my region?), Resource Health (is the platform breaking this resource?), Change Analysis (did a change of mine line up?), then Application Insights (application root cause). Starting in application logs wastes the first hour.

11. How long does the Activity Log retain data, and what if you need more? About 90 days by default. For longer retention — required for many post-incident reviews and audits — route the Activity Log to a Log Analytics workspace (or storage) via a diagnostic setting, where you set your own retention. (AZ-305.)

12. Why is “correlation in time” not enough to assign cause to a change? A change with a nearby timestamp may be unrelated — a different resource, or too far before the symptom. Confirmation needs the change to be plausibly causal (the diff actually produces this symptom) and high-ranked for the affected resource. A scale-in that removed the last instance is a culprit; a tag edit an hour earlier is noise.

Quick check

  1. Your dashboards are red. What is the first blade you open, and why that one rather than your application logs?
  2. Service Health shows a “health advisory,” not a “service issue.” Is your site down because of Azure right now? What does the advisory actually mean?
  3. Resource Health reports your App Service plan as Available, but users get 503s. Whose problem is it, and where do you look next?
  4. You find a config change in Change Analysis two minutes before the outage. Is that proof it caused the incident? What else must be true?
  5. You want Azure to page you the moment it declares an outage in your region. Which alert do you create, on which Activity Log category, and what is the most important thing to tune?

Answers

  1. Service Health — it answers “is it Azure?” scoped to your tenant. Open it first because if Azure declared an issue in your region your response changes entirely (communicate/failover, not debug), and starting in app logs wastes the first hour on a problem you do not own.
  2. No — a health advisory is advance notice needing your attention (a deprecation, a quota/config condition), not an active outage. It may be the future root cause if ignored, but Azure is not breaking your site right now. Look for an active service issue to confirm a live platform outage.
  3. It is your problem (app/config/dependency), because the platform considers the plan healthy — Available never means the app is healthy. Go to Change Analysis for what changed, then Application Insights for the root cause; the 503 is caused above the platform.
  4. No, not by itself. Time-proximity is a lead, not proof. It must be plausibly causal — the diff has to actually produce this symptom — and ideally high-ranked for the resource. A scale-in to one instance is a culprit; an unrelated tag edit is coincidence.
  5. A Service Health alert — an Activity Log alert on category ServiceHealth (typically incidentType=Incident), routed to an action group. The most important tuning is matching services and regions to your real footprint so you page on events that touch you and stay silent on the rest.

Glossary

Next steps

AzureService HealthChange AnalysisResource HealthObservabilityIncident ManagementAzure MonitorActivity Log
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading