Skip to content

Azure Monitor Alerts — Operations Runbook

Scope: PROD tenant d477c9f8 (ciriusgroup.com) — infrastructure health alerting only LAW: cirius-logging-law-central (ID: 5d76d1f2, rg-logging-logs, logging subscription) Not in scope: Security events — those go through the 17-agent pipeline to SecOps. Azure Monitor here covers infrastructure health: VM down, disk full, backup failure, quota exhaustion.


1. Azure Monitor Architecture at Cirius

Signal Flow

Azure Resources (VMs, RSVs, Container Apps, Storage, Key Vault, Azure OpenAI)

       ├── Platform Metrics (1-min granularity, automatic, no config required)
       │       → Azure Monitor Metrics Store (93-day hot retention)
       │       → Metric Alert Rules evaluate against thresholds

       ├── Resource Logs / Diagnostic Settings
       │       → LAW: cirius-logging-law-central (5d76d1f2)
       │       → Tables: AzureDiagnostics, AzureActivity, ContainerAppConsoleLogs, etc.
       │       → Scheduled Query Rules (Log Alert Rules) run KQL over these tables

       └── Activity Log (subscription-level control plane events)
               → LAW via diagnostic settings on each subscription
               → Table: AzureActivity

Alert Rule fires

       └── Action Group
               ├── Email: rgarshol@gmail.com
               └── Webhook (optional): Telegram bot endpoint

What Goes Where

Signal TypeTransportDestinationLatency
VM CPU / memory / disk metricsPlatform metrics → metric alertAction group~1 min
Backup job statusAzureDiagnostics in LAWScheduled query rule~5-15 min
Container App restartsContainerAppSystemLogs in LAWScheduled query rule~5 min
Key Vault throttle / availabilityPlatform metricsMetric alert~1 min
Storage availabilityPlatform metricsMetric alert~1 min
LAW ingestion cap hitPlatform metrics on LAWMetric alert~5 min
Azure OpenAI 429 errorsPlatform metricsMetric alertSee azure-openai-operations.md

Division of Labour — Azure Monitor vs Agent Pipeline

Azure Monitor is infrastructure health. The 17-agent security monitoring pipeline is security events. Do not route security alerts through Azure Monitor.

Alert CategorySystem
VM down, disk full, high CPUAzure Monitor
Backup failureAzure Monitor (also covered by operational agent)
Key Vault unavailableAzure Monitor
Suspicious login, impossible travel17-agent pipeline → SecOps
New persistence (scheduled task, service)17-agent pipeline → SecOps
Log cleared (EventID 1102)17-agent pipeline → SecOps

Arctic Wolf monitors Azure via its connector and receives its own alert stream — Azure Monitor action groups do not forward to Arctic Wolf. Arctic Wolf has independent visibility.


2. Action Groups

What Action Groups Exist

Cirius uses a single central action group for infrastructure alerts in PROD:

Action GroupResource GroupSubscriptionPurpose
ag-infra-alerts-prodrg-logging-logsLoggingAll infra health alerts

One action group is enough for current scale. If alert volume grows, split by severity (critical vs warning) with different receiver lists.

Create Action Group in Terraform

