Rules overview
For platform and security engineers deciding whether Operational Rules fit their estate. This page explains what rules are, how they reach a module, and how they relate to compliance controls. To apply one, go to Add your first rule.
- Operational Rules transform your Terraform modules at download time to apply your organization's operational standards.
- Rules handle lifecycle blocks, tagging, provisioner removal, and instance type restrictions.
- No forks, no wrapper modules. Same
terraform init, same modules. - Rules and compliance controls are independent layers. Controls run first, then rules. Changes are merged additively.
- On your organization's host, rules come from a Baseline that applies to every module and named rulesets a module selects with
?ruleset=.
The problem rules solve
Terraform's lifecycle meta-argument cannot be parameterized. You cannot pass prevent_destroy = true into an upstream module as a variable, and a module has no way to accept one. The request to allow it (hashicorp/terraform#3116) was closed in 2023 without a change; among the open requests are hashicorp/terraform#18367, hashicorp/terraform#22544 and opentofu/opentofu#1329. The per-module pages under Workarounds list where this bites in terraform-aws-modules and what the native workaround is.
When a platform team needs prevent_destroy on S3 buckets, ignore_changes on tags, and no provisioner blocks, they respond with one of these workarounds:
- Fork the module. Maintain a copy with lifecycle blocks added. Repeat for every module. Sync upstream updates manually.
- Write wrapper modules. Add a layer of indirection that still needs lifecycle blocks inside.
- Add PR review checklists. "Did you add
prevent_destroy?" Depends on human memory. Does not scale. - Wire up OPA or Sentinel policies. Can flag missing lifecycle blocks, but cannot add them to the code.
Each forked module is one more copy to re-fork and test on every upstream release, and forked versions drift apart across teams. With Operational Rules, the standards are defined once and applied to every module downloaded from the compliance.tf registry.
How rules work
- An org admin configures rules. On the Operational Rules page (or its API), rules go into the organization's Baseline, which applies to every module downloaded from the organization's host, or into a named ruleset that a module selects with
?ruleset=<name>. On a framework host, a module can also name rules for one download in itssourceline. - A developer runs
terraform init. The compliance.tf registry resolves the developer's org, looks up the org's rule configuration, and applies rules during module download. - The module arrives with rules applied. Lifecycle blocks, provisioner removal, and instance restrictions are already in the HCL. The developer runs
terraform planandterraform applynormally.
Rules are applied server-side, at the same time as compliance controls. The downloaded module is standard Terraform HCL that you can read, diff, and audit like any other module. On your organization's own host, per-download overrides in the URL are refused with a 400 rather than ignored, because there the organization's configuration is the whole answer. Which query parameters each host accepts is on Rulesets in the console.
How rules relate to compliance controls
| Compliance controls | Operational Rules | |
|---|---|---|
| What it enforces | Regulatory requirements | Organizational standards |
| Driven by | Frameworks (SOC 2, PCI DSS, HIPAA, NIST...) | Platform team decisions |
| Examples | Encryption at rest, access logging, public access blocking | prevent_destroy, ignore_changes, instance restrictions |
| Who configures | Compliance.tf (framework-defined) | Org admins (org-defined) |
| Applied when | Module download | Module download, after controls |
| Can be disabled | Per module via ?disable= on a framework host | Per download via ?rules= on a framework host; on your organization's host only by changing the Baseline or a ruleset |
Both layers apply during terraform init. Developers keep their usual workflow. When a rule and a control touch the same attribute, the changes merge additively. The first question below has the detail.
Frequently asked questions
Do operational rules affect compliance controls?
No. Operational Rules and compliance controls are independent layers. Controls are applied first (regulatory enforcement), rules are applied second (operational standards). A rule cannot override, weaken, or bypass a compliance control.
If a rule and a control affect the same resource attribute, the changes merge additively (for example, both adding to ignore_changes).
Rules live in a separate scope from controls: controls enforce encryption, logging, access blocking, and other regulatory requirements. Rules enforce lifecycle blocks, tagging behavior, provisioner removal, and instance restrictions.
Are rules part of my SOC 2, HIPAA, or PCI DSS compliance?
Rules are not compliance controls and should not be presented to auditors as regulatory enforcement. They are operational safety measures.
Several rules support operational practices that auditors view favorably:
- Prevent destroy data and Prevent destroy encryption reduce the risk of accidental data loss, which supports data protection and availability objectives.
- No provisioners removes a supply chain risk vector from module code, which supports change management and code integrity objectives.
- Restrict instance types reports costly instance types written into module source, which supports cost governance.
If an auditor asks about these, describe them as operational safeguards that complement your compliance controls.
Can I disable rules?
Yes. On a framework host, a module can remove a rule for one download, or add one, in its HTTPS source line; Add your first rule shows both.
Free tier: you apply rules per request by naming them in your module source URL. Rules are not forced; you choose which to include.
Trial, paid and enterprise plans: org admins configure the organization's Baseline and rulesets on the Operational Rules page. On the framework and canonical hosts, individual developers can still override per request. On the organization's own host they cannot: a rule stops applying to a module only when it is taken out of the Baseline, or kept in a ruleset that module does not select. Org admins can also disable a rule for the entire organization. See Rulesets in the console.
Do rules work with OpenTofu?
Yes. Operational Rules modify the module source code (standard HCL) before it is downloaded. The resulting module works identically with Terraform and OpenTofu. There are no provider-specific or CLI-specific dependencies in the rule transformations.
All lifecycle meta-arguments (prevent_destroy, ignore_changes) and the absence of provisioner blocks are standard HCL features supported by both Terraform and OpenTofu.
What happens when rules change?
Rules are applied at download time (terraform init). If your org admin enables a new rule or changes the rule configuration, the change takes effect on the next terraform init -upgrade that downloads a fresh copy of the module.
Modules already downloaded and cached in .terraform/modules/ are not retroactively modified. To pick up new rules, run terraform init -upgrade. You can inspect the downloaded .tf files to see which rules were applied to a given module copy.
Can I write my own rules?
No. Every rule comes from the compliance.tf rule catalog; your organization chooses which ones apply and sets their parameters.
Does this work with Atlantis, Terragrunt, or HCP Terraform agents?
Yes. Rules are applied server-side during module download, so any tool that runs terraform init against the compliance.tf registry receives modules with rules applied; Terragrunt runs terraform init under the hood. Credential setup for each platform is on CI/CD integration.
How do I see exactly what was transformed?
Read the downloaded module. It is standard HCL in .terraform/modules/, and the lifecycle blocks, provisioner removals, and other transformations are in the code. To see a rule's diff before downloading, use Preview a rule or the Rules Playground.
A module compliance.tf builds for you also carries a compliancetf-manifest.json listing the rules the build received, each with an outcome saying what it did; a listed rule can have enforced nothing. The manifest records file paths, not resource addresses. How to read it, including which fields confirm that a change reached a module, is on How a module request resolves.
How do I know dev, staging, and prod got the same rules?
Compare the downloaded .tf files across environments. If the same module version was downloaded with the same rules in its source line, or from the same organization Baseline and ruleset, the resulting HCL will be identical. A diff or checksum of the downloaded module directories confirms consistency, and the manifest fields described on How a module request resolves identify the exact built variant.
What's my break-glass path when prevent_destroy blocks an intentional replacement?
Each rule page has a "Break-glass path" section for that rule, such as Prevent destroy data. On a framework host the path is a per-download override in the source line; on your organization's host it is an admin change to the Baseline or a ruleset, because URL overrides are refused there.
How do I leave compliance.tf without state churn?
Downloaded modules are standard Terraform HCL. The lifecycle blocks, tagging behavior, and provisioner changes are all written directly in the .tf files. To leave compliance.tf:
- Change the module
sourceURL back toterraform-aws-modules/... - Manually add the lifecycle blocks that your team needs (or don't; they are standard Terraform features you can add to any module)
- Run
terraform init, which installs the module from its new source, andterraform plan
Because the lifecycle settings in compliance.tf modules are identical to what you would write by hand, there is no state migration, no resource recreation, and no proprietary format to unwind. If your existing state already has the values that rules enforce (for example prevent_destroy = true), the plan will show no changes.
Why should I trust your pipeline more than my own wrapper repo?
Here is what compliance.tf provides that a wrapper repo does not:
- Transparency: The downloaded module is plain HCL. You can read every
.tffile and see exactly what was transformed. A manifest file (compliancetf-manifest.json) records the rules requested, what each one actually did, and a variant fingerprint. A wrapper repo may add the same lifecycle blocks, but has no standard manifest format documenting what it changed. - Consistency: When the same rules are applied (named in the
sourceline on a framework host, or from the organization's Baseline and rulesets), the same module version produces identical HCL. Wrapper repos depend on consistent builds and artifact promotion, which is possible but adds moving parts. - No maintenance burden: Wrapper repos require re-wrapping on every upstream module update. Compliance.tf applies rules to the latest upstream version automatically.
- Inspectability: Open the downloaded
.tffiles and read them, or see the diff before downloading with Preview a rule.
If your wrapper repo is well maintained and working, you can keep it; rules give the same outcome without maintaining a fork.
Next steps
- Add your first rule - apply one rule to one module and check it worked
- Rule catalog - every rule, with before-and-after examples
- Rulesets in the console - how an organization's rules are grouped, and how one module selects a ruleset
- Preview a rule - see a rule's diff before you download