It is 03:00 and your phone has buzzed forty-one times. The same alert — “CPU > 80% on vm-batch-03” — has fired, resolved, fired, resolved, all night. You silence the phone, and at 03:40 the one alert that actually mattered (the payment API’s availability test going red) lands in the same flood and you sleep through it. By morning the team has done what every team does with a noisy monitor: they have started ignoring it. That is the real failure. A monitoring system that cries wolf trains its humans to disbelieve it, and a disbelieved alert is worse than no alert, because you are paying for it and trusting it and it is quietly lying to you.
This article is the tuning playbook. Azure Monitor alerts are noisy for a small number of concrete, fixable reasons: a threshold set on a metric that naturally oscillates around it, an evaluation frequency so tight that a one-minute spike pages you, an alert with no auto-resolve so it never closes, an action group fanned out to six channels with no de-duplication, or a hundred near-identical resources each firing their own copy of the same rule. What makes noise feel intractable is that the cause hides behind one symptom — “too many pages” — and the dials that fix it (windowSize, evaluationFrequency, autoMitigate, numberOfEvaluationPeriods, alert processing rules, dynamic thresholds) are scattered across three different blades, one of which most people have never opened.
By the end you will stop treating “the monitor is too loud” as a personality trait of Azure and start treating it as a set of misconfigured knobs. You will localise any noisy alert to exactly one cause — bad threshold, bad window, missing auto-mitigate, fan-out duplication, flapping near the boundary, or a storm with no suppression — and know the precise az command or portal path to confirm it and the exact setting that fixes it. The centrepiece is a symptom → root cause → confirm → fix playbook you keep open the next time the pager melts down, plus a state reference so you can read what a “fired”, “acknowledged” or “auto-resolved” alert is actually telling you.
What problem this solves
Alert noise is an operational tax with three line items. First, alert fatigue: when 95% of pages are non-actionable, humans stop reading them and the 5% that matter get missed — the documented mechanism behind real outages, the signal there in the flood, unread. Second, cost: every evaluation and every notification has a price, and a rule at one-minute frequency across 200 resources fanning out to SMS and voice runs a bill you would not knowingly approve. Third, erosion of trust: once a team decides the monitor is “always wrong” they route it to a muted channel, and you have spent money to build a system everyone agrees to ignore.
What breaks without tuning: a metric hovering at the threshold (CPU oscillating 78–82% against an 80% rule) produces a flapping stream of fire/resolve pairs, dozens an hour, none a real state change. A platform-wide event (a region blip, a dependency outage, a deployment) trips fifty rules into an alert storm that buries the root-cause alert under fifty symptoms. A maintenance window or a known batch job pages the on-call for an entirely expected condition. A single noisy resource drowns out everything else. Each is a specific, nameable failure with a specific fix, and conflating them (“the monitor is broken”) is what keeps teams stuck.
Who hits this: every team past the first few resources. It bites hardest on fleets — dozens of VMs, App Services or databases sharing one rule, where noise multiplies by resource count — on teams that fanned alerts straight to a phone with no severity routing, on anyone using static thresholds for seasonal metrics (request rate, queue depth), and on shops without maintenance-window discipline who page themselves every patch. The fix is almost never “delete the alert” — it is “make it fire only on a real, actionable state change, route it by severity, and suppress the storms and windows you already expected.”
To frame the field before the deep dive, here is every noise class this article covers, the question it forces, and where to look first:
| Noise class | What you actually see | First question to ask | First place to look | Most common single cause |
|---|---|---|---|---|
| Flapping | Same alert fires/resolves repeatedly | Is the metric oscillating across the threshold? | Alerts list grouped by name; the metric chart | Threshold sits inside the metric’s normal jitter |
| Over-sensitive threshold | Alert fires on brief, harmless spikes | Did the spike last long enough to matter? | windowSize + evaluationFrequency on the rule |
One-minute window pages on a transient |
| Never resolves | Alert stays “fired” forever | Is auto-mitigate on? | Rule’s autoMitigate flag |
autoMitigate is false → alert never closes |
| Fan-out duplication | One condition, many notifications | How many channels does the action group hit? | Action group receivers list | 5+ receivers + no de-dup → one event, six pages |
| Alert storm | Dozens of alerts at once on one event | Did a shared dependency or region blip? | Alerts timeline; Service Health | No suppression rule for correlated events |
| Expected condition | Pages for maintenance / known batch jobs | Was this window planned? | Change Analysis; deployment calendar | No alert processing rule for the window |
Learning objectives
By the end of this article you can:
- Diagnose any noisy Azure Monitor alert as one of six classes — flapping, over-sensitive threshold, no-auto-resolve, fan-out duplication, alert storm, or expected-condition noise — and name the dial that fixes each.
- Tune a metric alert’s evaluation frequency, aggregation window (
windowSize) and threshold so it fires on a sustained, actionable condition rather than a one-minute blip. - Stop flapping with multiple evaluation periods (
numberOfEvaluationPeriods/minFailingPeriodsToAlert) and dynamic thresholds, and explain when each is the right tool. - Make alerts close themselves with auto-mitigate and understand the difference between stateful and stateless alerts.
- De-duplicate and route notifications with action groups and alert processing rules (suppression + action-group override), and apply maintenance-window suppression so planned work never pages anyone.
- Read the alert lifecycle states — New / Fired / Acknowledged / Closed, and the monitor condition Fired / Resolved — and the action-group notification limits, so you know what the system is actually doing.
- Drive the core tools fluently:
az monitor metrics alert,az monitor action-group, alert processing rules, the Alerts blade with its grouping and history, and KQL against theAlertsManagementResourcesgraph andAlertlogs.
Prerequisites & where this fits
You should already understand Azure Monitor’s basics: a metric is a numeric time series (CPU%, requests/sec, queue depth) emitted by a resource; a log query alert runs a KQL query against a Log Analytics workspace on a schedule; an action group is the reusable list of who-gets-told (email, SMS, webhook, ITSM, Logic App) that an alert triggers. You should be comfortable running az in Cloud Shell, reading JSON output, and reading a metric chart. Knowing what a Log Analytics workspace is helps, because log alerts and the alert-history queries live there.
This sits in the Observability track, downstream of getting alerts working at all and upstream of full incident management. If you have not yet created an alert and action group, start with Azure Monitor metric alert with action group email and SMS setup — this article assumes those exist and are too loud. It pairs tightly with Azure Monitor and Application Insights for observability, because Application Insights is where many noisy alerts (availability tests, failure-rate rules) originate. The data-model background — what is a metric versus a log versus a trace — is in Azure Monitor data platform: metrics, logs, and traces explained, and when to alert on a metric versus a log query is covered in Azure Monitor metrics vs Log Analytics: when to use which. For the KQL you will write to investigate alert history, see KQL for Log Analytics: summarize, join, and time-series queries.
A quick map of where each tuning knob lives, so you open the right blade fast:
| Concern | Lives on | Tuned with | Who usually owns it |
|---|---|---|---|
| How often the rule evaluates | The alert rule | evaluationFrequency |
App / platform team |
| How much data each evaluation looks at | The alert rule | windowSize (aggregation granularity) |
App / platform team |
| What value trips it | The alert rule | threshold (static) or dynamic |
App / platform team |
| Flapping suppression | The alert rule | numberOfEvaluationPeriods + min-failing |
App / platform team |
| Whether it auto-closes | The alert rule | autoMitigate |
App / platform team |
| Who gets told, on which channel | Action group | Receivers + severity routing | SRE / on-call lead |
| Storm de-dup, maintenance windows | Alert processing rule | Suppression / action-group override | SRE / platform team |
Core concepts
Five mental models make every later diagnosis obvious.
An alert is a rule, a condition, and an action — and noise can come from any of them. The alert rule defines what to watch and how often; the monitor condition is its verdict at each evaluation (Fired or Resolved); the action is the action group notifying its receivers. When the pager is too loud, the cause is in exactly one layer: the rule is mistuned (fires too easily), the condition flaps (oscillates fired/resolved), or the action fans out to too many channels. Naming the loud layer is the first fork in every decision.
Stateful alerts have a lifecycle; stateless alerts do not. A stateful metric or log alert tracks state — it creates one alert in New/Fired, and auto-resolves to Resolved/Closed when the condition clears if auto-mitigate is on. A stateless alert (older activity-log alerts, or stateful turned off) fires a fresh notification every evaluation the condition is true — no concept of “still firing”, so a sustained problem pages you every minute. This is the difference between one alert for a two-hour incident and 120 alerts for the same one. Metric alerts are stateful by default; the trap is assuming auto-resolve is on when it is not.
Evaluation frequency and window are two different dials, and confusing them is the root of most over-sensitivity. Evaluation frequency (evaluationFrequency) is how often the rule runs; window size (windowSize) is how much trailing data each run aggregates (e.g. average CPU over the last 5 minutes). A rule evaluating every minute over a one-minute window pages on a single transient spike; the same threshold over a 15-minute window only fires when the condition is sustained — which is usually what “a real problem” means. Tuning the window, not just the threshold, is the most under-used noise fix.
Flapping is a boundary problem, not a value problem. When a metric naturally oscillates — CPU bouncing 78–82% all day — a static threshold at 80% sits inside the jitter, so every up-crossing fires and every down-crossing resolves. The metric is healthy; the threshold is in the wrong place. Two mechanisms fix this without blindly raising the line: requiring multiple consecutive failing periods before firing, and dynamic thresholds that learn the normal band and alert on genuine deviation.
Suppression and routing are separate from the rule — and they tame storms you cannot prevent. Some noise is not one mistuned rule but fifty correctly-tuned rules firing on one underlying event (a region issue, a dependency outage, a deployment), which you cannot fix by editing fifty rules. Alert processing rules sit above the rules: they suppress notifications during a maintenance window, or override the action group for a class of alerts. Combined with action-group de-duplication and severity routing, this turns a storm into a single, well-targeted notification. Editing the rule fixes one alert; the processing-rule layer fixes a whole category.
The vocabulary in one table
Before the deep sections, pin down every moving part — the glossary repeats these for lookup; this is the mental model side by side:
| Concept | One-line definition | Where it lives | Why it matters to noise |
|---|---|---|---|
| Alert rule | What to watch, how often, what trips it | Azure Monitor → Alerts | Mistuned → fires too easily |
| Evaluation frequency | How often the rule runs | Rule condition | Too tight → pages on transients |
| Window size | How much trailing data each run aggregates | Rule condition | Too small → spikes page you |
| Threshold | The value that trips a static rule | Rule condition | Inside normal jitter → flapping |
| Dynamic threshold | ML-learned normal band instead of a fixed line | Rule condition | Kills seasonal false positives |
| Monitor condition | The rule’s verdict: Fired / Resolved | Per evaluation | Oscillates → flapping stream |
| Auto-mitigate | Whether the alert auto-resolves when clear | Rule setting | Off → alert never closes |
| Stateful vs stateless | Tracks state and de-dups, or fires every time | Rule type | Stateless → one page per minute |
| Action group | Reusable list of who/what gets notified | Azure Monitor → Action groups | Many receivers → fan-out noise |
| Alert processing rule | Suppress or re-route alerts above the rule layer | Azure Monitor → Alert processing rules | Storms + maintenance windows |
| Severity | Sev 0 (critical) … Sev 4 (verbose) | Set on the rule | Wrong sev → everything pages |
| Alert storm | Many alerts from one underlying event | Operational state | Buries the root-cause alert |
The alert lifecycle and state reference
Before tuning anything, read what an alert is telling you. An alert carries two orthogonal states people confuse: the monitor condition (is the rule’s condition true right now?) and the alert state (the human workflow status). A rule can be Resolved while its alert is still Acknowledged (a human is on it). The full reference:
| State field | Possible values | Set by | What it means | Noise relevance |
|---|---|---|---|---|
| Monitor condition | Fired | The platform | The rule’s condition is currently true | Repeated Fired/Resolved pairs = flapping |
| Monitor condition | Resolved | The platform (auto-mitigate) | The condition cleared and was auto-resolved | If never reached → autoMitigate is off |
| Alert state | New | The platform | Just created, nobody has touched it | The default on every new alert |
| Alert state | Acknowledged | A human | Someone is investigating | Set to mute re-notification while working |
| Alert state | Closed | A human / auto | Resolved and closed out | Stateful alerts can auto-close on resolve |
| Severity | Sev 0 | The rule author | Critical — wake someone up | Mis-set Sev 0 is the loudest noise |
| Severity | Sev 1 | The rule author | Error — urgent | |
| Severity | Sev 2 | The rule author | Warning | |
| Severity | Sev 3 | The rule author | Informational | Should rarely page a phone |
| Severity | Sev 4 | The rule author | Verbose | Dashboard/log only, never page |
Two reading notes that save the most time during a storm. First, monitor condition vs alert state: “it says Resolved but I’m still paged” happens because the monitor condition is the platform’s view of the metric while the alert state is the human workflow — a re-notification can fire on a still-open alert. Second, fired-once vs flapping: a flapping alert looks like one rule “spamming,” so group the alerts list by rule name and count fire events in an hour — more than three or four fire/resolve cycles is flapping, not a single firing.
And the notification mechanics — because “I got six texts for one problem” is a fan-out fact, not a bug:
| Notification fact | The number | Why it produces noise | The lever |
|---|---|---|---|
| One action group, many receivers | Each receiver notified per alert | 6 receivers = 6 notifications for 1 alert | Trim receivers; split by severity |
| Voice / SMS rate limits | No more than 1 SMS per ~5 min per number; 1 voice call per ~5 min | Burst alerts queue/drop | Don’t fan a storm to SMS/voice |
| Email rate limit | No more than 100 emails in an hour to one address before throttling | An alert storm self-throttles email | Route storms to a webhook/queue, not inboxes |
| Action group reuse | One group can serve many rules | A bad group amplifies across all of them | Fix the group once, fix many alerts |
Diagnosing flapping: the boundary oscillation
Flapping is the most common and most maddening noise: the same alert fires and resolves over and over, dozens of times an hour, with no underlying state change worth a page. Five causes hide here — scan the matrix, then read the row that matches:
| # | Flapping cause | Tell-tale signal | Confirm with | Real fix |
|---|---|---|---|---|
| 1 | Threshold inside the metric’s normal jitter | Metric chart oscillates across the line | Overlay threshold on the metric chart | Move threshold out of the band, or use dynamic |
| 2 | Single-period evaluation (no hysteresis) | Every brief cross fires immediately | numberOfEvaluationPeriods = 1 |
Require N-of-M failing periods |
| 3 | Window too small for a spiky metric | 1-min spikes trip it; 15-min average is fine | windowSize = PT1M on a bursty metric |
Widen windowSize |
| 4 | Seasonal metric on a static threshold | Fires every business-hours peak, fine at night | Static threshold + daily pattern in chart | Dynamic thresholds (learns seasonality) |
| 5 | Auto-mitigate fast-cycling | Resolves the instant the metric dips, re-fires next minute | Tight resolve + tight eval | Add evaluation periods so resolve needs persistence too |
Cause 1 — The threshold sits inside the metric’s normal range
Your metric naturally lives in a band — CPU on a busy worker bounces 75–85% all day — and you drew the alert at 80%. Every up-cross fires, every down-cross auto-resolves: a fire/resolve pair every few minutes. The metric is healthy; the threshold is in the wrong place.
Confirm. In the portal, open the alert rule and click the condition — the chart overlays the threshold on the live metric, and if the line cuts through the middle of the wiggle, that is your answer. Via CLI, list the metric and look at the spread:
# Pull average CPU at 1-minute granularity for the last 3 hours and inspect the spread
az monitor metrics list \
--resource $(az vm show -g rg-batch -n vm-batch-03 --query id -o tsv) \
--metric "Percentage CPU" \
--interval PT1M --aggregation Average \
--start-time 2026-06-24T00:00:00Z --end-time 2026-06-24T03:00:00Z \
--query "value[0].timeseries[0].data[].average" -o tsv
If the values cluster around your threshold, the threshold is the problem.
Fix. Move the threshold clearly above the normal band (if 85% is normal, alert at 92%, not 80%), or — better for a wide or shifting band — switch to dynamic thresholds (below). Raising a static threshold is blunt and can hide a real climb; the more robust fix is to require persistence (Cause 2) so a brief excursion doesn’t fire at all.
Cause 2 — No hysteresis: a single crossing fires instantly
By default a rule with numberOfEvaluationPeriods = 1 fires the moment one evaluation breaches the threshold — no requirement that the condition persist. A metric that pokes above the line for sixty seconds and drops back pages you, then resolves: pure flapping, no real incident.
Confirm. Inspect the rule’s evaluation-period settings:
az monitor metrics alert show --name cpu-high-vm-batch-03 --resource-group rg-batch \
--query "criteria.allOf[0].{metric:metricName, op:operator, threshold:threshold, periods:windowSize}" -o json
If you have not configured multiple periods, every single breach fires.
Fix. Require the condition to fail across multiple consecutive evaluation periods before firing — Azure Monitor supports an “N out of the last M periods” model. This adds hysteresis: a one-minute blip is ignored; only a sustained breach fires.
# Fire only when at least 4 of the last 5 one-minute periods breach the threshold
az monitor metrics alert create \
--name cpu-high-vm-batch-03 --resource-group rg-batch \
--scopes $(az vm show -g rg-batch -n vm-batch-03 --query id -o tsv) \
--condition "avg Percentage CPU > 85 with 4 violations out of 5 aggregated points" \
--window-size PT1M --evaluation-frequency PT1M \
--description "Sustained high CPU on vm-batch-03" --severity 2
resource cpuHigh 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: 'cpu-high-vm-batch-03'
location: 'global'
properties: {
severity: 2
enabled: true
scopes: [ vm.id ]
evaluationFrequency: 'PT1M'
windowSize: 'PT1M'
autoMitigate: true
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: [
{
name: 'HighCPU'
metricName: 'Percentage CPU'
operator: 'GreaterThan'
threshold: 85
timeAggregation: 'Average'
criterionType: 'StaticThresholdCriterion'
// Require persistence: 4 of the last 5 evaluation periods must breach
// (configured via the rule's failing-periods settings)
}
]
}
}
}
The “N out of M” model is the single most effective anti-flap dial for a metric whose breaches are real but brief. The choices and their effect:
| Evaluation-period setting | Effect | When to use | Trade-off |
|---|---|---|---|
| 1 of 1 (default) | Fires on the first breach | Truly instantaneous conditions (a heartbeat stops) | Maximum sensitivity → maximum flapping |
| 3 of 3 | Must breach 3 periods in a row | Steady, sustained conditions | Slower to fire (delay = 3 × frequency) |
| 4 of 5 | Tolerates one good period among five | Bursty metrics with occasional dips | Best balance; rides a single good minute |
| 1 of 5 | Fires if any one of five breaches | You want to catch any spike at all | Re-introduces sensitivity; rarely what you want |
Cause 3 — The window is too small for a spiky metric
A PT1M window on a naturally spiky metric (request latency, queue depth during bursts) makes each one-minute spike its own data point that can trip the rule. Averaged over PT15M, the spikes smooth out and the line is only crossed when the elevation is sustained.
Confirm. Check the configured window:
az monitor metrics alert show --name latency-high-api --resource-group rg-shop \
--query "{window:windowSize, frequency:evaluationFrequency}" -o json
A PT1M window on a bursty metric is the smoking gun.
Fix. Widen the window to the shortest problem worth paging on. If a 30-second latency spike is harmless but five sustained minutes is an incident, use a PT5M or PT15M window:
az monitor metrics alert update --name latency-high-api --resource-group rg-shop \
--window-size PT15M --evaluation-frequency PT5M
Cause 4 — A seasonal metric on a static threshold
Request rate, queue depth, login volume have a daily and weekly shape. A static threshold correct at 3 a.m. is far too low at the 11 a.m. peak (false alarms all morning) or too high overnight (misses a real anomaly). If breaches line up with predictable peaks (every weekday morning, never weekends), it is seasonality, not incidents — confirm by looking at the metric over a few days. The fix is dynamic thresholds, which model the historical pattern and alert on deviation from the learned band (its own section, below).
Cause 5 — Auto-mitigate cycling too fast
Auto-mitigate closes alerts that clear — good — but if both the fire and the resolve are single-period, the alert resolves the instant the metric dips and re-fires the instant it rises, and auto-mitigate is now manufacturing flapping. The fix is the same hysteresis from Cause 2: requiring multiple failing periods means a brief dip-then-rise stays within one logical firing instead of a resolve-then-refire pair.
Tuning the three dials: frequency, window, threshold
These three are the heart of metric-alert noise, and the most common mistake is tuning the threshold when the problem is the window or the frequency.
Evaluation frequency — how often the rule runs
evaluationFrequency is how often Azure Monitor executes the rule. Tighter frequency means faster detection but more evaluations (more cost) and more chances to fire on a transient:
| Frequency | Detection lag | Noise tendency | Cost | When to use |
|---|---|---|---|---|
| PT1M (1 min) | Fastest | Highest (catches every blip) | Highest eval count | Critical, fast-moving conditions (availability, heartbeat) |
| PT5M (5 min) | Up to 5 min | Moderate | Moderate | The sensible default for most resource metrics |
| PT15M | Up to 15 min | Low | Lower | Slow-moving capacity metrics (disk %, storage growth) |
| PT30M / PT1H | Slow | Lowest | Lowest | Trend/cost/quota metrics where minutes don’t matter |
The instinct to set everything to one-minute frequency “to catch things fast” is the single biggest source of self-inflicted noise. Most problems that matter persist for minutes; PT5M catches them and ignores the transients that would otherwise page you.
Window size — how much data each evaluation sees
windowSize is the trailing aggregation period — not the same as frequency. A rule can evaluate every 5 minutes (PT5M) yet average the last 15 (windowSize = PT15M): overlapping windows that smooth spikes. The sane combinations:
| Combination (freq / window) | Behaviour | Good for | Risk |
|---|---|---|---|
| PT1M / PT1M | Evaluate every minute, look at 1 minute | Heartbeats, hard availability | Pages on any 60-second blip |
| PT5M / PT5M | Evaluate every 5 min, look at 5 min | General resource health | Reasonable, slight spike sensitivity |
| PT5M / PT15M | Evaluate every 5 min, average last 15 | Smoothing a bursty metric | Slower to clear (window lingers) |
| PT15M / PT15M | Coarse, smooth | Capacity trends | Detection lag up to 15 min |
| PT1M / PT5M | Frequent checks, 5-min smoothing | Fast detect, low flap | More evaluations (cost) |
The window must be ≥ the frequency in most setups, and a window several multiples of the frequency gives frequent, smoothed evaluations — fast detection without spike noise. The trade-off: a longer window also takes longer to clear, so the alert lingers after the metric recovers.
Threshold — static versus dynamic
The threshold is the value the aggregated metric is compared against, and the real choice is static versus dynamic. A static threshold (a fixed number you pick) is trivial to set and right when there is a genuine physical or contractual limit — “disk over 90% is bad regardless of time of day,” “error rate over 5%.” A dynamic threshold (an ML-learned band from history, chosen by sensitivity rather than a number) is right when “bad” is relative to normal for this metric at this time — “requests/sec is 4× the usual Tuesday-2pm value.” Its only weaknesses are that it needs a few days of history and can surprise you at first. Use static for hard limits, dynamic for seasonal or per-resource-variable metrics.
Dynamic thresholds: alerting on deviation, not a fixed line
Dynamic thresholds are Azure Monitor’s machine-learning option for metric alerts. Instead of a fixed value you pick a sensitivity (High / Medium / Low), and the platform learns the metric’s historical pattern — including daily and weekly seasonality — and computes an upper and/or lower band. The alert fires on deviation from the learned band, not on crossing a static line. This is the purpose-built cure for seasonal flapping (Cause 4) and for fleets where every resource has a different “normal.”
Confirm whether a rule is static or dynamic. Look at the criterion type:
az monitor metrics alert show --name traffic-anomaly-api --resource-group rg-shop \
--query "criteria.allOf[0].criterionType" -o tsv
# DynamicThresholdCriterion = dynamic; StaticThresholdCriterion = static
Create a dynamic-threshold alert. Pick sensitivity and how many periods must deviate:
az monitor metrics alert create \
--name traffic-anomaly-api --resource-group rg-shop \
--scopes $(az webapp show -g rg-shop -n app-shop-prod --query id -o tsv) \
--condition "avg Requests > dynamic medium 4 violations out of 5 aggregated points" \
--window-size PT5M --evaluation-frequency PT5M \
--description "Request volume deviates from learned normal" --severity 3
In Bicep, the dynamic-threshold criterion differs from the static one only in the criteria.allOf[] entry (the rest of the rule — scopes, evaluationFrequency, windowSize, autoMitigate — is identical to the Cause 2 block above):
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: [
{
name: 'TrafficDeviation'
metricName: 'Requests'
operator: 'GreaterOrLessThan'
timeAggregation: 'Average'
criterionType: 'DynamicThresholdCriterion' // not StaticThresholdCriterion
alertSensitivity: 'Medium' // High | Medium | Low
failingPeriods: { numberOfEvaluationPeriods: 5, minFailingPeriodsToAlert: 4 }
}
]
}
The sensitivity choices and what each means in practice:
| Sensitivity | Band width | Fires on | Use when |
|---|---|---|---|
| High | Narrow band | Small deviations | You want to catch subtle anomalies, accept some noise |
| Medium | Balanced | Moderate deviations | The default for most variable metrics |
| Low | Wide band | Only large, obvious deviations | Very noisy metrics where only big swings matter |
Two gotchas that surprise people:
| Dynamic-threshold gotcha | Why it happens | What to do |
|---|---|---|
| Needs training data | The model learns from history; a brand-new metric has none | Give it ~3 days of history, or start static and migrate |
| Reacts to a sustained shift | A genuine new normal (a launch doubled traffic) makes the band re-learn and may alert during the transition | Expect a settling period after a step change; pair with failing-periods |
Dynamic thresholds also pair with GreaterOrLessThan to catch both anomalous spikes and drops (a queue that suddenly hits zero is as suspicious as one that explodes), which a single static threshold cannot express.
Auto-mitigate and the never-resolving alert
The inverse of flapping is just as common: an alert that fires once and never closes, sitting red on the dashboard forever, so the team learns to ignore the red. The cause is almost always that auto-mitigate is off.
Confirm. Check the flag on the rule:
az monitor metrics alert show --name disk-full-vm-data --resource-group rg-data \
--query "autoMitigate" -o tsv
# false → the alert will never auto-resolve when the condition clears
Fix. Turn auto-mitigate on so the alert resolves when the metric recovers:
az monitor metrics alert update --name disk-full-vm-data --resource-group rg-data \
--auto-mitigate true
properties: {
autoMitigate: true // resolve the alert automatically when the condition clears
// ... rest of the rule
}
The behaviours, and which alerts should and should not auto-mitigate:
| Setting | Behaviour | Right for | Wrong for |
|---|---|---|---|
autoMitigate = true |
Alert resolves when the condition clears | Transient/recoverable conditions (CPU, latency, queue depth) | Conditions needing human sign-off to “really” be fixed |
autoMitigate = false |
Alert stays fired until a human closes it | Events that need an explicit ack (data loss, security) | Routine metrics — produces permanent red noise |
The subtler issue: a log query alert using the number-of-results measure is stateless in spirit — every evaluation that finds matching rows fires a fresh notification, so a persistent condition pages you each run. Fix it by making the query stateful (metric-measurement mode with resolve behaviour) or widening the frequency. Know which alert type you have:
| Alert type | Stateful? | De-dups repeated firing? | Noise risk |
|---|---|---|---|
| Metric alert (default) | Yes | Yes (one alert per ongoing condition) | Low if auto-mitigate on |
| Log search alert (stateful) | Yes | Yes | Low |
| Log search alert (number-of-results, no resolve) | Effectively per-evaluation | No | High — re-notifies each run |
| Activity log alert | Stateless | No | Per matching event |
Suppression, routing and storms: alert processing rules
When the noise is not one mistuned rule but many rules firing on one event, or a known maintenance window, you cannot fix it by editing rules. The tool is the alert processing rule — a layer above the rules that intercepts fired alerts and either suppresses their notifications or overrides the action group. Two modes:
| Processing-rule mode | What it does | Classic use |
|---|---|---|
| Suppress notifications | Alerts still fire (visible in the list) but send no notifications | Maintenance windows; muting a known-noisy scope |
| Apply action group (override) | Replaces the alert’s action group with one you specify | Route all Sev-3 dev alerts to a muted channel; add a webhook to a class of alerts |
Suppressing a maintenance window
You are patching a fleet tonight 22:00–02:00, and every reboot will trip CPU, availability and heartbeat alerts. Instead of disabling rules one by one (and forgetting to re-enable them), create a scheduled suppression processing rule scoped to the resource group for the window:
# Suppress all alert notifications for rg-batch during the maintenance window
az monitor alert-processing-rule create \
--name suppress-patch-night --resource-group rg-batch \
--scopes $(az group show -n rg-batch --query id -o tsv) \
--rule-type RemoveAllActionGroups \
--schedule-recurrence-type Once \
--schedule-start-date-time "2026-06-28 22:00:00" \
--schedule-end-date-time "2026-06-29 02:00:00" \
--description "Mute alerts during monthly patch window"
resource patchWindow 'Microsoft.AlertsManagement/actionRules@2021-08-08' = {
name: 'suppress-patch-night'
location: 'global'
properties: {
scopes: [ resourceGroup().id ]
actions: [ { actionType: 'RemoveAllActionGroups' } ]
schedule: {
effectiveFrom: '2026-06-28T22:00:00'
effectiveUntil: '2026-06-29T02:00:00'
}
description: 'Mute alerts during monthly patch window'
enabled: true
}
}
The alerts still appear in the Alerts list (you keep the audit trail), but nobody is paged for expected reboots. This is the correct way to handle planned work — far safer than disabling rules, which you will forget to turn back on.
Overriding the action group to re-route by severity
A common storm pattern: a shared dependency hiccups and forty Sev-3 informational alerts fan out to the on-call phone. Create a processing rule that, for low-severity alerts in a scope, swaps the action group to a quiet one (Teams/email only, no SMS/voice):
az monitor alert-processing-rule create \
--name dev-sev3-to-teams --resource-group rg-dev \
--scopes $(az group show -n rg-dev --query id -o tsv) \
--rule-type AddActionGroups \
--action-groups $(az monitor action-group show -g rg-dev -n ag-teams-quiet --query id -o tsv) \
--filter-severity Equals Sev3 \
--description "Route dev Sev-3 alerts to a muted Teams channel, not the pager"
Storm de-duplication with action-group grouping
Within an action group, identical alerts can be grouped so a burst produces one notification rather than fifty. Combined with suppression rules for known correlated events, this keeps a region blip from generating a wall of texts. The layered model:
| Layer | What it catches | Tool | Example |
|---|---|---|---|
| Rule tuning | A single mistuned alert | Frequency / window / periods | Stop one CPU rule flapping |
| Auto-mitigate | Never-closing alerts | autoMitigate |
Disk alert resolves itself |
| Action-group routing | Wrong-channel noise | Severity-based action groups | Sev-3 to Teams, Sev-0 to phone |
| Processing rule (suppress) | Expected windows | Scheduled suppression | Mute patch night |
| Processing rule (override) | Correlated storms | Action-group override | Re-route a dependency-storm class |
Architecture at a glance
Picture an alert flowing left to right through four stages, and every noise decision becomes a question of which stage to fix at. Stage one, the source: a resource emits a metric time series (or writes logs), where seasonality and natural jitter live. Stage two, the rule: at each evaluationFrequency tick, Azure Monitor aggregates the trailing windowSize and compares it to the threshold (static or dynamic), optionally requiring N-of-M failing periods — this decides whether a sustained condition exists, so over-sensitivity (tiny window, single period, threshold-in-the-jitter) and flapping are stage-two defects. Stage three, the alert object: a firing rule creates a stateful alert with a monitor condition (Fired/Resolved) and an alert state (New/Acknowledged/Closed); auto-mitigate lives here, and its absence is the “never resolves” defect. Stage four, processing and action: every fired alert passes through any alert processing rules (suppress for a window, swap the action group) before the surviving notification fans out through the action group — so storm noise and fan-out noise are stage-four problems.
The discipline is to fix noise at the earliest stage that owns it: a jitter-flap is stage two (tune the rule), a never-resolving alert is stage three (autoMitigate), a maintenance storm is stage four (suppression). Reaching for stage four to paper over a stage-two problem hides a real signal — the same anti-pattern as scaling up to hide a connection leak. Localise the noise to its stage, fix it there, and the pager goes quiet without going blind.
Real-world scenario
Vellore Retail runs a flash-sale e-commerce platform on Azure: App Service behind Application Gateway, Azure SQL, a fleet of 24 batch VMs, and Service Bus queues. Their on-call had quietly collapsed into uselessness — the previous quarter logged 3,200 alert notifications, of which a review judged 31 actionable. The engineers had built an Outlook rule to auto-file every Azure Monitor email into a folder nobody read. When a genuine SQL DTU exhaustion took checkout down for 19 minutes during a sale, the alert fired correctly at minute two — into the ignored folder.
The platform lead, Priya, ran the numbers first. A KQL query over AlertsManagementResources showed two rules generated 71% of all notifications: a Percentage CPU > 80% rule across all 24 batch VMs (which run at a steady 78–84% by design — pure flapping, Cause 1), and a one-minute availability test with a PT1M window and a single evaluation period, firing on every transient blip from a flaky third-party dependency. Neither had ever caught a real incident.
The fix was staged, not a rewrite. For the CPU rule: raised the threshold to 92% (above the by-design band), set a 4-of-5 failing-periods requirement, and switched to PT5M evaluation with a PT15M window — flapping dropped to zero, and only a sustained climb above 92% now pages. For the availability test: kept PT1M frequency (availability should be fast) but required 3-of-5 failing periods, so a 60-second dependency blip no longer fired while a real outage still paged within five minutes.
Then the structural fixes. Priya split the single “page everyone” action group into three by severity (Sev-0/1 to PagerDuty, Sev-2 to Teams, Sev-3/4 to email only), added a recurring suppression processing rule for the nightly batch window (22:00–23:00), enabled autoMitigate on the eleven metric rules that had it off (permanent red on the dashboard), and turned on dynamic thresholds for the request-rate and queue-depth alerts that had fired every weekday morning peak.
The result after one month: notifications fell from ~1,070/month to 94, of which the team judged 81 actionable — an 86% volume cut and a complete inversion of the signal-to-noise ratio. The engineers deleted the Outlook auto-file rule. The next real incident — a Service Bus dead-letter spike — paged the right person on the right channel within four minutes and was acknowledged in two. The monitor was trusted again, which had been the entire point.
Advantages and disadvantages
Tuning for low noise is not free — there is a real trade-off between sensitivity and quiet, and pretending otherwise leads to either a screaming pager or a silent one that misses incidents.
| Advantages of aggressive noise tuning | Disadvantages / risks |
|---|---|
| On-call trusts the pager again — real alerts get read | Wider windows and N-of-M periods add detection lag (you learn later) |
| Lower notification + evaluation cost | Higher thresholds can hide a slow, real climb |
| Storms collapse to a single actionable signal | A too-broad suppression rule can mute a real alert during the window |
| Dynamic thresholds handle seasonality automatically | Dynamic thresholds are harder to explain and audit than a number |
| Maintenance windows stop self-paging | A forgotten “always on” suppression rule blinds you indefinitely |
| Severity routing keeps Sev-3 off the phone | Mis-severitised rules route critical alerts to a muted channel |
The balance depends on the alert’s job. A heartbeat or availability alert stays sensitive — fast frequency, few periods — because a missed minute matters and a false page is a cheap price for catching an outage early. A capacity or trend alert (disk filling, cost climbing) goes slow and smoothed — minutes of lag are irrelevant and flapping is pure noise. The mistake is one philosophy for all alerts: sluggish availability alerts miss outages, twitchy capacity alerts drown you in flaps. Tune each to its job, and audit suppression rules quarterly so no “temporary” mute outlives its reason.
Hands-on lab
This walk-through creates a deliberately noisy alert, observes the flapping, then tunes it quiet — all on a single cheap VM and free Azure Monitor features. Budget a couple of rupees for the VM if you tear it down promptly; the alert features themselves are near-free at this volume.
1. Set variables and create a resource group and a small VM to alert on.
RG=rg-alert-lab
LOC=centralindia
az group create -n $RG -l $LOC
# B1s is the cheapest burstable VM — enough to emit a CPU metric
az vm create -g $RG -n vm-noisy \
--image Ubuntu2204 --size Standard_B1s \
--admin-username azureuser --generate-ssh-keys --public-ip-sku Standard
VMID=$(az vm show -g $RG -n vm-noisy --query id -o tsv)
2. Create an action group that just emails you (no SMS — keep it cheap and quiet).
az monitor action-group create -g $RG -n ag-lab \
--short-name labalert \
--email-receiver name=me email=h.vinod@gmail.com
AGID=$(az monitor action-group show -g $RG -n ag-lab --query id -o tsv)
3. Create a deliberately noisy CPU alert — tight frequency, tiny window, single period, low threshold. This is the anti-pattern, so you can watch it flap.
az monitor metrics alert create -g $RG -n cpu-noisy \
--scopes $VMID \
--condition "avg Percentage CPU > 20" \
--window-size PT1M --evaluation-frequency PT1M \
--action $AGID \
--severity 3 --description "Deliberately noisy CPU alert (lab)"
4. Generate oscillating CPU on the VM so the metric crosses 20% up and down. SSH in and run a burst-then-idle loop:
# On the VM: 30s of load, 30s idle, repeated — produces an oscillating CPU metric
ssh azureuser@$(az vm show -d -g $RG -n vm-noisy --query publicIps -o tsv) \
'for i in $(seq 1 10); do timeout 30 yes > /dev/null; sleep 30; done'
5. Watch it flap. After ~15 minutes, query the alert history and count the fire/resolve cycles:
# List recent alerts for this rule and their fired/resolved state
az monitor metrics alert show -g $RG -n cpu-noisy --query "{enabled:enabled, window:windowSize, freq:evaluationFrequency, threshold:criteria.allOf[0].threshold}" -o json
# Count alert state changes via the alerts management list
az rest --method get \
--url "https://management.azure.com/subscriptions/$(az account show --query id -o tsv)/providers/Microsoft.AlertsManagement/alerts?api-version=2019-05-05-preview&timeRange=1h" \
--query "value[?contains(name,'cpu-noisy')].properties.essentials.{state:monitorCondition, time:startDateTime}" -o table
You will see multiple Fired/Resolved pairs — the flapping, in your inbox too.
6. Tune it quiet. Apply the three fixes: widen the window, slow the frequency, raise the threshold above the oscillation band, and require persistence.
# Re-create with sane settings: 5-min freq, 15-min window, threshold above the band,
# and require 4 of 5 periods to breach
az monitor metrics alert delete -g $RG -n cpu-noisy
az monitor metrics alert create -g $RG -n cpu-tuned \
--scopes $VMID \
--condition "avg Percentage CPU > 80 with 4 violations out of 5 aggregated points" \
--window-size PT5M --evaluation-frequency PT5M \
--action $AGID --auto-mitigate true \
--severity 3 --description "Tuned CPU alert (lab)"
7. Re-run the oscillating load (step 4) and confirm the tuned rule does not fire on the same workload — the spikes no longer breach a sustained 80% over a 15-minute view with 4-of-5 persistence. You have turned a flapping rule into a quiet one without making it blind to a real, sustained CPU pin.
8. Add a suppression processing rule to mute the lab resource group during a window, then confirm alerts are suppressed.
az monitor alert-processing-rule create \
--name lab-suppress --resource-group $RG \
--scopes $(az group show -n $RG --query id -o tsv) \
--rule-type RemoveAllActionGroups \
--schedule-recurrence-type Once \
--schedule-start-date-time "2026-06-28 22:00:00" \
--schedule-end-date-time "2026-06-29 06:00:00" \
--description "Lab: suppress all notifications overnight"
9. Teardown. Delete the resource group to stop all charges.
az group delete -n $RG --yes --no-wait
You have now reproduced flapping, fixed it three ways, and added suppression — the whole noise-tuning toolkit on one VM.
Common mistakes & troubleshooting
This is the playbook. Each row is a real noise failure mode: the symptom you see, the root cause, the exact command or portal path to confirm it, and the fix. Scan for your symptom, confirm, then fix.
| # | Symptom | Root cause | Confirm (exact cmd / portal path) | Fix |
|---|---|---|---|---|
| 1 | Same alert fires & resolves dozens of times an hour | Threshold sits inside the metric’s normal jitter | Alert rule → condition chart (threshold overlaid on metric); or az monitor metrics list spread |
Raise threshold above the band, or use dynamic thresholds + N-of-M periods |
| 2 | Alert fires on a 60-second spike that doesn’t matter | Window too small / single evaluation period | az monitor metrics alert show --query "{window:windowSize}" shows PT1M |
Widen windowSize to PT5M/PT15M; require 4-of-5 failing periods |
| 3 | Alert fired days ago, still red, condition long gone | autoMitigate is off |
az monitor metrics alert show --query autoMitigate → false |
az monitor metrics alert update --auto-mitigate true |
| 4 | One problem, six text messages | Action group has many receivers (and SMS/voice) | az monitor action-group show --query "smsReceivers[].name" |
Trim receivers; split action groups by severity; keep SMS for Sev-0 only |
| 5 | Forty alerts at 02:00 from one region blip | No suppression for correlated/storm events | Alerts blade timeline (cluster of starts in one minute); Service Health | Add an alert processing rule (action-group override) for the class; enable action-group grouping |
| 6 | Pages every night during patching | No maintenance-window suppression | Change Analysis shows reboots; alerts align with patch window | Scheduled suppression processing rule scoped to the RG for the window |
| 7 | Alert fires every weekday morning, fine at night | Static threshold on a seasonal metric | Metric chart shows daily pattern; breaches align with peaks | Switch to dynamic thresholds (Medium sensitivity) |
| 8 | Log alert re-notifies every single evaluation | Number-of-results log alert with no resolve/state | Rule type = log search, measure = table rows | Use metric-measurement with resolve, or widen evaluationFrequency |
| 9 | Critical alert went to a muted Teams channel | Severity mis-set, or a processing-rule override caught it | Rule’s severity; list processing rules touching the scope |
Correct the rule severity; scope the override to exclude Sev-0/1 |
| 10 | 200 identical alerts, one per resource in a fleet | One rule per resource with no aggregation/grouping | Alerts grouped by rule name show N near-identical | Use a single multi-resource rule at RG/sub scope; group in the action group |
| 11 | Disabled a rule for maintenance, forgot to re-enable | Manual disable instead of scheduled suppression | az monitor metrics alert list --query "[?enabled==\false`]"` |
Use scheduled suppression processing rules (auto-expire), never manual disable |
| 12 | Alert “Resolved” but on-call still got re-paged | Re-notification on a still-open alert / stateless type | Alert essentials: monitorCondition vs alertState | Confirm stateful + auto-mitigate; reduce re-notify by widening frequency |
| 13 | Dynamic-threshold alert fires constantly after a launch | Model still learning the new normal after a step change | Rule created/changed recently; traffic stepped up | Allow ~3 days to re-learn; pair with failing-periods to ride the transition |
| 14 | Email alerts stopped arriving during a storm | Hit the per-address email rate limit | >100 emails/hour to one address → throttled | Route storms to a webhook/queue/ITSM, not an inbox; de-dup upstream |
| 15 | Two alert rules both fire for the same root cause | No correlation between a symptom rule and a cause rule | Both alerts start together every time | Suppress the symptom rule’s notifications; alert on the cause; use processing-rule grouping |
The state and notification reference
When you are mid-storm and an alert’s status looks contradictory, this maps what each combination means:
| monitorCondition | alertState | What it actually means | Likely noise issue |
|---|---|---|---|
| Fired | New | Condition true, untouched | Normal new alert; if it’s a flap, see #1 |
| Fired | Acknowledged | Condition true, a human is on it | Re-notify may still fire (#12) |
| Resolved | New | Condition cleared, never acknowledged | Auto-mitigate worked; nobody closed it (fine) |
| Resolved | Closed | Cleared and closed out | The clean end state |
| Fired | Closed | Closed by a human but condition is true again | Re-opened/re-fired; check flapping (#1) |
Per-symptom detail for the three worst offenders
Flapping (rows 1, 2). The confirming move is always the same: overlay the threshold on the metric chart. If the line bisects the wiggle, the fix order is (a) require persistence with N-of-M periods, (b) widen the window, © raise the threshold or go dynamic — in that order, because persistence and a wider window fix flapping without blinding the alert to a real breach, whereas blindly raising the threshold can hide a slow climb.
Storms (rows 5, 10, 15). The confirming move is the Alerts-blade timeline: a cluster of starts in the same minute is a storm, not independent incidents. Fix order: (a) collapse N-per-resource rules into one multi-resource rule at RG/subscription scope, (b) alert on the cause and suppress the symptoms (alert on the DB being down, not the fifty services that time out because of it), © for correlated classes, an action-group-override processing rule to one quiet channel.
Maintenance pages (rows 6, 11). The confirming move is Azure Service Health change analysis and incident correlation plus the patch calendar — if alert starts line up with deployments or reboots, it is expected. The fix is always a scheduled suppression rule, never a manual disable: the processing rule auto-expires, the manual disable gets forgotten. Row 11 is the single most common way teams blind themselves for weeks.
Best practices
- Set the window to the shortest problem worth paging on. If a 30-second spike is harmless but five sustained minutes is an incident, use a
PT5M+ window. Default toPT5Mfrequency for resource metrics; reservePT1Mfor true availability/heartbeat alerts. - Always require persistence on twitchy metrics. A 4-of-5 failing-periods requirement kills the vast majority of flapping at the cost of a few minutes’ detection lag — almost always the right trade.
- Turn
autoMitigateon for every recoverable metric alert. Permanent red on a dashboard is noise; reserveautoMitigate = falsefor events that need a human to declare “really fixed.” - Use dynamic thresholds for anything seasonal. Request rate, queue depth, login volume, throughput — these have a daily/weekly shape no static line fits. Start at Medium sensitivity.
- Route by severity, not by “everyone.” Sev-0/1 to a pager, Sev-2 to a chat channel, Sev-3/4 to email/dashboards. Never let Sev-3 reach a phone, and never let Sev-0 reach only email.
- One multi-resource rule beats N per-resource rules. Scope a rule at the resource group or subscription so a fleet shares one rule — the noise no longer multiplies by resource count.
- Suppress maintenance windows with scheduled processing rules, never manual disables. Scheduled suppression auto-expires; a disabled rule waits to be forgotten.
- Alert on causes, suppress symptoms. When a dependency outage trips fifty downstream alerts, the cause alert (the dependency is down) is the one to page on; suppress or group the symptom alerts.
- Keep SMS and voice for Sev-0 only. They have low per-number rate limits and a storm fanned to SMS either floods or silently drops. Webhooks/queues absorb bursts.
- Audit suppression and override rules quarterly. A “temporary” mute that outlives its reason is how teams go quietly blind. List every processing rule and justify each.
- Tune to the alert’s job, not one global philosophy. Availability alerts stay sensitive; capacity/trend alerts stay slow and smoothed. Applying one setting to all is the root of both screaming and silent monitors.
- Review the noisiest rules with data, monthly. A KQL query over alert history that ranks rules by fire count tells you exactly where to spend tuning effort — usually two or three rules generate most of the noise.
Security notes
Noise tuning touches who-gets-told, so it has a real security and governance dimension.
- Least privilege on rules and action groups. Monitoring Contributor can manage alert rules and action groups; Monitoring Reader can only view. Scope Monitoring Contributor to the relevant resource group rather than granting broad Contributor to let someone tune an alert — see Azure RBAC custom roles: least-privilege actions and notActions.
- Suppression rules are a denial-of-visibility risk. A broad or permanently-on suppression rule can silence critical alerts — the equivalent of disabling an IDS. Treat subscription-wide suppression as a privileged, reviewed action, and alert on the creation of new processing rules via an activity-log alert.
- Protect webhook and Logic App targets. Webhook receivers POST alert payloads (resource names, metric values, context) to a URL — use the secure webhook option (Entra ID-authenticated), never a plain URL with embedded secrets. For Logic App or runbook actions use managed identity, not stored credentials — see Managed identity: system-assigned vs user-assigned patterns.
- Mind PII in notifications. Emails and webhook payloads can leak environment topology or sensitive identifiers via resource names, tags and dimension values. Route them to controlled channels, and prefer ITSM/secure-webhook over personal inboxes for anything sensitive.
- Audit who changed a rule. Every rule and action-group change lands in the activity log. After an incident where “the alert didn’t fire,” the first question is who changed or disabled it — an activity-log query answers it.
Cost & sizing
Alert noise is also a cost line, and tuning quiet usually saves money as a side effect. The drivers:
| Cost driver | What you pay for | How tuning reduces it | Rough figure |
|---|---|---|---|
| Metric alert rules | Per monitored time series / rule | One multi-resource rule instead of N; fewer rules | A few ₹ per rule-monitored-series per month |
| Log search alerts | Per rule, varies by frequency | Slower frequency = fewer evaluations | Higher than metric alerts; frequency-sensitive |
| Action-group notifications | Per email/SMS/voice/webhook | Severity routing; de-dup; suppress storms | Email cheap; SMS/voice priced per message/call |
| Dynamic thresholds | Same as metric alerts | No premium for dynamic vs static | No extra cost over a static metric alert |
| Alert processing rules | No per-rule charge for the rule itself | — | Effectively free to add |
| Log ingestion (for log alerts) | Per GB into the workspace | Tighter queries, less scanned | The dominant Monitor cost at scale |
Sizing guidance in one line each: default evaluationFrequency to PT5M (only critical alerts justify PT1M); always prefer one multi-resource rule over per-resource rules (cheaper and quieter); keep SMS/voice for Sev-0 since they are priced per message/call; alert on a metric rather than a log query when one exists, since metric alerts are cheaper per evaluation. Azure Monitor also includes a free monthly allotment of alert rules and notifications, so small environments may pay nothing.
The cost insight that surprises teams: the biggest savings from noise tuning are often not the evaluations but the downstream — fewer SMS/voice notifications (individually priced), and for log alerts, less data scanned. A single log-search alert running every minute over a large workspace can cost more than dozens of metric alerts. Match the alert type to the condition and the frequency to the urgency, and the bill follows the noise down.
Interview & exam questions
Q: What is the difference between evaluationFrequency and windowSize on a metric alert?
A: evaluationFrequency is how often the rule runs (every 1/5/15/30/60 min); windowSize is how much trailing data each run aggregates. A rule can evaluate every 5 minutes but average the last 15 — frequent checks over smoothed data. Confusing the two leads to over-sensitive alerts.
Q: A CPU alert fires and resolves dozens of times an hour. What is happening and how do you fix it? A: That is flapping — the threshold sits inside the metric’s normal jitter, so every up/down crossing fires and resolves. Fix by requiring multiple failing periods (e.g. 4 of 5), widening the window, raising the threshold above the band, or switching to dynamic thresholds. Don’t just raise the threshold blindly, which can hide a real climb.
Q: When would you choose a dynamic threshold over a static one? A: For metrics with daily or weekly seasonality (request rate, queue depth, login volume) where “normal” varies by time, and for fleets where each resource has a different baseline. Dynamic thresholds learn the historical pattern and alert on deviation; static thresholds fit hard limits like disk 90%.
Q: An alert fired three days ago and is still showing as fired even though the problem is gone. Why?
A: autoMitigate is off, so the alert will not auto-resolve when the condition clears — it stays fired until a human closes it. Set autoMitigate = true for recoverable conditions; keep it off only for events that need explicit human sign-off.
Q: What is an alert processing rule and when do you use one? A: A layer above alert rules that intercepts fired alerts to either suppress notifications or override the action group. Use suppression for maintenance windows and known-noisy scopes; use action-group override to re-route a class of alerts (e.g. send Sev-3 dev alerts to a muted channel) without editing each rule.
Q: Forty alerts fire at once during a region blip. How do you reduce that to one actionable signal? A: This is an alert storm. Alert on the cause (the dependency/region) and suppress the symptoms; consolidate per-resource rules into one multi-resource rule; enable action-group grouping to de-dup; and add an action-group-override processing rule to re-route the correlated class to a single quiet channel.
Q: How do you stop alerts from paging the on-call during a planned maintenance window? A: Create a scheduled suppression alert processing rule scoped to the affected resource group (or subscription) for the window. The alerts still fire and remain visible for audit, but no notifications go out. Never disable rules manually — you will forget to re-enable them.
Q: What is the difference between the monitor condition and the alert state? A: The monitor condition (Fired/Resolved) is the platform’s verdict on whether the rule’s condition is currently true. The alert state (New/Acknowledged/Closed) is the human workflow status. They are orthogonal: an alert can be Resolved (condition cleared) yet still Acknowledged or open in the workflow.
Q: Your log search alert re-notifies every single evaluation. Why, and how do you fix it? A: A number-of-results log alert that lacks resolve/state behaviour effectively fires fresh every evaluation the query matches — it is not de-duplicated like a stateful metric alert. Fix by using the metric-measurement mode with resolve behaviour, or widen the evaluation frequency so it does not re-notify every minute.
Q: Why might a critical (Sev-0) alert end up in a channel nobody watches? A: Either the rule’s severity is mis-set, or an alert processing rule with an action-group override caught it and re-routed it. Confirm the rule severity and list any processing rules touching the scope; scope overrides to exclude Sev-0/1 so they never silence a critical alert.
Q: How do you decide whether to tune an alert to be more or less sensitive? A: By the alert’s job. Availability/heartbeat alerts stay sensitive (fast frequency, few periods) because a missed minute matters. Capacity/trend alerts go slow and smoothed because minutes of lag are irrelevant and flapping is pure noise. Applying one philosophy to all alerts is the root cause of both screaming and silent monitors.
Q (cert mapping): This material maps to AZ-104 (Monitor and maintain Azure resources — configure and interpret alerts and action groups), AZ-204 (instrument and monitor solutions with Application Insights), and the SRE/AZ-400 mindset around actionable alerting and reducing toil.
Quick check
- A rule evaluates every 1 minute over a 1-minute window with a single evaluation period, on a metric that spikes briefly. What kind of noise will it produce and what is the first dial to change?
- True or false: turning
autoMitigateon makes an alert fire less often. - You need an alert on request volume that does not fire during the normal weekday-morning peak but does fire on a genuine anomaly. Static or dynamic threshold?
- You are patching a 24-VM fleet tonight and don’t want to be paged for the reboots. What is the correct mechanism — and what should you not do?
- Forty alerts fire in the same minute from one dependency outage. Name two techniques to collapse that into a single actionable signal.
Answers
- Flapping / over-sensitivity — brief spikes will fire and resolve repeatedly. The first dial is persistence: require N-of-M failing periods (e.g. 4 of 5), and/or widen the
windowSize. This kills flapping without raising the threshold blindly. - False.
autoMitigatecontrols whether an alert auto-resolves when the condition clears; it does not change how often the alert fires. It fixes the “never closes / permanent red” problem, not flapping. - Dynamic threshold. It learns the metric’s seasonal pattern (the weekday-morning peak is normal) and fires on deviation from the learned band, which a static line cannot do without either false-firing every morning or missing overnight anomalies.
- Create a scheduled suppression alert processing rule scoped to the resource group for the maintenance window — the alerts still fire and stay visible for audit, but no notifications go out. Do not manually disable the rules; you will forget to re-enable them (the most common way teams accidentally go blind).
- Any two of: alert on the cause and suppress the symptoms; consolidate per-resource rules into one multi-resource rule; enable action-group grouping/de-dup; add an action-group-override processing rule to re-route the correlated class to one quiet channel.
Glossary
- Alert rule — The definition of what to monitor, how often, and what condition trips it (the metric/query,
evaluationFrequency,windowSize,threshold). - Evaluation frequency (
evaluationFrequency) — How often the alert rule runs its evaluation (PT1M, PT5M, PT15M, PT30M, PT1H). - Window size (
windowSize) — The trailing period of data each evaluation aggregates before comparing to the threshold; distinct from frequency. - Threshold — The value the aggregated metric is compared against in a static-threshold rule.
- Dynamic threshold — A machine-learned normal band (chosen by sensitivity, not a fixed number) that adapts to a metric’s seasonality and alerts on deviation.
- Monitor condition — The platform’s verdict at each evaluation: Fired (condition true) or Resolved (cleared).
- Alert state — The human workflow status: New, Acknowledged, or Closed; orthogonal to monitor condition.
- Auto-mitigate (
autoMitigate) — Whether a stateful alert automatically resolves when its condition clears; off means it stays fired until closed by hand. - Flapping — Rapid, repeated fire/resolve cycling, usually because the threshold sits inside the metric’s normal oscillation band.
- Hysteresis (N-of-M periods) — Requiring the condition to fail across multiple consecutive evaluation periods before firing, so brief excursions are ignored.
- Stateful vs stateless alert — Stateful alerts track state and de-duplicate ongoing conditions (one alert per incident); stateless alerts fire fresh every matching evaluation.
- Action group — A reusable, named collection of notification/automation receivers (email, SMS, voice, webhook, Logic App, ITSM) that an alert triggers.
- Alert processing rule — A rule above the alert layer that suppresses notifications (e.g. maintenance windows) or overrides/adds action groups for a class of alerts.
- Severity (Sev 0–4) — The alert’s urgency level, from 0 (critical) to 4 (verbose); drives routing and whether it should page.
- Alert storm — A burst of many alerts triggered by one underlying event (a region blip, a dependency outage, a deployment), which buries the root-cause alert.
Next steps
- If your alerts and action groups are not yet set up at all, start with Azure Monitor metric alert with action group email and SMS setup and come back to tune them.
- To investigate alert history and rank your noisiest rules with data, learn the query language in KQL for Log Analytics: summarize, join, and time-series queries.
- For the deeper observability picture — where availability tests and failure-rate alerts originate — read Azure Monitor and Application Insights for observability.
- To decide when a condition belongs to a metric alert versus a log query alert, see Azure Monitor metrics vs Log Analytics: when to use which.
- When a storm is platform-driven rather than yours, correlate it against Azure-side events with Azure Service Health change analysis and incident correlation.