Operational Rules

Your platform standards, applied at terraform init

Terraform's lifecycle blocks can't be passed into upstream modules, so teams fork, wrap, or patch. Operational Rules apply your standards automatically at download time.

The Fork Tax

Terraform doesn't let you pass prevent_destroy or ignore_changes into a module as a variable. It's been an open request since 2015, with hundreds of reactions across related issues. So teams work around it:

Fork every module

Maintain a copy with lifecycle blocks added, re-sync upstream updates manually, repeat for each module.

Write wrapper modules

Another layer of indirection that still needs lifecycle blocks inside.

Add PR review checklists

"Did you add prevent_destroy?" Human memory doesn't scale.

Wire up OPA/Sentinel policies

Can flag a missing lifecycle block, but cannot add one to the code.

Operational Rules handle this at module download time, without forks or wrappers.

Read the full story on our blog

Two Problems, One Product

compliance.tf modules ship with two independent layers applied at download time. Compliance Controls handle regulatory requirements. Operational Rules handle your organizational standards.

Compliance Controls

Audit-ready modules

What it enforces
Regulatory requirements
Driven by
Frameworks (SOC 2, PCI DSS, HIPAA, NIST, ISO 27001...)
Examples
Encryption at rest, access logging, public access blocking
Who configures
compliance.tf (framework-defined)

Operational Rules

Your standards, built in

What it enforces
Organizational standards
Driven by
Platform team decisions
Examples
prevent_destroy, ignore_changes, instance restrictions
Who configures
Org admins (org-defined)

Rule Catalog

Nine of the rules in the catalog. Each one is applied at module download time, to every module your organization enables it for.

Prevent Destroy Data

Adds a lifecycle block to stateful resources so accidental terraform destroy can't wipe production data.

lifecycle { prevent_destroy = true }
S3, RDS instances and clusters, DynamoDB, EFS, ElastiCacheDocs

Ignore Tag Changes

Adds a lifecycle block so external tag changes (AWS Config rules, cost allocation tools, FinOps automation) won't trigger plan diffs.

lifecycle { ignore_changes = [tags, tags_all] }
All taggable resourcesDocs

Ignore Autoscaling Changes

Adds a lifecycle block so DynamoDB auto-scaling doesn't fight with Terraform on every plan.

lifecycle { ignore_changes = [read_capacity, write_capacity] }
DynamoDBDocs

Ignore AMI Changes

Adds a lifecycle block so AMI updates from image pipelines don't cause unexpected instance replacements.

lifecycle { ignore_changes = [ami, image_id] }
EC2 instances, launch templatesDocs

Prevent Destroy Encryption

Adds a lifecycle block to encryption keys and secrets. Losing a KMS key means losing access to everything it encrypted.

lifecycle { prevent_destroy = true }
KMS, Secrets ManagerDocs

No Provisioners

Removes all provisioner blocks from module resources. Provisioners run arbitrary commands outside Terraform's state model and break idempotency.

provisioner blocks removed
All resourcesDocs

Restrict Instance Types

Denies GPU and specialty instance types (p3, p4, x1, x2, u-*) that can generate five- and six-figure monthly bills from a single terraform apply.

denies p3, p4, x1, x2, u-* types
EC2Docs

Allowed Regions

Adds a validation block to the module's region variable so a region outside your organization's allowlist is rejected before anything is created.

validation { condition = contains(allowlist, var.region) }
Modules with a region variableDocs

Ignore Selected Tag Keys

Adds a lifecycle block that ignores only the tag keys your organization names, so scanner or cost tags written outside Terraform stop showing up as drift.

lifecycle { ignore_changes = [tags["<key>"]] }
S3 by defaultDocs

What Module Transformation Looks Like

The Prevent Destroy Data rule applied to an S3 bucket from terraform-aws-modules. This happens at download time, before terraform init completes.

main.tf
resource "aws_s3_bucket" "this" {
  bucket = var.bucket
  # ... existing config ...
+
+ lifecycle {
+   prevent_destroy = true
+ }
}

Each downloaded module includes a manifest with a variant hash identifying the exact transformation that was applied, so you can verify two environments pulled the same rules-applied artifact.

Run this diff yourself in the Rules Playground - real engine, real module, no account.

Three Steps

1

Configure

Org admins set a Baseline that applies to every module, and named rulesets a single module opts into, from the Operational Rules page in the compliance.tf dashboard or the API.

2

Init

Developers run terraform init with a compliance.tf module source. No new CLI or policy daemon needed.

3

Done

The module arrives with your operational standards already applied. Lifecycle blocks are in the code.

Need to override a rule for a specific download? On a framework host, use per-request query parameters like ?rules=-lifecycle_prevent_destroy_data to drop one of your organization's rules for that download, or ?add_rules=lifecycle_prevent_destroy_data to add one. A bare ?rules= list replaces your rules rather than adding to them, so the two cannot be combined. On your organization's host, select a ruleset with ?ruleset=ignore-scanner-tags.

On the Roadmap

Operational Rules are shipping today. Here's what's next.

Versioned, approved rulesets

planned

Pin a module to an immutable ruleset version and require an approval before a change reaches a download.

Preview API

planned

See exact diffs before downloading a module.

Org-authored rules

planned

Write your own transformation rules for org-specific standards.

Start with Hardened Modules Today

Compliance controls and operational rules, applied when you run terraform init. No forks, no wrappers.

No credit card or AWS account needed to start.

Stay Informed About New Features

Join the mailing list for releases, new modules, and roadmap updates. No spam. Unsubscribe anytime.

Not convinced yet, or missing a feature you need? Send us an email - we really want to hear your feedback!