Policy-as-code & scanning

OPA/Conftest and Checkov on OpenTofu.

Advanced12 min · lesson 11 of 12

Policy-as-code puts automated guardrails on your infrastructure — “no public S3 buckets,” “every resource must be tagged,” “no security group open to 0.0.0.0/0” — and fails the pipeline when a plan violates them. Because Terraform Cloud’s Sentinel is HashiCorp-specific, the OpenTofu-native path is the open stack: Open Policy Agent with Conftest, evaluating the plan as JSON.

POLICY GATE ON THE PLAN
1tofu plan -out=tfplan.bin
save the planned changes
2tofu show -json tfplan.bin > plan.json
render saved plan to JSON
3conftest test plan.json --policy policy/
OPA/Rego policy on resolved plan
4Pass / fail gate
fail pipeline on violation
Checkov/Trivy scan the HCL for fast feedback; Conftest on the plan JSON is the authoritative gate that sees computed values.
terminal
# render the plan to JSON, then test it against policy
$ tofu plan -out=tfplan.bin
$ tofu show -json tfplan.bin > plan.json
$ conftest test plan.json --policy policy/
FAIL - plan.json - deny: aws_s3_bucket.logs is public (acl = public-read)
policy/s3.rego
package main
deny[msg] {
r := input.resource_changes[_]
r.type == "aws_s3_bucket"
r.change.after.acl == "public-read"
msg := sprintf("%s is public", [r.address])
}

Scanning the config

Alongside policy on the plan, static scanners read the HCL itself for known misconfigurations. Checkov, Trivy (which absorbed tfsec), Terrascan, and KICS all understand OpenTofu/Terraform config and ship hundreds of built-in rules — unencrypted volumes, permissive IAM, missing logging. Run one in CI and fail on high severity, exactly as in the Terraform course; the config format is the same so the tools work unchanged.

terminal
$ checkov -d . --framework terraform --compact
Passed checks: 34, Failed checks: 3
Check: CKV_AWS_18 "Ensure S3 bucket has access logging enabled"
FAILED for resource: aws_s3_bucket.logs
Gate on the plan, not just the code
Static scanners read HCL and miss anything computed at plan time (a public flag set from a variable, a count that expands). Policy-as-code on the plan JSON sees the resolved result, so use both: Checkov/Trivy on the config for fast feedback, Conftest on the plan for the authoritative gate before apply.