hcl
resource "azurerm_monitor_action_group" "infra_alerts_prod" {
  name                = "ag-infra-alerts-prod"
  resource_group_name = "rg-logging-logs"
  short_name          = "InfraAlert"

  email_receiver {
    name          = "rory"
    email_address = "rgarshol@gmail.com"
  }

  # Optional: Telegram via Logic App or custom webhook endpoint
  webhook_receiver {
    name        = "telegram"
    service_uri = var.telegram_webhook_url   # stored in Key Vault, passed via TF var
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

Create Action Group in Portal (one-off, then import to Terraform)

  1. Portal → Monitor → Alerts → Action groups → Create
  2. Subscription: Logging | Resource group: rg-logging-logs
  3. Name: ag-infra-alerts-prod | Display: InfraAlert
  4. Notifications tab → Email/SMS/Push/Voice → Email: rgarshol@gmail.com
  5. (Optional) Actions tab → Webhook → paste Telegram endpoint URL
  6. Review + Create

After portal creation, import before writing Terraform:

bash
terraform import azurerm_monitor_action_group.infra_alerts_prod \
  /subscriptions/<logging-sub-id>/resourceGroups/rg-logging-logs/providers/microsoft.insights/actionGroups/ag-infra-alerts-prod

Test an Action Group

Portal:

  1. Monitor → Alerts → Action groups → select ag-infra-alerts-prodTest action group
  2. Select notification type (Email) → Test
  3. Check inbox — arrives within ~2 minutes

CLI:

bash
az monitor action-group test \
  --name ag-infra-alerts-prod \
  --resource-group rg-logging-logs \
  --alert-type email

You will receive a test email from azure-noreply@microsoft.com with subject [Test Notification]. If email doesn't arrive in 5 minutes, check spam and confirm the action group was saved with the correct address.


3. Metric Alerts

Metric alerts evaluate platform metrics against a threshold. No LAW required — data comes directly from the metrics store. Evaluation frequency: 1–5 minutes. Good for: CPU, memory, disk, storage availability, Key Vault throttling.

How to Create — Terraform (azurerm_monitor_metric_alert)

hcl
# VM CPU > 90% for 15 minutes
resource "azurerm_monitor_metric_alert" "vm_cpu_high" {
  name                = "alert-vm-cpu-high-prod"
  resource_group_name = "rg-logging-logs"
  scopes              = [azurerm_windows_virtual_machine.example.id]
  description         = "VM CPU above 90% for 15 minutes"
  severity            = 2   # 0=Critical 1=Error 2=Warning 3=Informational 4=Verbose
  enabled             = true
  frequency           = "PT5M"   # evaluate every 5 min
  window_size         = "PT15M"  # lookback window

  criteria {
    metric_namespace = "Microsoft.Compute/virtualMachines"
    metric_name      = "Percentage CPU"
    aggregation      = "Average"
    operator         = "GreaterThan"
    threshold        = 90
  }

  action {
    action_group_id = azurerm_monitor_action_group.infra_alerts_prod.id
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

How to Create — Portal

  1. Portal → Monitor → Alerts → CreateAlert rule
  2. Scope → select target resource (VM, storage account, etc.)
  3. Condition → select signal → set aggregation, operator, threshold, lookback period
  4. Actions → select ag-infra-alerts-prod
  5. Details → name, resource group (rg-logging-logs), severity
  6. Review + Create
  7. After portal creation, import to Terraform before adding more config.

Common Metric Alert Thresholds

ResourceMetric NameNamespaceAggregationOpThresholdWindow
VMPercentage CPUMicrosoft.Compute/virtualMachinesAverage>90PT15M
VMAvailable Memory BytesMicrosoft.Compute/virtualMachinesAverage<524288000 (500 MB)PT15M
VMOS Disk Free Space %Microsoft.Compute/virtualMachinesAverage<10PT5M
Storage AccountAvailabilityMicrosoft.Storage/storageAccountsAverage<99.9PT5M
Key VaultServiceApiResult (Throttled)Microsoft.KeyVault/vaultsCount>10PT5M
Key VaultAvailabilityMicrosoft.KeyVault/vaultsAverage<100PT5M
LAWOverQuotaInGB (ingestion cap)Microsoft.OperationalInsights/workspacesTotal>0PT1H
Container AppRestartCountMicrosoft.App/containerAppsTotal>3PT1H
Container AppCpuUsageMicrosoft.App/containerAppsAverage>80PT10M

Memory note: Azure VMs report Available Memory Bytes — you are alerting on what is free, not what is used. A threshold of 524288000 = 500 MB free. Adjust per VM RAM size.

VM disk note: OS Disk Free Space % requires the Azure Monitor Agent (AMA) extension with the Disk performance counter enabled. If the metric shows no data, check that AMA is installed and the DCR includes the disk performance counter.


4. Log-Based Alerts (Scheduled Query Rules)

When to Use vs Metric Alert

Use a metric alert when the signal is a numeric metric with native Azure support (CPU, memory, storage availability). Fast, cheap, no LAW dependency.

Use a log alert (Scheduled Query Rule) when:

  • Signal only exists in logs (backup job status, restart reasons, custom app logs)
  • You need to correlate multiple fields (e.g., backup job status AND vault name AND job type)
  • You want KQL aggregation or windowing more complex than a simple threshold
  • The metric doesn't exist natively (backup failures have no platform metric)

Minimum evaluation frequency: 5 minutes (PT5M). The resource type azurerm_monitor_scheduled_query_rules_alert_v2 supports 1-min frequency only on certain workspaces. Use PT5M as the safe default.

Log alert latency: 5–15 minutes behind real time. Do not use for P1 hard-realtime signals.

How to Create — Terraform (azurerm_monitor_scheduled_query_rules_alert_v2)

hcl
# Backup job failure — KQL over AzureDiagnostics
resource "azurerm_monitor_scheduled_query_rules_alert_v2" "backup_job_failed" {
  name                = "alert-rsv-backup-failure-prod"
  resource_group_name = "rg-logging-logs"
  location            = "eastus"
  description         = "RSV backup job failed in last 24 hours"
  enabled             = true
  severity            = 1   # Error
  evaluation_frequency = "PT5M"
  window_duration      = "PT60M"
  scopes               = ["/subscriptions/<logging-sub-id>/resourceGroups/rg-logging-logs/providers/microsoft.operationalinsights/workspaces/cirius-logging-law-central"]

  criteria {
    query = <<-KQL
      AzureDiagnostics
      | where Category == "AzureBackupReport"
      | where OperationName == "Job"
      | where JobStatus_s == "Failed"
      | where TimeGenerated >= ago(1h)
      | summarize FailedJobs = count() by JobType_s, BackupItemUniqueId_s, Resource
    KQL

    time_aggregation_method = "Count"
    threshold               = 0
    operator                = "GreaterThan"

    failing_periods {
      minimum_failing_periods_to_trigger_alert = 1
      number_of_evaluation_periods             = 1
    }
  }

  action {
    action_groups = [azurerm_monitor_action_group.infra_alerts_prod.id]
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

Alert Deduplication

Scheduled Query Rules in Azure Monitor have built-in alert deduplication via the alert state — the same alert rule will not re-fire while it is in a Fired state unless the condition resolves and fires again. This prevents alert spam on persistent conditions.

For additional deduplication on flapping conditions, use auto-mitigate = false combined with alert processing rules (see Section 7).


All alert rules should be defined in the azure-infra Terraform repo under modules/monitor-alerts/. The LAW resource ID for scopes in log alert rules:

/subscriptions/<logging-sub-id>/resourceGroups/rg-logging-logs/providers/microsoft.operationalinsights/workspaces/cirius-logging-law-central

VM Alerts

CPU > 90% for 15 minutes (metric alert, severity 2)

  • Metric: Percentage CPU, aggregation: Average, threshold: 90, window: PT15M
  • Scope: all production VMs (set per-VM or use a resource group scope)

Available Memory < 500 MB (metric alert, severity 2)

  • Metric: Available Memory Bytes, aggregation: Average, threshold: 524288000 (bytes)
  • Window: PT15M, frequency: PT5M
  • Requires: AMA extension installed

OS Disk free < 10% (metric alert, severity 1)

  • Metric: OS Disk Free Space %, aggregation: Average, threshold: 10
  • Window: PT5M, frequency: PT5M
  • Requires: AMA + disk performance counter in DCR

Container App (ca-soc-prod)

Restarts > 3 in 1 hour (metric alert, severity 1)

  • Metric: RestartCount, aggregation: Total, threshold: 3, window: PT1H
  • Scope: ca-soc-prod resource ID in rg-logging-logs
hcl
resource "azurerm_monitor_metric_alert" "ca_soc_restarts" {
  name                = "alert-ca-soc-prod-restarts"
  resource_group_name = "rg-logging-logs"
  scopes              = ["/subscriptions/<logging-sub-id>/resourceGroups/rg-logging-logs/providers/Microsoft.App/containerApps/ca-soc-prod"]
  description         = "ca-soc-prod restarted more than 3 times in an hour"
  severity            = 1
  frequency           = "PT5M"
  window_size         = "PT1H"

  criteria {
    metric_namespace = "Microsoft.App/containerApps"
    metric_name      = "RestartCount"
    aggregation      = "Total"
    operator         = "GreaterThan"
    threshold        = 3
  }

  action {
    action_group_id = azurerm_monitor_action_group.infra_alerts_prod.id
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

CPU > 80% (metric alert, severity 2)

  • Metric: CpuUsage, aggregation: Average, threshold: 80, window: PT10M

Recovery Services Vault — Backup Job Failed

KQL log alert over AzureDiagnostics. Requires diagnostic settings on the RSV to forward to LAW. If AzureDiagnostics has no backup data, check RSV → Diagnostic settings → confirm the AzureBackupReport category is enabled to cirius-logging-law-central.

kql
// Fires when any RSV backup job fails in the last hour
AzureDiagnostics
| where Category == "AzureBackupReport"
| where OperationName == "Job"
| where JobStatus_s == "Failed"
| where TimeGenerated >= ago(1h)
| project TimeGenerated, Resource, JobType_s, BackupItemFriendlyName_s, JobFailureCode_s

Severity: 1 (Error), evaluation: PT5M, window: PT1H, threshold: count > 0.

Storage Account — Availability < 99.9%

hcl
resource "azurerm_monitor_metric_alert" "storage_availability" {
  name                = "alert-storage-availability-prod"
  resource_group_name = "rg-logging-logs"
  scopes              = [azurerm_storage_account.example.id]
  description         = "Storage account availability dropped below 99.9%"
  severity            = 1
  frequency           = "PT5M"
  window_size         = "PT15M"

  criteria {
    metric_namespace = "Microsoft.Storage/storageAccounts"
    metric_name      = "Availability"
    aggregation      = "Average"
    operator         = "LessThan"
    threshold        = 99.9
  }

  action {
    action_group_id = azurerm_monitor_action_group.infra_alerts_prod.id
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

Key Vault — Throttled Requests > 10 in 5 min

hcl
resource "azurerm_monitor_metric_alert" "keyvault_throttle" {
  name                = "alert-kv-throttle-prod"
  resource_group_name = "rg-logging-logs"
  scopes              = ["/subscriptions/<logging-sub-id>/resourceGroups/rg-logging-logs/providers/Microsoft.KeyVault/vaults/cirius-openai-kv-prod"]
  description         = "Key Vault throttling: >10 throttled requests in 5 minutes"
  severity            = 2
  frequency           = "PT1M"
  window_size         = "PT5M"

  criteria {
    metric_namespace = "Microsoft.KeyVault/vaults"
    metric_name      = "ServiceApiResult"
    aggregation      = "Count"
    operator         = "GreaterThan"
    threshold        = 10

    dimension {
      name     = "StatusCodeClass"
      operator = "Include"
      values   = ["4xx"]
    }
  }

  action {
    action_group_id = azurerm_monitor_action_group.infra_alerts_prod.id
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

Key Vault vault unavailable (metric alert, severity 0 / Critical)

  • Metric: Availability, aggregation: Average, threshold: < 100, window: PT5M
  • A Key Vault below 100% availability is an anomaly worth an immediate page.

Log Analytics Workspace — Daily Ingestion Cap Hit

This is a critical alert. If LAW hits its daily data cap, it stops ingesting logs. The 17-agent security monitoring pipeline goes blind. Do not set a hard cap (blocked by CLAUDE.md principle). Instead, alert at the threshold so you can investigate before it would be a problem.

hcl
resource "azurerm_monitor_metric_alert" "law_data_cap" {
  name                = "alert-law-ingestion-cap-prod"
  resource_group_name = "rg-logging-logs"
  scopes              = ["/subscriptions/<logging-sub-id>/resourceGroups/rg-logging-logs/providers/microsoft.operationalinsights/workspaces/cirius-logging-law-central"]
  description         = "LAW daily ingestion cap reached — log ingestion has stopped"
  severity            = 0   # Critical
  frequency           = "PT5M"
  window_size         = "PT1H"

  criteria {
    metric_namespace = "Microsoft.OperationalInsights/workspaces"
    metric_name      = "OverQuotaInGB"
    aggregation      = "Total"
    operator         = "GreaterThan"
    threshold        = 0
  }

  action {
    action_group_id = azurerm_monitor_action_group.infra_alerts_prod.id
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

When this fires: immediately check which tables are driving ingestion (Section 9, KQL queries). Check for a runaway log source. Do not raise the cap without understanding the cause.

Azure OpenAI — Sustained 429 Errors

Cross-reference: azure-openai-operations.md — the alert rule and response steps are documented there. Ensure the action group ag-infra-alerts-prod is wired to that alert rule as well.


6. Alert Management

Viewing Fired Alerts in Portal

Portal → Monitor → Alerts → Alert instances

Filter by:

  • Subscription: Logging (action groups live here; filter by scope for VM-specific alerts)
  • Time range: last 24 hours
  • State: Fired / Acknowledged / Closed

Each alert instance shows: alert rule name, fired time, severity, target resource, condition details (what threshold was breached and the actual value).

Acknowledging an Alert

Acknowledging signals that someone has seen the alert and is investigating. It does not resolve the underlying condition.

Portal: click the alert instance → Change stateAcknowledged → add a comment.

CLI:

bash
# List active alert instances
az monitor alert-instances list \
  --resource-group rg-logging-logs \
  --output table

# Acknowledge (requires the alert instance ID from the list output)
az monitor alert-instances update \
  --ids /subscriptions/<sub-id>/providers/Microsoft.AlertsManagement/alertInstances/<instance-id> \
  --state Acknowledged \
  --comment "Investigating elevated CPU on vm-prod-dc01"

Closing an Alert

Close when the condition is resolved and root cause is understood. Always include a note.

Portal: alert instance → Change stateClosed → enter root cause in the comment.

Required comment format:

Root cause: [what caused it]
Resolution: [what was done]
Recurrence risk: [low/medium/high and why]

Example:

Root cause: vm-prod-dc01 CPU spiked during monthly Windows Update installation.
Resolution: Updates completed, CPU returned to baseline.
Recurrence risk: Low — normal monthly patch cycle. Will recur next Patch Tuesday; create maintenance window.

Do not close without a root cause. Unclosed alerts accumulate and make the Alerts blade useless. Unexplained alerts also create compliance gaps — auditors will ask why no root cause was documented.


7. Alert Noise Management

Alert Processing Rules (Suppression)

Alert processing rules let you suppress or modify actions on alerts matching certain criteria without changing the alert rule itself. Use for:

  • Scheduled maintenance windows
  • Known noisy alerts pending threshold tuning
  • Suppressing non-actionable alerts during off-hours

Create a maintenance window alert processing rule (Terraform):

hcl
resource "azurerm_monitor_alert_processing_rule_suppression" "patch_maintenance" {
  name                = "apr-patch-tuesday-maintenance"
  resource_group_name = "rg-logging-logs"
  description         = "Suppress infra alerts during monthly Patch Tuesday window"
  enabled             = true

  scopes = [
    "/subscriptions/<prod-sub-id>",
    "/subscriptions/<logging-sub-id>"
  ]

  condition {
    severity {
      operator = "Equals"
      values   = ["Sev2", "Sev3", "Sev4"]   # suppress Warning and below
    }
  }

  schedule {
    effective_from  = "2026-06-09T22:00:00"   # second Tuesday + maintenance start
    effective_until = "2026-06-10T06:00:00"
    time_zone       = "Pacific Standard Time"

    recurrence {
      monthly {
        days_of_month = [9, 10, 11, 12, 13, 14, 15]   # catches second Tuesday of any month
        # Combine with day-of-week filter in condition if needed
      }
    }
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

Portal: Monitor → Alerts → Alert processing rules → Create

Important: Alert processing rules suppress the action (email/webhook). The alert still fires and appears in the Alerts blade — it just doesn't send a notification. This means you can still see it after the maintenance window without losing history.

Reducing False Positives — Threshold Guidance

ProblemFix
CPU alert fires during every backup jobIncrease window from PT15M to PT30M, or exclude backup hours via alert processing rule
Memory alert fires on bootAdd 10-minute delay with auto_mitigate = true and a longer window
Backup alert fires for known excluded VMsScope alert rules to specific RSVs, not the whole resource group
Container App alert fires during deploy restartsCreate an alert processing rule tied to CM ticket windows

Flapping Alerts

A flapping alert repeatedly fires and resolves. Two fixes:

  1. Widen the evaluation window. Change PT5M window to PT15M or PT30M. The threshold must be sustained over the longer window before firing.

  2. Raise the threshold. If CPU at 91% for 15 min is genuinely not a problem (it resolves on its own), raise the threshold to 95% or reduce from Average to the P90 percentile (requires a log alert with KQL, not a metric alert).

auto_mitigate = true (the default) means Azure automatically closes the alert when the condition drops below threshold. This is correct behavior — do not set it to false unless you specifically want the alert to persist past resolution.


8. Terraform Patterns

Resource ID Reference

The LAW scope string for log alert rules:

/subscriptions/<logging-sub-id>/resourceGroups/rg-logging-logs/providers/microsoft.operationalinsights/workspaces/cirius-logging-law-central

Store this in a local or variable to avoid repetition:

hcl
locals {
  law_id = "/subscriptions/${var.logging_subscription_id}/resourceGroups/rg-logging-logs/providers/microsoft.operationalinsights/workspaces/cirius-logging-law-central"
}

Full azurerm_monitor_metric_alert Pattern

hcl
resource "azurerm_monitor_metric_alert" "example" {
  name                = "alert-<resource>-<condition>-<env>"
  resource_group_name = "rg-logging-logs"
  scopes              = [var.target_resource_id]
  description         = "Human-readable description of what this alert means"
  severity            = 2        # 0=Critical 1=Error 2=Warning 3=Informational
  enabled             = true
  frequency           = "PT5M"   # PT1M, PT5M, PT15M, PT30M, PT1H
  window_size         = "PT15M"  # must be >= frequency; PT5M to PT1D
  auto_mitigate       = true     # auto-close when condition clears

  criteria {
    metric_namespace = "Microsoft.Compute/virtualMachines"   # exact namespace string
    metric_name      = "Percentage CPU"
    aggregation      = "Average"   # Average, Count, Maximum, Minimum, Total
    operator         = "GreaterThan"
    threshold        = 90

    # Optional: filter by dimension (e.g. disk, container name)
    # dimension {
    #   name     = "vmName"
    #   operator = "Include"
    #   values   = ["*"]
    # }
  }

  action {
    action_group_id = azurerm_monitor_action_group.infra_alerts_prod.id
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

Full azurerm_monitor_scheduled_query_rules_alert_v2 Pattern

hcl
resource "azurerm_monitor_scheduled_query_rules_alert_v2" "example" {
  name                = "alert-sqr-<condition>-<env>"
  resource_group_name = "rg-logging-logs"
  location            = "eastus"
  description         = "Description"
  enabled             = true
  severity            = 1

  # How often the rule evaluates
  evaluation_frequency = "PT5M"   # minimum is PT5M for most workspace types

  # How far back each evaluation looks
  window_duration = "PT1H"

  # Must be the LAW resource ID for KQL-over-LAW alerts
  scopes = [local.law_id]

  # Optional: mute alert re-notifications for this duration after firing
  mute_actions_duration = "PT1H"

  criteria {
    query = <<-KQL
      AzureDiagnostics
      | where Category == "AzureBackupReport"
      | where OperationName == "Job"
      | where JobStatus_s == "Failed"
      | where TimeGenerated >= ago(1h)
      | summarize FailedJobs = count()
    KQL

    time_aggregation_method = "Count"   # Count, Average, Maximum, Minimum, Total
    threshold               = 0
    operator                = "GreaterThan"

    failing_periods {
      minimum_failing_periods_to_trigger_alert = 1
      number_of_evaluation_periods             = 1
    }
  }

  action {
    action_groups = [azurerm_monitor_action_group.infra_alerts_prod.id]
  }

  tags = {
    Environment = "prod"
    Owner       = "infrastructure"
    Project     = "cirius-group"
    ManagedBy   = "terraform"
    Lifecycle   = "permanent"
  }
}

Common Errors

ErrorCauseFix
window_duration must be >= evaluation_frequencyWindow shorter than eval periodSet window_duration >= evaluation_frequency
The scope is not supportedWrong resource type for metric namespaceVerify namespace matches scope resource type
No data in KQL log alertDiagnostic settings missingEnable AzureBackupReport / relevant category on the resource
Threshold must be > 0 for Count aggregationSome aggregations require nonzeroUse >= 1 instead of > 0 for count-type queries

9. Checking Alert History

Portal

Monitor → Alerts → Alert instances

  • Default view shows last 24 hours
  • Filter by time range (up to 30 days)
  • Export to CSV for audit documentation

Monitor → Alerts → Alert rules → click a rule → Alert instance history tab

  • Shows all fires for that specific rule
  • Includes condition value at time of fire

KQL — Fired Alert Events in AzureActivity

AzureActivity captures when alert rules fire as microsoft.insights/alertrules/activated/action events. Query the LAW:

kql
// All alert activations in the last 7 days
AzureActivity
| where TimeGenerated >= ago(7d)
| where OperationNameValue contains "microsoft.insights/alert"
| where ActivityStatusValue == "Succeeded"
| project TimeGenerated, OperationNameValue, ResourceGroup, Resource, Properties
| sort by TimeGenerated desc
kql
// Alert activations with alert name and description
AzureActivity
| where TimeGenerated >= ago(7d)
| where OperationNameValue == "microsoft.insights/alertrules/activated/action"
| extend AlertName = tostring(parse_json(Properties).alertRuleName)
| extend Description = tostring(parse_json(Properties).description)
| project TimeGenerated, AlertName, Description, ResourceGroup
| sort by TimeGenerated desc
kql
// Count alert fires by rule — useful for identifying noisy rules
AzureActivity
| where TimeGenerated >= ago(30d)
| where OperationNameValue == "microsoft.insights/alertrules/activated/action"
| extend AlertName = tostring(parse_json(Properties).alertRuleName)
| summarize FireCount = count() by AlertName
| sort by FireCount desc

Azure CLI — List Alert Rules

bash
# List all metric alert rules in the logging RG
az monitor metrics alert list \
  --resource-group rg-logging-logs \
  --output table \
  --query "[].{Name:name, Enabled:enabled, Severity:severity, Description:description}"

# List all scheduled query rules in the logging RG
az monitor scheduled-query list \
  --resource-group rg-logging-logs \
  --output table

Azure CLI — Check a Specific Alert's Recent State

bash
# Show the current state and last evaluation of a metric alert
az monitor metrics alert show \
  --name alert-vm-cpu-high-prod \
  --resource-group rg-logging-logs

# Get alert instances (fired alerts) for last 24 hours
az monitor alert-instances list \
  --output table

Quick Reference — Resource IDs and Namespaces

ResourceNamespace
Virtual MachinesMicrosoft.Compute/virtualMachines
Container AppsMicrosoft.App/containerApps
Recovery Services VaultsMicrosoft.RecoveryServices/vaults
Storage AccountsMicrosoft.Storage/storageAccounts
Key VaultMicrosoft.KeyVault/vaults
Log Analytics WorkspaceMicrosoft.OperationalInsights/workspaces
Azure OpenAI / Cognitive ServicesMicrosoft.CognitiveServices/accounts
Severity NumberLabel
0Critical
1Error
2Warning
3Informational
4Verbose
Frequency TokenMeaning
PT1M1 minute
PT5M5 minutes
PT15M15 minutes
PT30M30 minutes
PT1H1 hour

Internal use only — Cirius Group