Runs an Azure Policy remediation task against a single resource, bringing existing non-compliant resources into line by executing the assigned policy's effect. Targets
hashicorp/azurerm ~> 4.0.
- 🔧 Runs a remediation task against exactly one resource -- the narrowest scope Azure offers.
- ⚡ Executes the assigned policy's
deployIfNotExistsormodifyeffect — this changes infrastructure. - 🎚️ Exposes the three blast-radius controls and reports when none of them is set.
- 🌍 Can restrict the task to named Azure regions.
- 🚨 Flags
tolerates_total_failure, becausefailure_percentageis a fraction, not a percentage. - ⏱️ Documents why
ReEvaluateCompliancemakesterraform destroyslow — a cancel-and-poll the schema never mentions.
💡 Why it matters: this is the mirror image of a policy exemption. An exemption suppresses evaluation and changes nothing; a remediation reaches into resources Terraform has never managed and modifies them. The two sit next to each other in this library and are easy to confuse.
If this module saved you time:
- ⭐ Star the repository — it genuinely helps other people find it.
- 💼 Connect on LinkedIn — linkedin.com/in/microsoftexpert
- ☕ Buy me a coffee — buymeacoffee.com/microsoftexpert
flowchart TB
DEF["terraform-azurerm-policy-definition"]
subgraph assign["Assignments, one module per scope"]
MGA["terraform-azurerm-management-group-policy-assignment"]
SUBA["terraform-azurerm-subscription-policy-assignment"]
RGA["terraform-azurerm-policy-assignment"]
end
subgraph remed["Remediations, one module per scope, widest first"]
MGR["terraform-azurerm-management-group-policy-remediation"]
SUBR["terraform-azurerm-subscription-policy-remediation"]
RGR["terraform-azurerm-resource-group-policy-remediation"]
RESR["terraform-azurerm-resource-policy-remediation"]
end
EXEMPT["terraform-azurerm-resource-group-policy-exemption"]
TARGET["existing resources, CHANGED in place"]
DEF -->|"only deployIfNotExists or modify can be remediated"| RGA
DEF -->|"policy_definition_id"| MGA
DEF -->|"policy_definition_id"| SUBA
MGA -->|"policy_assignment_id, updatable in place"| MGR
SUBA -->|"policy_assignment_id, updatable in place"| SUBR
RGA -->|"policy_assignment_id, updatable in place"| RGR
RGA -->|"policy_assignment_id, updatable in place"| RESR
RGR -->|"runs the policy effect against"| TARGET
RESR -->|"runs the policy effect against"| TARGET
EXEMPT -.->|"the opposite operation: suppresses evaluation, changes nothing"| RGA
style RGR fill:#0078D4,stroke:#004578,color:#ffffff
style RESR fill:#0078D4,stroke:#004578,color:#ffffff
style RGA fill:#004578,stroke:#004578,color:#ffffff
style TARGET fill:#8A2B06,stroke:#5C1D04,color:#ffffff
style MGR fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style SUBR fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style MGA fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style SUBA fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style DEF fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style EXEMPT fill:#F3F2F1,stroke:#8A8886,color:#201F1E
Azure offers remediations at four scopes as four separate resource types, and this library has one module for each. The dotted edge to the exemption module is the contrast worth holding onto: both attach to an assignment, and they do opposite things.
flowchart TB
SCOPEIN["resource_id: WHERE resources get changed"]
ASSIGNIN["policy_assignment_id: WHICH effect runs"]
MODEIN["resource_discovery_mode: also decides how DESTROY behaves"]
BOUNDS["resource_count, parallel_deployments, failure_percentage"]
FILTERS["location_filters"]
THIS["azurerm_resource_policy_remediation.this"]
EFFECT["deployIfNotExists or modify RUNS against existing resources"]
FACTS["parsed scope facts and posture flags"]
RISK["is_unbounded and tolerates_total_failure"]
CANCEL["destroy may CANCEL and POLL before deleting"]
SCOPEIN -->|"FORCE-NEW"| THIS
ASSIGNIN -->|"NOT force-new at any scope, unlike exemptions"| THIS
MODEIN --> THIS
BOUNDS -->|"all optional: unset means Azure decides"| THIS
FILTERS -->|"the only geographic limit"| THIS
THIS --> EFFECT
THIS --> FACTS
THIS --> RISK
MODEIN -.->|"ReEvaluateCompliance only"| CANCEL
style THIS fill:#0078D4,stroke:#004578,color:#ffffff
style EFFECT fill:#8A2B06,stroke:#5C1D04,color:#ffffff
style RISK fill:#8A2B06,stroke:#5C1D04,color:#ffffff
style CANCEL fill:#8A2B06,stroke:#5C1D04,color:#ffffff
style ASSIGNIN fill:#004578,stroke:#004578,color:#ffffff
style SCOPEIN fill:#004578,stroke:#004578,color:#ffffff
style MODEIN fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style BOUNDS fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style FILTERS fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style FACTS fill:#F3F2F1,stroke:#8A8886,color:#201F1E
This diagram is shared with terraform-azurerm-resource-policy-remediation, deliberately: the two resources are identical in the schema apart from the name of the scope field.
| Resource | Cardinality | Notes |
|---|---|---|
azurerm_resource_policy_remediation.this |
single, named this |
Named by you, so a group may hold several against different assignments. |
| Item | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/azurerm |
~> 4.0 |
| Provider block | None in this module. The caller configures provider "azurerm" { features {} }, auth and subscription. |
Schema notes that bite, each confirmed against the provider's own source at the pinned version:
- 🔴
resource_discovery_mode = "ReEvaluateCompliance"changes how DESTROY behaves. The provider must cancel the running task and poll until the cancellation completes before deleting — and it checks the state first, because cancelling a completed remediation returns a 400. Invisible in the schema. - 🔴
failure_percentageis a FRACTION between 0 and 1, despite the name.0.1is ten per cent.1is one hundred. - 🔴
failure_percentage = 0is silently discarded. The validator accepts it; the expand function only sends the field when it is non-zero. The strictest setting a caller can express becomes Azure's default. This module rejects0. - 🔴
policy_assignment_idis NOT force-new at any scope — the opposite of the policy exemption resources, where three of the four scopes replace. ⚠️ namemust be lower case, because Azure returns it lower-cased and an upper-case name would differ on every plan. The provider enforces this; the reason is only in its source comment.⚠️ The forbidden character set%^#/\&?is the provider's empirical finding, not a published Azure rule — Azure publishes naming rules for assignments, definitions and exemptions, but not for remediations.⚠️ policy_definition_reference_idis a SINGLE string here, where the exemption resources take a list.⚠️ The management-group-scoped remediation has noresource_discovery_modeat all — the one scope of four without it.- ✅ The default
ExistingNonCompliantis already the cheaper, more predictable mode. This module keeps it. - No
tags.
| Action | Role | Scope |
|---|---|---|
| Create / update / delete the remediation | Resource Policy Contributor, or a custom role with Microsoft.PolicyInsights/remediations/* |
The resource group |
Read it for plan |
Reader | The resource group |
| Perform the remediation itself | Whatever the policy's deployIfNotExists / modify effect requires — held by the assignment's managed identity, not by you |
The remediated resources |
🔴 The permission that does the work is not yours. A remediation executes under the policy assignment's managed identity, using the roles granted to that identity in the assignment. So triggering a remediation is a small permission, and what it then changes is bounded by a different principal's rights entirely. Review both together — terraform-azurerm-policy-assignment emits identity_principal_id for exactly this.
✅ No secret is involved. This resource accepts none and emits none.
Microsoft.PolicyInsightsregistered on the subscription.- An existing policy assignment at or above this scope, whose policy effect is
deployIfNotExistsormodify. Anything else gives the task nothing to do. - A managed identity on that assignment, with the roles the effect needs. Without it the remediation runs and fails.
- An evaluation cycle to have run, unless you are using
ReEvaluateCompliance. - A generous delete timeout if you use
ReEvaluateCompliance.
terraform-azurerm-resource-policy-remediation/
├── providers.tf # required_version + pinned azurerm; no provider block
├── variables.tf # 3 required inputs, 7 optional, 21 validations
├── main.tf # the keystone `this` + derived locals
├── outputs.tf # 26 outputs; id first; none sensitive
├── README.md # this file
├── SCOPE.md # the cross-module contract
├── LICENSE # MIT
└── .gitignore # the canonical library ignore set
provider "azurerm" {
features {}
}
module "remediate_diagnostics" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-policy-remediation.git?ref=v1.0.0"
name = "remediate-diagnostics"
resource_id = "${module.rg.id}/providers/Microsoft.Storage/storageAccounts/observabilitylogs"
policy_assignment_id = module.deploy_diagnostics.id
resource_count = 100
parallel_deployments = 5
failure_percentage = 0.1
}The caller configures the provider, its authentication and the mandatory features {} block. The module declares none of them.
Consumes
| Input | Type | Source |
|---|---|---|
resource_id |
string |
Whichever module creates the resource → its id |
policy_assignment_id |
string |
terraform-azurerm-policy-assignment → id |
Emits
| Output | Description |
|---|---|
id |
The remediation's Resource ID |
is_unbounded, bounded_control_count |
Whether any blast-radius control was set |
tolerates_total_failure |
Whether failure_percentage is 1 |
re_evaluates_compliance |
Whether destroy will have to cancel and poll |
is_region_restricted, location_filter_count |
Geographic limits |
subscription_id, resource_group_name, provider_namespace, resource_type, resource_name |
Parsed scope facts |
| 4 constant facts | It changes resources; only two effects are remediable; destroy may cancel first; the scope covers exactly one resource |
1 · The smallest real call
module "remediation" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-policy-remediation.git?ref=v1.0.0"
name = "remediate-diagnostics"
resource_id = var.resource_id
policy_assignment_id = var.policy_assignment_id
}
⚠️ This is legal and it is the shape to be careful with: no blast-radius control at all. Azure's own defaults decide how many resources change and how fast.is_unboundedreportstruefor exactly this call.
2 · Bounding what the task may change
resource_count = 100
parallel_deployments = 5
failure_percentage = 0.1🎚️ These three are the only things limiting the change.
resource_countcaps how many resources are touched,parallel_deploymentscaps the rate, andfailure_percentagestops the task once too many have failed.💡
bounded_control_countreports how many of the three you set, so acheckcan require all of them.
3 · The fraction that looks like a percentage
failure_percentage = 0.1 # ten per cent
# failure_percentage = 50 # REJECTED: not a percentage
# failure_percentage = 1 # legal, but means ONE HUNDRED per cent🚨 The argument is named
percentageand takes a fraction between 0 and 1. Writing50is rejected outright; writing1intending one per cent silently tolerates every remediation failing.💡
tolerates_total_failureexists because the module cannot reject1— it is a legal value — but it can make the consequence visible.
4 · The zero that does nothing
# REJECTED by this module:
failure_percentage = 0🚧 The provider's validator accepts
0, but its expand function only sends the field when the value is non-zero. So0— which reads as "abort on the first failure" — is discarded, and Azure's default applies instead. The strictest setting a caller can express is indistinguishable from setting nothing, so the module converts that silence into an error.
5 · Restricting the task to specific regions
location_filters = ["eastus2", "westus2"]🌍 Without filters the task acts wherever it finds non-compliant resources in scope. The provider validates each entry against the real region list, so a typo is caught at plan.
ℹ️ Duplicates are rejected case- and space-insensitively:
East USandeastusare the same region.
6 · Remediating one policy inside an initiative
policy_definition_reference_id = "deployDiagnosticsToStorage"
⚠️ Note this is a single string, where the policy exemption resources take a list. A remediation acts on one policy at a time.💡 Omitting it remediates the assignment as a whole;
targets_single_policy_referencereports which you chose.
7 · Re-evaluating compliance first
resource_discovery_mode = "ReEvaluateCompliance"
timeouts = {
delete = "60m"
}⏱️ This mode also changes how
terraform destroybehaves. With it set, the provider must cancel a still-running task and poll until the cancellation finishes before deleting — so raise the delete timeout alongside it.ℹ️ The default,
ExistingNonCompliant, acts on resources already assessed as non-compliant. It is cheaper and more predictable, and this module keeps it.
8 · Blocking unbounded remediations
check "remediation_is_bounded" {
assert {
condition = !module.remediation.is_unbounded
error_message = "This remediation sets none of resource_count, parallel_deployments or failure_percentage, so Azure decides how much changes."
}
}🚨 A remediation changes live infrastructure. This is the single most useful control available on the resource, and it passes only when a human has thought about the limits.
9 · Requiring all three controls
check "remediation_fully_bounded" {
assert {
condition = module.remediation.bounded_control_count == 3
error_message = "Set all of resource_count, parallel_deployments and failure_percentage before remediating in production."
}
assert {
condition = !module.remediation.tolerates_total_failure
error_message = "failure_percentage is 1, which tolerates every remediation failing. Did you mean 0.01?"
}
}💡 The second assertion catches the specific confusion the argument's name invites.
10 · Several remediations with `for_each`
locals {
tasks = {
"remediate-diagnostics" = var.diagnostics_assignment_id
"remediate-tags" = var.tags_assignment_id
}
}
module "remediations" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-policy-remediation.git?ref=v1.0.0"
for_each = local.tasks
name = each.key
resource_id = var.resource_id
policy_assignment_id = each.value
resource_count = 50
parallel_deployments = 2
failure_percentage = 0.05
}ℹ️
for_eachis safe: the remediation's Resource ID ends in the name you chose. Note the names are lower case, which the module requires.
11 · Re-pointing at a different assignment
policy_assignment_id = var.new_assignment_id✅ This updates in place at every scope — unlike the policy exemption resources, where three of the four scopes replace instead. The two families are adjacent and behave differently here, which is worth knowing before reading a plan.
12 · Names the module rejects
# All REJECTED:
name = "Remediate-Diagnostics" # must be lower case
name = "remediate#diagnostics" # %^#/\&? are not allowed🚧 The lower-case rule is not cosmetic: Azure returns the name lower-cased, so an upper-case name would differ on every plan and never settle. The provider enforces it; this module explains why.
13 · The narrowest scope, and what the provider will let through
# This module: exactly one resource, and it never widens.
resource_id = var.resource_id
# REJECTED by this module, though the PROVIDER would accept both:
# resource_id = "/subscriptions/0000.../resourceGroups/rg-prod" # a resource group
# resource_id = "/subscriptions/0000..." # a whole subscription🛑 This field is validated with
azure.ValidateResourceID, whose own source comment says it "should only be used when a more specific Resource ID validation function is unavailable". It parses the string as an ARM ID and nothing more, and an ARM ID needs only a/subscriptions/<id>pair and an even number of segments — so a resource-group ID and a bare subscription ID both pass. The other three scopes each get a strict validator; this one does not.
⚠️ Since a remediation changes resources, a mistyped scope here is considerably worse than the same mistake on an exemption: it would silently run the policy effect across a whole resource group or subscription. The module requires a/providers/segment and names the right sibling in the error.
14 · 🏗️ End-to-end composition
provider "azurerm" {
features {}
}
module "rg" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
name = "rg-observability"
location = "eastus2"
}
module "deploy_diagnostics_definition" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-policy-definition.git?ref=v1.0.0"
name = "deploy-diagnostics-to-law"
display_name = "Deploy diagnostic settings to Log Analytics"
}
module "deploy_diagnostics" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-policy-assignment.git?ref=v1.0.0"
name = "deploy-diagnostics"
resource_group_id = module.rg.id
policy_definition_id = module.deploy_diagnostics_definition.id
}
module "grant_remediation_identity" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-role-assignments.git?ref=v1.0.0"
scope = module.rg.id
role_assignments = {
contributor = {
principal_id = module.deploy_diagnostics.identity_principal_id
role_definition_name = "Contributor"
principal_type = "ServicePrincipal"
}
}
}
module "remediation" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-policy-remediation.git?ref=v1.0.0"
name = "remediate-diagnostics"
resource_id = "${module.rg.id}/providers/Microsoft.Storage/storageAccounts/observabilitylogs"
policy_assignment_id = module.deploy_diagnostics.id
resource_count = 100
parallel_deployments = 5
failure_percentage = 0.1
}
check "remediation_is_bounded" {
assert {
condition = !module.remediation.is_unbounded
error_message = "This remediation is unbounded."
}
}🏗️ Read the chain: a definition is written, assigned, its managed identity is granted the rights the effect needs, and only then is a remediation run. Skip the role assignment and the remediation runs and fails — the identity doing the work is the assignment's, not yours.
⚠️ Terraform orders these only through themodule.deploy_diagnosticsreferences they share; it does not understand that the role assignment must propagate first. A remediation started immediately after the grant can still fail on permissions, and re-applying is the fix.
| Name | Type | Required | Summary |
|---|---|---|---|
name |
string |
yes | Lower case, ≤260 chars, no %^#/\&?. Force-new. |
resource_id |
string |
yes | The single resource to remediate. Force-new. |
policy_assignment_id |
string |
yes | The assignment to act on. Updates in place. |
policy_definition_reference_id |
string |
no | One policy inside an initiative. A single string. |
resource_discovery_mode |
string |
no | ExistingNonCompliant (default) or ReEvaluateCompliance. |
location_filters |
list(string) |
no | Restrict to named regions. |
resource_count |
number |
no | Cap on resources touched. |
parallel_deployments |
number |
no | Cap on concurrency. |
failure_percentage |
number |
no | A fraction 0–1, exclusive of 0. |
timeouts |
object |
no | create / read / update / delete. |
| Output | Description |
|---|---|
id, name |
The remediation. |
resource_id, subscription_id, resource_group_name, provider_namespace, resource_type, resource_name |
The scope, raw and parsed. |
policy_assignment_id, policy_definition_reference_id, targets_single_policy_reference |
What it acts on. |
resource_discovery_mode, re_evaluates_compliance |
Which mode, and whether destroy will cancel first. |
location_filters, location_filter_count, is_region_restricted |
Geographic limits. |
resource_count, parallel_deployments, failure_percentage |
The controls, as stored. |
bounded_control_count, is_unbounded |
How many controls were set. |
tolerates_total_failure |
Whether failure_percentage is 1. |
remediation_changes_resources_it_is_not_an_audit |
Constant true. |
only_deploy_if_not_exists_and_modify_policies_can_be_remediated |
Constant true. |
destroy_may_have_to_cancel_a_running_task_first |
Constant true. |
scope_covers_exactly_one_resource |
Constant true. |
No output is sensitive. Nothing about this resource is a credential.
A remediation changes resources; an exemption suppresses evaluation. The two attach to the same policy assignment and sit side by side in this library, and they are opposites. Applying this module reaches into infrastructure Terraform has never managed — resources it has never seen, in whatever state they are in — and runs the assigned policy's deployIfNotExists or modify effect against them. Everything else in the module follows from taking that seriously.
The work is done by somebody else's identity. A remediation executes under the policy assignment's managed identity, with the roles granted to that identity. Creating the remediation is therefore a small permission; what it goes on to change is bounded by a different principal's rights. Reviewing one without the other tells you very little, which is why the composition example puts the role assignment in the same frame and the permissions table names the split explicitly.
Three optional arguments are the only brakes, and the empty call sets none of them. resource_count, parallel_deployments and failure_percentage bound how much changes and how fast. The module cannot choose values on a caller's behalf without inventing numbers, so instead it counts how many were set and emits is_unbounded — making the absence of a decision visible rather than pretending a default is one.
failure_percentage is the sharpest trap on the resource, twice over. It is a fraction despite its name, so 1 means one hundred per cent and is exactly what somebody reaches for when they mean one; the module emits tolerates_total_failure because it cannot reject a legal value. And 0 — the strictest possible setting — passes validation and is then discarded by the expand function, silently substituting Azure's default. That second one the module can reject, and does.
ReEvaluateCompliance reaches into the destroy path. With that mode set and the task still running, the provider cancels the remediation and polls until the cancellation settles before it can delete, checking the state first because cancelling a completed task returns a 400. A terraform destroy is then bounded by the delete timeout rather than being immediate, and a destroy that times out leaves a cancelling task behind. None of this appears in the schema; it is only in the provider's delete body.
The naming rules come from three different authorities, and it is worth knowing which. The 260-character limit is what the service itself reports. The forbidden set %^#/\&? is the provider's own empirical finding, stated as such in its source. And the lower-case requirement exists because Azure returns the name lower-cased, so an upper-case name would produce a difference on every plan — a round-trip trap the provider prevents by refusing the input. Azure publishes naming rules for policy assignments, definitions and exemptions, but not for remediations.
Force-new differs from the neighbouring exemption family. Here policy_assignment_id updates in place at all four scopes. On the exemptions, three of the four scopes replace. Nothing in the schema shows either fact; both come from the ForceNew flags in the Go sources, and a reader who generalises from one family to the other will be wrong.
| Concern | This module's position | Why |
|---|---|---|
| Blast radius | All three controls optional, and is_unbounded emitted |
Picking caps for a caller would invent numbers; reporting that none were picked does not. |
failure_percentage = 0 |
Rejected | Accepted upstream and then discarded — the strictest setting silently becomes the default. |
failure_percentage = 1 |
Allowed, and flagged | Legal, so it cannot be rejected; it is also the value the argument's name invites by mistake. |
resource_discovery_mode |
Provider default ExistingNonCompliant kept |
Already the cheaper and more predictable mode. Examined and agreed, not overlooked. |
| Delete timeout | Documented rather than raised | The module cannot know how long a cancellation takes; it can say when one will happen. |
name |
Lower case enforced, with the reason given | Prevents a round-trip diff. The provider enforces it; only its source says why. |
| Scope choice | Narrower siblings named in the errors | Four scopes, four resource types, not interchangeable. |
| Secrets | None accepted, none emitted | Nothing here is a credential. |
terraform init -backend=false
terraform validate
terraform fmt -checkPin the module with ?ref=v1.0.0, never a branch. This library is plan-only: a human applies from CI.
What the offline gate covers:
terraform validateproves the configuration parses and every type is satisfied.terraform fmt -checkproves the HCL is canonically formatted.- Feeding deliberately bad
.tfvarsthroughterraform consolefires the input validations — all 21 validation blocks in this module have been proven reachable, each by a fixture that triggers it, with zero condition-evaluation errors. - All 16 derived locals have been driven to more than one value, including
is_unboundedandtolerates_total_failurein both states, and a CHILD resource ID provingresource_nametakes the last segment whileresource_typereports the top-level type.
What only a real plan or apply can exercise:
- Whether the assignment exists, sits at or above this scope, and has a remediable effect.
- Whether its managed identity holds the roles the effect needs.
- How long a
ReEvaluateCompliancecancellation actually takes. - What the remediation changes, which is the part that matters and which no static check can evaluate.
id = "/subscriptions/.../resourceGroups/rg-observability/providers/Microsoft.PolicyInsights/remediations/remediate-diagnostics"
name = "remediate-diagnostics"
resource_group_name = "rg-observability"
policy_assignment_id = "/subscriptions/.../providers/Microsoft.Authorization/policyAssignments/deploy-diagnostics"
resource_discovery_mode = "ExistingNonCompliant"
re_evaluates_compliance = false
location_filter_count = 0
is_region_restricted = false
resource_count = 100
parallel_deployments = 5
failure_percentage = 0.1
bounded_control_count = 3
is_unbounded = false
tolerates_total_failure = false
targets_single_policy_reference = false
| Symptom | Cause | Fix |
|---|---|---|
| The remediation completes but nothing changed | The assignment's policy effect is audit, deny or disabled |
Only deployIfNotExists and modify are remediable. There is no error for this |
| The remediation fails on permissions | The assignment's managed identity lacks the roles the effect needs | Grant them to identity_principal_id, not to your own principal |
| Far more changed than expected | No blast-radius control was set, so Azure's defaults applied | Set resource_count, parallel_deployments and failure_percentage; assert is_unbounded is false |
| The task kept going after many failures | failure_percentage was 1, meaning one hundred per cent |
It is a fraction. 0.01 is one per cent |
failure_percentage = 0 had no effect |
Accepted by the validator, then discarded by the provider's expand | This module now rejects 0; omit the argument for Azure's default |
terraform destroy hung or timed out |
ReEvaluateCompliance requires a cancel-and-poll before delete |
Raise the delete timeout; re-run destroy if it times out |
name rejected after working elsewhere |
It contained capitals | Azure lower-cases the name on read, so capitals would diff forever |
| Nothing was remediated in a region you expected | location_filters excluded it |
Omit the filter, or add the region |
| A whole resource group was remediated unexpectedly | A resource-group ID was passed as resource_id; the provider's generic validator accepts it |
This module now rejects it and names the correct sibling |
azurerm_resource_policy_remediation— provider resource reference.- Remediate non-compliant resources — what a remediation task does and which effects it applies to.
- Sibling modules:
terraform-azurerm-resource-group-policy-remediation,terraform-azurerm-subscription-policy-remediation,terraform-azurerm-management-group-policy-remediation,terraform-azurerm-policy-assignment,terraform-azurerm-resource-group-policy-exemption. - This module's
SCOPE.md.
💙 "Infrastructure as Code should be standardized, consistent, and secure."