The resources you only discover are unmonitored during an outage are the ones nobody set an alert rule on. A production SQL database or Key Vault can sit for months with zero metric or log alerts pointed at it, and nothing in the portal flags that. Azure Monitor will happily show you every alert rule you have; it has no view that answers the opposite question: "which of my production resources have no alerts at all?"
Why Azure cannot tell you this out of the box
- The Alerts experience is rule-centric, not resource-centric. It lists the alert rules that exist and the alerts that fired. A resource with nothing watching it simply never appears, which is exactly the failure mode you are trying to catch.
- The mapping lives on the rule, not the resource. An alert rule stores the
resources it evaluates in its own
properties.scopes[]. The resource has no back-reference to the alerts that watch it, so there is no per-resource "coverage" property to filter or sort on. - Broad scopes do not mean broad coverage. A metric alert scoped at a
resource group or subscription is restricted to a single resource type
(
targetResourceType) and a single region (targetResourceRegion). A log alert scoped to a Log Analytics workspace watches whatever its KQL query says, which may be one resource or none of yours. Eyeballing scopes gives false confidence.
The DIY approach: two Resource Graph queries and a diff
All the data you need is queryable with Azure Resource Graph and nothing more than Reader access. The shape of the job:
- Query 1, the inventory. Pull every resource of a type worth alerting on: SQL databases, storage accounts, App Service sites, Cosmos DB accounts, Key Vaults, Redis caches, Service Bus namespaces, Application Gateways, Front Door / CDN profiles, virtual machines. Project id, type, and region.
- Query 2, the alert scopes. Pull every metric alert
(
microsoft.insights/metricalerts) and log / scheduled-query alert (microsoft.insights/scheduledqueryrules), expandproperties.scopes[], and keep the target type and region fields. - Diff them, carefully. A metric alert scoped to a specific resource id covers that one resource. A multi-resource metric alert covers only the resources under its scope whose type and region match the rule. Credit a log alert only when it is scoped directly to the resource id; a workspace- or subscription-scoped log alert has indeterminate coverage. Do not count activity-log alerts as metric or health coverage at all: they are a different alert type.
- Run it on a schedule (a Function on a weekly timer is plenty) and post the uncovered list somewhere people look, such as a Teams channel. In the portal you can spot check any resource via Monitoring → Alerts → Alert rules, but only one resource at a time, which is why the sweep needs to be a job.
The one rule that keeps the report honest: when in doubt, count the resource as uncovered. A report that wrongly says "covered" is worse than no report, because it tells you a resource is monitored when it is not, and that is the dangerous direction.
A ready-made option: Alert Blindspot
Alert Blindspot is that pattern packaged. Every Monday it runs the two Resource Graph queries under a system-assigned managed identity with Reader access only, applies the conservative coverage rules above (including the multi-resource type-and-region restriction and the indeterminate broad log alerts, which it surfaces as an informational note), and posts an Adaptive Card to a Teams webhook you configure: an overall coverage percentage plus a per-type breakdown, worst coverage first, naming exactly which resources are blind. It is report-only and never modifies anything, and the built-in resource-type list can be overridden per tenant.
Key facts
- Native "resources with no alerts" view: none. Azure Monitor lists rules, not the resources no rule watches.
- Data source: Azure Resource Graph has both halves, the resource inventory
and every alert rule's
properties.scopes[], readable with Reader only. - Coverage traps: multi-resource metric alerts are limited to one type and one region; broad log alerts depend on their KQL; activity-log alerts are not metric coverage.
- Honest default: err toward uncovered. A false "covered" hides the exact gap you built the report to find.
Related reading: find orphaned Azure resources still costing you money.
Written by the team at Katabarwa Labs. We build small, single-purpose Azure tools that run entirely inside your own tenant. Questions: abaho@llmgraph.ai.