Operational Rule Definitions
Which rules run is decided before your source string gets a say. An
organization host applies what that organization's admins configured. A
framework host applies your own organization's Baseline when its rules state is
Enforced, and ?rules= then replaces or subtracts from that set for a single
download, while ?add_rules= adds to it. Either way, picking a rule starts with knowing that it exists
and what it would do.
This page is that list. Every selectable rule appears below with its id, its
parameters, its shipped defaults, and the transformers it runs, in the HCL the
engine loads. Read the one you are considering before you put its id in a
source line.
Each rule has its own anchor, so a rule can be linked to directly.
lifecycle_ignore_ami_changes is /rules/definitions/#lifecycle_ignore_ami_changes.
What this page is, and what it is not
It is the list of rules you can name, with what each one would change. The definitions are rendered from the rule catalog that ships with the engine, so a change in the engine changes this page.
It is not a reusable artifact. There is no licence attached to these definitions and no grant of any kind is made. They are published so you can choose a rule, not so they can be lifted into another tool.
It is not the engine's source files byte-for-byte. See How this page is rendered.
It is not a record of what ran on a download. A module compliance.tf builds
for you carries a compliancetf-manifest.json naming every rule the build
received, each with an outcome saying what it actually did — a listed rule
may have enforced nothing. That is the per-download record; this is the menu.
Rules are operational, not regulatory — see Operational Rules for how the two layers differ.
How this page is rendered
The definitions come from the rule catalog the engine publishes
(rules-catalog.json, engine version 0.20.1), which is the
engine's own serialization of its rule files. This site renders a vendored
snapshot of that catalog rather than reading it live, so the version above is
the snapshot's own: when it lags the engine the registry is running, a
definition on this page lags with it.
Three things follow from rendering the catalog rather than copying the source files, and they matter when you are reading a definition to decide on it:
- The
intentblock is not shown. It selects which blocks a rule inspects. The catalog stores a derived, lossy form of it, and reconstructing one would mean printing HCL that a rule does not have. - A field left at an empty or wildcard default is absent from the transformer
block. The catalog drops those. The parameter table in each definition is
still complete, so a parameter you see with a
[]default is real even when no transformer line mentions it. - A value that equals a parameter's default is shown as
param.<name>. That is how the rules are written, and it keeps a configurable value from reading as a hard-coded one.
The per-rule pages carry the parts a definition cannot show you: before-and-after HCL, known limits, failure modes, and the break-glass path.
Naming a rule
Append the rule id to the module source, exactly as it is written below. Nothing else about the source line changes.
module "example" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=lifecycle_ignore_ami_changes"
}Parameters never travel in the URL, so a rule named this way runs with the defaults printed below. Changing a parameter means configuring the rule in an organization Baseline or ruleset — see Baseline rules and rulesets.
lifecycle_ignore_ami_changes
Ignore AMI changes on EC2 instances to prevent unnecessary replacements
| Severity | warning |
| Transformers | lifecycle (safe) |
| Page | Ignore AMI Changes |
rule "lifecycle_ignore_ami_changes" {
description = "Ignore AMI changes on EC2 instances to prevent unnecessary replacements"
severity = "warning"
param "ignore_changes" {
type = "list(string)"
default = ["ami", "image_id"]
description = "Lifecycle attributes to ignore"
}
param "resource_types" {
type = "list(string)"
default = ["aws_instance", "aws_launch_template"]
description = "Target resource type patterns"
}
transformer "lifecycle" {
action = "add"
ignore_changes = param.ignore_changes
resource_types = param.resource_types
}
}lifecycle_ignore_autoscaling_changes
Ignore autoscaling-managed capacity on DynamoDB tables to prevent drift
| Severity | warning |
| Transformers | lifecycle (safe) |
| Page | Ignore Autoscaling Changes |
rule "lifecycle_ignore_autoscaling_changes" {
description = "Ignore autoscaling-managed capacity on DynamoDB tables to prevent drift"
severity = "warning"
param "ignore_changes" {
type = "list(string)"
default = ["read_capacity", "write_capacity"]
description = "Lifecycle attributes to ignore"
}
param "resource_types" {
type = "list(string)"
default = ["aws_dynamodb_table"]
description = "Target resource type patterns"
}
transformer "lifecycle" {
action = "add"
ignore_changes = param.ignore_changes
resource_types = param.resource_types
}
}lifecycle_ignore_tag_keys
Ignore drift on specific resource tag keys (e.g. an external scanner tag) via lifecycle ignore_changes, without exposing an arbitrary lifecycle value
| Severity | warning |
| Transformers | lifecycle (safe) |
| Family | lifecycle_ignore_tags — at most one rule in a family applies to a given resource type |
| Page | Ignore Selected Tag Keys |
rule "lifecycle_ignore_tag_keys" {
description = "Ignore drift on specific resource tag keys (e.g. an external scanner tag) via lifecycle ignore_changes, without exposing an arbitrary lifecycle value"
severity = "warning"
family = "lifecycle_ignore_tags"
param "tag_keys" {
type = "list(string)"
default = []
description = "AWS tag keys to ignore drift on. Each is emitted as ignore_changes = [tags[\"<key>\"]] with safe literal encoding; empty = unconfigured no-op"
}
param "resource_types" {
type = "list(string)"
default = ["aws_s3_bucket"]
description = "Target resource type patterns. A module with none of these is not applicable"
}
transformer "lifecycle" {
action = "add"
resource_types = param.resource_types
}
}lifecycle_ignore_tags
Ignore tag changes in lifecycle to prevent Terraform drift on managed resources
| Severity | warning |
| Transformers | lifecycle (safe) |
| Page | Ignore Tag Changes |
rule "lifecycle_ignore_tags" {
description = "Ignore tag changes in lifecycle to prevent Terraform drift on managed resources"
severity = "warning"
param "ignore_changes" {
type = "list(string)"
default = ["tags", "tags_all"]
description = "Lifecycle attributes to ignore"
}
param "resource_types" {
type = "list(string)"
default = ["aws_*"]
description = "Target resource type patterns"
}
transformer "lifecycle" {
action = "add"
ignore_changes = param.ignore_changes
resource_types = param.resource_types
}
}lifecycle_prevent_destroy_data
Prevent accidental destruction of data-storage resources (S3, RDS instances and clusters, DynamoDB, EFS, ElastiCache replication groups)
| Severity | error |
| Transformers | lifecycle (safe) |
| Page | Prevent Destroy Data |
rule "lifecycle_prevent_destroy_data" {
description = "Prevent accidental destruction of data-storage resources (S3, RDS instances and clusters, DynamoDB, EFS, ElastiCache replication groups)"
severity = "error"
param "resource_types" {
type = "list(string)"
default = ["aws_s3_bucket", "aws_db_instance", "aws_rds_cluster", "aws_dynamodb_table", "aws_efs_file_system", "aws_elasticache_replication_group"]
description = "Data-storage resource types to protect"
}
transformer "lifecycle" {
action = "add"
prevent_destroy = true
resource_types = param.resource_types
}
}lifecycle_prevent_destroy_encryption
Prevent destruction of KMS keys and Secrets Manager secrets
| Severity | error |
| Transformers | lifecycle (safe) |
| Page | Prevent Destroy Encryption |
rule "lifecycle_prevent_destroy_encryption" {
description = "Prevent destruction of KMS keys and Secrets Manager secrets"
severity = "error"
param "resource_types" {
type = "list(string)"
default = ["aws_kms_key", "aws_secretsmanager_secret"]
description = "Encryption resource types to protect"
}
transformer "lifecycle" {
action = "add"
prevent_destroy = true
resource_types = param.resource_types
}
}provisioner_remove_blocks
Remove provisioner blocks which are an anti-pattern in reusable modules
| Severity | warning |
| Transformers | block_removal (caution) |
| Page | No Provisioners |
rule "provisioner_remove_blocks" {
description = "Remove provisioner blocks which are an anti-pattern in reusable modules"
severity = "warning"
param "resource_types" {
type = "list(string)"
default = ["*"]
description = "Target resource type patterns"
}
transformer "block_removal" {
block_type = "provisioner"
}
}resource_restrict_instance_types
Restrict EC2 instance types to cost-effective options
| Severity | error |
| Transformers | attribute_restriction (safe) |
| Page | Restrict Instance Types |
rule "resource_restrict_instance_types" {
description = "Restrict EC2 instance types to cost-effective options"
severity = "error"
param "denied" {
type = "list(string)"
default = ["p3.*", "p4.*", "x1.*", "x2.*", "u-*"]
description = "Denied instance type patterns"
}
param "message" {
type = "string"
default = "GPU and high-memory instance types are not permitted without approval"
description = "Validation error message"
}
transformer "attribute_restriction" {
resource_type = "aws_instance"
attribute = "instance_type"
denied = param.denied
message = param.message
}
}variable_allowed_regions
Restrict the module's region variable to an org-approved allowlist
| Severity | error |
| Transformers | variable_assertion (safe) |
| Page | Allowed Regions |
rule "variable_allowed_regions" {
description = "Restrict the module's region variable to an org-approved allowlist"
severity = "error"
param "regions" {
type = "list(string)"
default = []
description = "Allowed AWS regions (enumerated, no globs). Empty = unconfigured: the rule injects nothing and enforces nothing"
}
param "allow_unset" {
type = "bool"
default = false
description = "Accept a null region (provider-inherited). Unenforced by consent - it does not permit a wrong region"
}
param "variable_name" {
type = "string"
default = "region"
description = "Name of the module variable carrying the region"
}
param "resource_types" {
type = "list(string)"
default = ["aws_*"]
description = "Resource types this rule applies to. A module with none is not applicable"
}
transformer "variable_assertion" {
variable = param.variable_name
condition = "var.{{variable}} == null ? {{allow_unset}} : contains({{values}}, var.{{variable}})"
error_message = "Variable \"{{variable}}\" must be one of the regions allowed by your organization: {{values}}. An empty or unknown value is rejected."
allow_unset = param.allow_unset
}
}Rules that are not listed here
The registry also carries a small set of internal content-processing rules.
They are not selectable — ?rules= does not accept them — so they are outside
what this page is for, which is choosing a rule to name. They are not among the
rules above and their definitions are not published.
They are not invisible either: a module compliance.tf builds for you carries a
compliancetf-manifest.json naming each rule the build received, internal ones
included, each with an outcome recording what it actually did.
Terraform and OpenTofu compatible
Every rule above reads ordinary HCL, and anything it changes stays ordinary HCL. Modules behave the same under Terraform (1.x+) and OpenTofu (1.6+).