You've been working on the same S3 module configuration for an hour. CI blocked the pull request: Checkov reports 14 findings. Three are real. The rest are policy noise from rules written for a different service. The sprint review is tomorrow.
This is how compliance debt accumulates. Write code, scan it, negotiate findings, defer what you can, fix what you must. Compliance becomes a column in the backlog rather than a property of the infrastructure.
The alternative: build compliance controls into the modules themselves. When the module blocks non-compliant values at plan time, there's no finding to debate and no scanner policy to keep aligned with what the module actually creates.
Coverage scope
Compliance.tf currently covers a specific set of modules for AWS. Controls apply only to those modules. For modules without a compliance.tf version, requests fall back to the original terraform-aws-modules. Check the module catalog to confirm which services are available today.
Terraform and OpenTofu
Compliance.tf works with both Terraform and OpenTofu. This article uses "Terraform" for brevity, but all examples and concepts apply equally to OpenTofu users.
A Different Approach: Compliance at the Registry Level
Compliance.tf is a private Terraform registry that serves pre-curated versions of terraform-aws-modules with compliance constraints built into the module code. The interface is identical to the original modules. The difference is what happens when you pass a non-compliant value.
Here is what that looks like:
# Before: Using official Terraform registrymodule "s3_bucket" { source = "terraform-aws-modules/s3-bucket/aws" version = "5.9.0" bucket = "my-data"} # After: Using compliance.tf registry with SOC 2 (CC6.1, CC7.2) controlsmodule "s3_bucket" { source = "soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws" version = "5.9.0" bucket = "my-data"}Only the source URL changes. The module interface — every variable name, every output — stays the same. The registry namespace (soc2.compliance.tf, pcidss.compliance.tf, hipaa.compliance.tf) selects which control set applies to that module.
Under the hood:
- The registry serves versions of
terraform-aws-moduleswith compliance constraints added to the module code. - Each namespace maps to a specific framework control set: SOC 2 (CC6.1, CC7.2), PCI DSS v4.0 (10.2.1, 3.4.1), CIS AWS v6.0 (3.3), HIPAA, and others.
- Modules ship with safe defaults and Terraform validations that reject non-compliant inputs.
For the modules compliance.tf supports, there are no wrapper modules to build, no policy-as-code rules to maintain, and no scanner policies to keep aligned with upstream framework changes. The compliance layer travels with the module.
How Controls Are Enforced Inside the Modules
Compliance.tf applies constraints inside the module code, before resources are provisioned. Controls map directly to framework requirements.
Enforcement happens through three mechanisms:
- Safe defaults. Options like encryption, logging, public access blocks, and secure transport are on by default and set to compliant values. Callers do not need to configure them to get compliant behavior.
- Required inputs with validators. Certain variables must be provided and must satisfy validation rules. A non-compliant value causes
terraform planto fail with a clear error. - Restricted configuration surface. Some dangerous options are not exposed at all. Others are accessible only through explicit exception paths.
For example, a TLS version constraint looks like this inside the module:
variable "minimum_tls_version" { description = "Minimum version of TLS" type = string default = "TLS1_2" validation { condition = contains(["TLS1_2", "TLS1_3"], var.minimum_tls_version) error_message = "TLS version must meet compliance requirements (TLS1_2 or higher)." }}When terraform plan runs, Terraform evaluates validation rules before generating the plan. A non-compliant value fails immediately. The configuration never enters a plan file, so there is no finding for a scanner to report — for the controls this module enforces.
Why Bypass Attempts Fail
Take S3 bucket logging. It is required by SOC 2 (CC7.2), PCI DSS v4.0 (10.2.1), and CIS AWS v6.0 (3.3). With a standard module, a developer might disable it by omitting the block or passing an empty value:
# Attempting to skip logging configurationmodule "s3_bucket" { source = "soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws" version = "5.9.0" bucket = "my-data" # Trying to disable logging - this will not work logging = {}}With compliance.tf, this fails. The S3 bucket logging control is enforced inside the module. The constraint is structural: the module ensures logging is configured regardless of what the caller passes.
The error is explicit:
Control 's3_bucket_logging_enabled': `logging.target_bucket` must be set to enable S3 bucket access logging.
Read more: https://compliance.tf/docs/controls/aws/s3_bucket_logging_enabled
Frameworks: SOC 2 (CC7.2), CIS AWS v6.0 (3.3), PCI DSS v4.0 (10.2.1)
Where exceptions are needed, they require explicit flags or separate modules. Any deviation is visible to security teams and auditors. Learn how to customize controls when your situation requires it.
DIY Scanning vs Built-in Prevention
Both IaC scanners and compliance.tf modules aim for compliant infrastructure. They reach it differently.
| DIY approach | compliance.tf | |
|---|---|---|
| When controls are applied | After code is written (scanner finds it) | Before plan is accepted (module blocks it) |
| Who maintains rules | Your team | compliance.tf team |
| Framework updates | Manual effort per framework | Managed upstream |
| Coverage scope | Whatever you write policies for | Supported modules and controls |
The shift is from detection to prevention. When a control lives inside the module, there is no gap between the policy and what the module creates. Your scanners — Checkov, Trivy, Prowler — keep running. They provide independent verification that the guardrails work.
For the enterprise context behind this approach — including an honest comparison with using terraform-aws-modules directly — see Why Enterprises Choose Compliance.tf Over terraform-aws-modules.
Next Steps
Compliance.tf modules enforce controls at the point of creation. Your existing scanners keep running alongside and provide independent confirmation. That gives you prevention where the module covers it, and detection for everything else.
- Browse the module catalog to see which services are covered.
- Review the controls each module enforces.
- Read the second part of this series to see how IaC scanners and infrastructure scanners fit into the verification story.
Found this useful?
Share it with your team or post it on LinkedIn, X, or your favorite community.
Next Step
