Home / Azure

AzureHow-toIntermediate

Azure Policy remediation tasks: fixing existing non-compliant resources with deployIfNotExists and modify

Assigned a deployIfNotExists or modify policy and old resources are still non-compliant? Give the assignment an identity, create a remediation task and read why it fails.

Deepak Bhardwaj5 min read

Applies toAzure Policy, Azure CLI 2.x, deployIfNotExists and modify effects

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 modify remediation updates live resources and a deployIfNotExists remediation 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 deployIfNotExists or modify assignment, 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 nonCompliantResources per assignment in a dashboard; a number that never falls usually means a broken identity or existence condition.