The problem
You assign a policy such as "Deploy diagnostic settings to Log Analytics" or "Add a tag to resources". New resources get fixed as they are created or updated. Everything that already existed stays Non-compliant, sometimes for months.
That is by design. deployIfNotExists and modify only act automatically during create and update requests. For existing resources you have to run a remediation task.
| Effect | Fixes new/updated resources automatically | Fixes existing resources |
|---|---|---|
deny, audit | n/a (blocks or reports only) | No remediation possible |
deployIfNotExists | Yes, runs a template deployment after the request | Remediation task |
modify | Yes, alters the request | Remediation task |
Step 1: check the assignment has an identity and roles
Remediation runs as the assignment's managed identity. No identity, no remediation.
ASSIGN_ID=$(az policy assignment show --name diag-to-law \
--scope /subscriptions/<sub-id> --query id -o tsv)
az policy assignment identity show --name diag-to-law \
--scope /subscriptions/<sub-id>
If the result is empty, add a system-assigned identity:
az policy assignment identity assign --system-assigned \
--name diag-to-law --scope /subscriptions/<sub-id> \
--identity-scope /subscriptions/<sub-id> --role "Contributor"
The roles the identity needs are listed in the policy definition under roleDefinitionIds. Grant exactly those, at a scope that covers the resources and anything the deployment touches (for example a Log Analytics workspace in another resource group):
az policy definition show --name <definition-name> \
--query "policyRule.then.details.roleDefinitionIds"
PRINCIPAL=$(az policy assignment show --name diag-to-law \
--scope /subscriptions/<sub-id> --query identity.principalId -o tsv)
az role assignment create --assignee-object-id $PRINCIPAL \
--assignee-principal-type ServicePrincipal \
--role "Log Analytics Contributor" \
--scope /subscriptions/<sub-id>/resourceGroups/rg-monitoring
Tip
The portal grants these roles for you when you tick "Create a remediation task" during assignment. Assignments created by CLI, Bicep or Terraform do not, which is the most common reason remediation fails with
AuthorizationFailed.
Step 2: make sure compliance data is current
Remediation works on the resources that compliance evaluation marked as non-compliant. A new assignment can take a while to be evaluated. Trigger a scan rather than wait:
az policy state trigger-scan --resource-group rg-app-prod # or omit for the subscription
az policy state summarize --policy-assignment diag-to-law \
--query "results.nonCompliantResources"
List what will be remediated:
az policy state list --policy-assignment diag-to-law \
--filter "complianceState eq 'NonCompliant'" \
--query "[].resourceId" -o tsv
Step 3: create the remediation task
For a single policy assignment:
az policy remediation create --name remediate-diag-20261027 \
--policy-assignment $ASSIGN_ID \
--resource-discovery-mode ReEvaluateCompliance
For an initiative, you remediate one policy inside it at a time, identified by its reference ID:
az policy assignment show --name security-baseline --scope /subscriptions/<sub-id> \
--query policyDefinitionId -o tsv
az policy set-definition show --name <initiative-name> \
--query "policyDefinitions[].policyDefinitionReferenceId"
az policy remediation create --name remediate-tags \
--policy-assignment <initiative-assignment-id> \
--definition-reference-id <reference-id>
--resource-discovery-mode ReEvaluateCompliance re-checks resources before fixing them instead of trusting the last scan. It is slower but avoids acting on stale data.
Warning
A
modifyremediation updates live resources and adeployIfNotExistsremediation deploys templates into production. Run it on one resource group first with--resource-group, check the result, then widen the scope.
Step 4: follow progress and read failures
az policy remediation show --name remediate-diag-20261027 \
--query "{state:provisioningState, summary:deploymentStatus}"
az policy remediation deployment list --name remediate-diag-20261027 \
--query "[?status=='Failed'].{resource:remediatedResourceId, error:error.message}" -o table
| Error in the deployment | Cause | Fix |
|---|---|---|
AuthorizationFailed | Identity lacks a role, or has it at the wrong scope | Grant the roleDefinitionIds roles at a scope that covers the target and dependencies |
InvalidTemplate / parameter errors | Assignment parameters wrong (e.g. workspace ID typo) | Fix the assignment parameters, then create a new task |
| Task finishes, resources still non-compliant | Compliance not re-evaluated yet, or the existenceCondition never matches what the template deploys | Trigger a scan; compare the deployed setting with the existence condition |
RequestDisallowedByPolicy | A deny policy blocks what remediation tries to deploy | Align the two policies or add an exemption |
Task stuck in Evaluating | Very large scope | Limit with --resource-group or --resource-count, run in batches |
Cancel a task that is going wrong:
az policy remediation cancel --name remediate-diag-20261027
Controlling the blast radius
az policy remediation create accepts --resource-count (how many resources to remediate), --parallel-deployments and --failure-threshold (stop when a percentage of deployments fail). For a first run in production:
az policy remediation create --name remediate-tags-pilot \
--policy-assignment $ASSIGN_ID --resource-group rg-app-dev \
--resource-count 50 --parallel-deployments 5 --failure-threshold 0.1
Limits and defaults change, so check az policy remediation create --help on your CLI version.
Prevent it next time
- Create assignments as code with the identity and the role assignments in the same template, so remediation is possible from day one.
- After every new
deployIfNotExistsormodifyassignment, schedule a remediation task as part of the change, not as a follow-up ticket. - Re-run remediation periodically (for example monthly from a pipeline) to catch resources that drifted while the policy was disabled or exempted.
- Track
nonCompliantResourcesper assignment in a dashboard; a number that never falls usually means a broken identity or existence condition.