Skip to content
Verifying compliance.tf Modules: From Guardrails to Audit Evidence

Verifying compliance.tf Modules: From Guardrails to Audit Evidence

Compliance.tf (CTF) modules are designed to make non-compliant infrastructure effectively impossible to create for the controls they cover. Controls are enforced inside the modules through safe defaults, validation rules, and restricted configuration surfaces, as described in the first part of this series.

But if you are on a security or audit team, you still need to answer a simple question: how do we know it really works?

This article walks through how to verify CTF modules in practice, how IaC scanners and infrastructure scanners each play a role, and what kind of audit evidence you can expect.

As with all CTF content, "Terraform" here includes OpenTofu - both are fully supported.

Where CTF Fits With Scanning Tools

Security scanners fall into two categories based on when they run:

  • IaC scanners (pre-apply) - Tools like Checkov and Trivy analyze Terraform source code and plan files before changes are applied. They catch misconfigurations in your code but cannot verify what is actually running in AWS.

  • Infrastructure scanners (post-apply) - Tools like Powerpipe, Prowler, and AWS Config query actual AWS resources via APIs. They verify the real state of your environment but only after resources are deployed.

Notably, Prowler since version 5.14 (released on November 25th, 2025) added IaC scanning capabilities via Trivy integration, making it a hybrid tool that can participate in both stages.

CTF enforces controls inside the module code itself, making high-impact misconfigurations structurally impossible. The scanners then confirm that the guardrails work:

  • Pre-apply - IaC scanners validate your Terraform plan against security policies before anything is created/updated.
  • Post-apply - Infrastructure scanners verify that deployed resources match expected configurations.

You should think of CTF modules as preventive guardrails, and scanners as independent verification that those guardrails are effective.

If you have not read it yet, the architectural overview in Make Non-Compliant Terraform Impossible With compliance.tf explains how CTF modules enforce controls before a plan is accepted.

A Typical Verification Flow

Verification happens at two stages: before you apply infrastructure changes, and after resources exist in AWS.

Pre-Apply: IaC Scanning

  1. Generate a Terraform plan
terraform init
terraform plan -out=plan.out
terraform show -json plan.out > plan.json

This gives you a final artifact (plan.json) you can feed into IaC scanners.

  1. Run IaC scanners against the plan Tools like Checkov or Trivy analyze your plan file:
checkov -f plan.json
trivy config plan.json

With CTF modules, most controls pass automatically because the modules enforce compliant configurations internally. When a check does fail, the scanner reports the finding with a description and remediation guidance. The developer fixes the configuration, regenerates the plan, and runs the scanner again until all checks pass.

  1. Integrate in CI Add IaC scanning to your pipeline so every Pull Request is validated before merge. Non-compliant configurations are blocked before they reach terraform apply.

Post-Apply: Infrastructure Scanning

  1. Deploy infrastructure After terraform apply, resources exist in your AWS account.

  2. Run infrastructure scanners Tools like Powerpipe, Prowler, or AWS Config query actual AWS resources:

powerpipe control run aws_compliance.benchmark.cis_v300
prowler aws --compliance cis_3.0_aws

These tools verify the real state of your environment, catching any drift or resources created outside of Terraform.

  1. Schedule regular scans Run infrastructure scans on a schedule (daily, weekly) to detect configuration drift and resources not managed by CTF modules.

The goal is not to replace your existing validation process. The goal is to make it predictable: CTF modules handle the heavy lifting, IaC scanners confirm the plan is clean, and infrastructure scanners verify the deployed state matches expectations.

Example: Proving S3 Logging Is Always On

In the first article we looked at how CTF prevents attempts to disable S3 logging inside a module. The s3_bucket_logging_enabled control is enforced across SOC 2, CIS, and PCI DSS.

Both scanning stages make this visible to security teams and auditors:

Pre-apply (IaC scanning):

  1. A developer writes Terraform configuration using the S3 bucket module from CTF.
  2. Checkov or Trivy scans the plan file and confirms logging is configured.
  3. Even if the developer tried to pass logging = {} or removed logging argument, the CTF module enforces logging internally, so the plan will fail until the developer specifies the required configuration (target_bucket is required in this case).

Post-apply (infrastructure scanning):

  1. After terraform apply, the S3 bucket exists in AWS.
  2. Powerpipe or Prowler queries the actual bucket and confirms logging is enabled.
  3. If someone creates a misconfigured bucket outside of CTF (via console or another tool), the infrastructure scanner flags it as non-compliant.

The combination of "you cannot disable logging in the module" and "independent scanners confirm logging is on at both stages" gives you a strong story for auditors.

What Evidence Auditors Actually Get

From an audit perspective, the interesting part is not the Terraform code itself. It is the repeatable artifacts that show controls are implemented and checked over time.

With CTF and your scanning tools you can produce:

  • Pre-apply scan reportsIaC scanner output from CI pipelines showing that every Terraform plan was validated before deployment.

  • Post-apply validation reports Infrastructure scanner reports (from Powerpipe, Prowler, or AWS Config) showing which controls passed or failed for each account, region, or environment.

  • Module documentation that references controls Each CTF module documents which framework controls it implements and how. This gives auditors a clear mapping from "control ID" to "module behavior".

  • Version history of modules and controls As modules evolve, you can track which controls were added, changed, or removed in each version, and link that to your change management process.

  • Exception documentation Where exceptions are allowed, flags or alternate modules make them explicit. Validation reports show when and where these exceptions are active.

Taken together, this becomes a strong narrative: controls are enforced preventively by CTF modules, validated pre-apply by IaC scanners, and independently verified post-apply by infrastructure scanners.

Putting It All Together

When you combine the perspectives, the picture looks like this:

  • Prevention: CTF modules make non-compliant configurations impossible to apply for the controls they cover, by design. (Read first article)
  • Pre-apply verification: IaC scanners (Checkov, Trivy) confirm the Terraform plan is compliant before any resources are created.
  • Post-apply verification: Infrastructure scanners (Powerpipe, Prowler, AWS Config) confirm that deployed resources match expectations and catch drift.

You keep your existing scanning tools. You keep Terraform. You just move a large chunk of compliance work into reusable modules that ship with guardrails, while your scanners provide independent verification at both stages.

If you want to see this in your own environment:

  • Start with the available modules and review the controls they enforce.
  • Check the Technical Usage guide for setup instructions.
  • Add an IaC scanner (Checkov or Trivy) to your CI pipeline for pre-apply validation.
  • Schedule infrastructure scans with Powerpipe or Prowler to verify deployed resources.
  • Explore the supported frameworks to find the right compliance baseline for your organization.