Operational Rules
At a glance
- Operational Rules transform your Terraform modules at download time to enforce 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.
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. There is no input for it, no override mechanism, no dynamic configuration. This has been an open request since 2015 (hashicorp/terraform#3116) with hundreds of reactions across related issues.
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.
Forking 5-10 modules creates a 5-10x maintenance multiplier. Upstream updates require manual re-forking and testing. Drift between forked versions across teams becomes inevitable. Platform teams become bottlenecks for module updates.
Operational Rules eliminate this. Define your operational standards once, and every module downloaded from the compliance.tf registry has them applied automatically.
Two pillars: Compliance Controls and Operational Rules
compliance.tf solves two distinct problems with one product:
| 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 (transparent) | Module download (transparent) |
| Can be disabled | Per-module via ?disable= parameter | Per-request via ?rules= parameter or per-org configuration |
Both layers apply during terraform init. Developers do not need to know either layer exists. They get hardened, standards-compliant modules by default.
How rules work
- Org admin configures rules. Enable, disable, or adjust rules for your organization via the compliance.tf API, or specify them per-module with
?rules=. - 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. - 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. You can read it, diff it, and audit it like any other module.
Per-request overrides are also available using the ?rules= query parameter in the HTTPS module source URL:
# Add a rule for this download
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=+pofix/prevent_destroy_data"
}
# Remove a rule for this download
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=-pofix/ignore_tag_changes"
}
How rules interact with compliance controls
Compliance controls and operational rules are separate, independent layers. Controls run first (regulatory enforcement), rules run second (operational standards).
If a rule and a control affect the same resource attribute, the changes are merged additively (e.g., both adding to ignore_changes). Explicit conflict detection, where a rule contradicts a control, is planned for a future release. Compliance controls always take priority in the processing order.
Frequently asked questions
Compliance and Controls
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 are merged additively (e.g., both adding to ignore_changes). Explicit conflict detection, where a rule contradicts a control, is planned for a future release.
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.
That said, 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 supports cost governance, which is relevant to resource management controls.
If an auditor asks about these, position them as operational safeguards that complement your compliance controls, not as compliance controls themselves.
Workflow and Adoption
Can I disable rules?
Yes. Per-request overrides are available using the ?rules= query parameter in the HTTPS module source URL:
# Remove a rule for this download
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=-pofix/prevent_destroy_data"
}
# Add a rule that is not in org defaults
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=+pofix/ignore_ami_changes"
}
Free tier: You can apply rules per-request by adding ?rules= to your module source URL. Rules are not forced; you choose which to include.
Paid tier: Org admins configure which rules are enabled by default for the organization. Individual developers can override the org defaults on a per-request basis using the ?rules= query parameter. The - prefix removes a rule for that download; the + prefix adds one. Org admins can also disable specific rules for the entire organization via the API.
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?
Not yet. All rules are currently managed by compliance.tf. Org-authored rules (custom HCL transformation rules defined by your team) are on the roadmap. When available, custom rules will follow the same transformation, manifest, and preview model as builtin rules.
Does this work with Atlantis, Terragrunt, or Terraform Cloud agents?
Yes. Rules are applied server-side during module download. Any tool that runs terraform init and downloads modules from the compliance.tf registry will receive modules with rules applied. This includes:
- Atlantis: configure the compliance.tf access token in the Atlantis server's
.terraformrc - Terragrunt: calls
terraform initunder the hood. Configure credentials as you would for plain Terraform. - Terraform Cloud agents: configure the compliance.tf token in the workspace or organization credential settings
- Spacelift, env0, Scalr: configure the access token in each platform's credential management
No platform-specific integration is required. If the platform can download modules from a private Terraform registry, rules work.
Trust and Transparency
How do I see exactly what was transformed?
Inspect the downloaded module source directly. The module is standard HCL. Open any .tf file in .terraform/modules/ and read it. The lifecycle blocks, provisioner removals, and other transformations are visible in the code.
Coming Soon
A .ctf-rules-manifest.json file will be included in every downloaded module, listing each rule applied, which resources were affected, and a SHA-256 hash of the ruleset. This feature is planned but not yet available.
When the Preview API becomes available, you will also be able to see unified diffs before downloading.
How do I know dev, staging, and prod got the same ruleset?
For now, you can compare the downloaded .tf files directly across environments. If the same module version was downloaded with the same ?rules= parameter (or the same org defaults), the resulting HCL will be identical. A simple diff or checksum of the downloaded module directories confirms consistency.
Coming Soon
The compliance.tf registry will include a SHA-256 ruleset hash in the download response headers (X-CTF-Rules-Hash) and in the .ctf-rules-manifest.json file inside each downloaded module. Currently, X-CTF-Rules-Hash returns a placeholder value (none). A meaningful hash will be available in a future release.
What's my break-glass path when prevent_destroy blocks an intentional replacement?
When you need to intentionally destroy a protected resource (database migration, bucket replacement, key rotation), use a per-request override to remove the Prevent Destroy Data or Prevent Destroy Encryption rule for a single terraform init:
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=-pofix/prevent_destroy_data"
}
Run terraform init -upgrade to download the module without prevent_destroy. Run your terraform destroy or replacement plan. After the operation, change the source back to remove the override and run terraform init -upgrade again to restore the protection.
The per-request override is visible in the downloaded HCL: the prevent_destroy lifecycle block will be absent. Org admins can also adjust the org defaults via the API if the override is needed permanently.
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 -upgradeandterraform 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 (e.g., prevent_destroy = true), the plan will show no changes.
Why should I trust your pipeline more than my own wrapper repo?
Fair question. 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 (.ctf-rules-manifest.json) that records applied rules and a SHA-256 hash is planned for a future release. Your wrapper repo may add lifecycle blocks, but there is no standard manifest format documenting what changed. - Consistency: When the same rules are applied (via
?rules=or org defaults), 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. When the Preview API ships, you can see the diffs before downloading.
That said, if your wrapper repo is well-maintained and working, rules are not a forced migration. They are an option for teams that want the same outcome without the fork tax.
Where to go next
- Getting Started with Operational Rules. Enable rules for your org in under 5 minutes.
- Get Started with Compliance.tf. Set up your account and first compliant module.
- Rule Catalog. Browse all 7 available rules with examples.