Back to blog
AzureBest PracticesCloud SecurityMonitoring & LoggingOperations & Compliance

No Alert for Azure Policy Assignment Changes: Why It Matters and How to Fix It

Learn why missing activity log alerts for Azure policy assignment changes is a security risk, and how to detect, fix, and prevent it with CLI and Terraform.

TL;DR

This check flags Azure subscriptions that have no activity log alert watching for Azure Policy assignment changes. Without it, someone can quietly disable or weaken your guardrails and you find out too late. Fix it by creating an activity log alert on the Microsoft.Authorization/policyAssignments/write and /delete operations.

Azure Policy is one of the few controls that actually enforces your security posture rather than just reporting on it. It blocks public storage accounts, requires encryption, denies untagged resources, and audits hundreds of other conditions. The problem is that policy assignments are mutable, and the people who can read your policies can often modify them too. When a policy assignment is changed or deleted, the enforcement disappears, and nothing in the platform shouts about it by default.

The No Alert for Policy Assignment Changes check looks at your subscription's monitoring configuration and verifies that an activity log alert exists for policy assignment write and delete events. If none is found, the check fails.


What this check detects

Every control-plane operation in Azure is recorded in the Activity Log. Creating, updating, or removing a policy assignment generates an entry under the operation names:

  • Microsoft.Authorization/policyAssignments/write — create or modify an assignment
  • Microsoft.Authorization/policyAssignments/delete — remove an assignment

An activity log alert is a rule that watches these entries and fires a notification, through an action group, when a matching event appears. This check confirms that such a rule exists and is enabled. If your subscription logs the events but no alert is configured to act on them, the check still fails, because logs nobody reads are not detection.

Note: Activity log alerts are different from metric alerts and log search alerts. They run against the Activity Log feed specifically, they are free, and they evaluate in near real time. There is no Log Analytics workspace or query cost involved.


Why it matters

Think about what a policy assignment actually does. Suppose you have an assignment that denies the creation of storage accounts with public blob access. That single assignment is the only thing standing between an engineer and an accidental data leak. If an attacker, or a careless administrator, deletes that assignment, the deny stops applying. The next storage account created with public access goes through without complaint.

