Make Non-Compliant Terraform Impossible With Compliance.tf

Replace after-the-fact Terraform scanning with modules that have built-in compliance controls, blocking non-compliant AWS infrastructure before it reaches your plan.

Anton Babenko

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:

terraform
# 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:

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 plan to 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:

terraform
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:

terraform
# 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:

text
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 approachcompliance.tf
When controls are appliedAfter code is written (scanner finds it)Before plan is accepted (module blocks it)
Who maintains rulesYour teamcompliance.tf team
Framework updatesManual effort per frameworkManaged upstream
Coverage scopeWhatever you write policies forSupported 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.

Found this useful?

Share it with your team or post it on LinkedIn, X, or your favorite community.

Next Step

See how to verify these guardrails in practice

The second article in this series shows how IaC scanners, infrastructure scanners, and audit evidence fit around compliance.tf modules after the preventive controls are in place.
Read the verification guide