Here is the realistic attack path. An adversary who gains a foothold with Owner or Contributor rights, or with a custom role that includes Microsoft.Authorization/policyAssignments/*, rarely starts by exfiltrating data. They start by clearing obstacles. Removing a deny policy or flipping an enforced policy to audit-only mode lets them stage further actions without tripping preventive controls. Because policy changes are quiet, this step often goes unnoticed for weeks.

The business impact lands in a few places:

  • Compliance drift. Frameworks like CIS, PCI DSS, and ISO 27001 expect enforced controls. A removed assignment can silently break a control you certified against.
  • Blast radius growth. One disabled guardrail rarely causes the breach by itself, but it widens what a single compromised credential can do.
  • Delayed detection. Without an alert, the gap between the change and your awareness of it is measured in your audit cadence, not in minutes.

Warning: A legitimate policy refactor (someone reorganizing assignments at a management group level) will also trigger this alert. That is expected and useful. The goal is visibility, not zero events. Route the alerts to a channel your team actually triages so the signal does not get ignored.


How to fix it

You need two things: an action group to receive the notification, and the activity log alert itself scoped to policy assignment operations.

1. Create an action group

If you already have an action group for security alerts, skip this and reuse its resource ID.

az monitor action-group create \
  --name "sec-policy-alerts" \
  --resource-group "rg-monitoring" \
  --short-name "secpol" \
  --action email secops [email protected]

2. Create the activity log alert

Activity log alerts match a single operation per rule, so create one for writes and one for deletes. The example below covers the write operation. Repeat it with the delete operation name for full coverage.

# Get your subscription ID and action group ID first
SUB_ID=$(az account show --query id -o tsv)
AG_ID=$(az monitor action-group show \
  --name "sec-policy-alerts" \
  --resource-group "rg-monitoring" \
  --query id -o tsv)

# Alert on policy assignment writes
az monitor activity-log alert create \
  --name "alert-policy-assignment-write" \
  --resource-group "rg-monitoring" \
  --scope "/subscriptions/$SUB_ID" \
  --condition category=Administrative \
    and operationName=Microsoft.Authorization/policyAssignments/write \
  --action-group "$AG_ID" \
  --description "Fires when a policy assignment is created or modified"

# Alert on policy assignment deletes
az monitor activity-log alert create \
  --name "alert-policy-assignment-delete" \
  --resource-group "rg-monitoring" \
  --scope "/subscriptions/$SUB_ID" \
  --condition category=Administrative \
    and operationName=Microsoft.Authorization/policyAssignments/delete \
  --action-group "$AG_ID" \
  --description "Fires when a policy assignment is deleted"

Doing it in the portal

  1. Open MonitorAlertsCreateAlert rule.
  2. Set the Scope to your subscription.
  3. Under Condition, choose the signal type Activity Log, then select Create policy assignment (policyAssignments).
  4. Attach an action group under Actions.
  5. Name the rule and create it. Repeat for the delete operation.

Note: Scope these alerts at the management group or subscription level depending on where you assign policies. If most of your policies are assigned at a management group, an alert scoped only to one subscription will miss those higher-level changes.


How to prevent it from happening again

Creating the alert by hand on one subscription does not scale, and it drifts the moment someone provisions a new subscription. Bake it into your infrastructure as code so every subscription gets the alert at landing-zone time.

Terraform

resource "azurerm_monitor_action_group" "sec_policy" {
  name                = "sec-policy-alerts"
  resource_group_name = "rg-monitoring"
  short_name          = "secpol"

  email_receiver {
    name          = "secops"
    email_address = "[email protected]"
  }
}

resource "azurerm_monitor_activity_log_alert" "policy_write" {
  name                = "alert-policy-assignment-write"
  resource_group_name = "rg-monitoring"
  location            = "global"
  scopes              = [data.azurerm_subscription.current.id]
  description         = "Policy assignment created or modified"

  criteria {
    category       = "Administrative"
    operation_name = "Microsoft.Authorization/policyAssignments/write"
  }

  action {
    action_group_id = azurerm_monitor_action_group.sec_policy.id
  }
}

Add a second azurerm_monitor_activity_log_alert block for the delete operation and put both in a module you apply to every subscription.

Tip: Use Azure Policy to enforce Azure Policy monitoring. The built-in policy initiative Deploy Diagnostic Settings family plus a DeployIfNotExists custom policy can auto-create these activity log alerts on any subscription that lacks them. That turns a one-time fix into a self-healing guardrail.

Gate it in CI/CD

If you manage monitoring through Terraform or Bicep, add a check to your pipeline that fails when the policy assignment alert resources are missing from the plan. A simple grep on the plan output, or a tool like Conftest with an OPA rule, keeps the alert from being removed in a future refactor.


Best practices

  • Alert on the whole authorization surface, not just assignments. Pair this with alerts on role assignment changes (Microsoft.Authorization/roleAssignments/write and /delete) and policy definition changes. Together they cover the most common ways guardrails get weakened.
  • Restrict who can modify assignments. Limit Microsoft.Authorization/policyAssignments/* to a small set of platform roles. The fewer principals that can change policy, the rarer and more meaningful each alert becomes.
  • Forward events to a SIEM. Export the Activity Log to a Log Analytics workspace or Event Hub so policy changes land in Microsoft Sentinel or your SIEM of choice. Alerts handle the immediate ping, the SIEM gives you correlation and retention.
  • Review enforcement mode, not just existence. An assignment flipped from Default (enforce) to DoNotEnforce still exists, but it no longer blocks anything. The write alert catches this, so make sure your triage process reads the changed properties, not only the operation name.
  • Test the alert. Make a benign change to a non-critical assignment in a sandbox subscription and confirm the notification arrives. An alert nobody has verified is a comforting placebo, not a control.

Danger: Do not delete or disable existing policy assignment alerts to quiet noisy notifications during a large migration. Suppressing the alert removes your visibility at exactly the moment when the most assignments are changing. Instead, temporarily widen the triage scope or route to a dedicated migration channel, and restore normal routing when the work is done.

Policy assignment alerting is cheap, free in fact, and quick to deploy. The payoff is that the next time someone weakens a guardrail, whether by mistake or on purpose, you hear about it in minutes instead of discovering it during your next audit.