Scanning plan vs code

Catch what only appears at plan.

Intermediate12 min · lesson 7 of 12

Scanning Terraform source has a blind spot: values that come from variables, locals, or modules are not resolved in the raw .tf, so a bucket made public by a variable, or a setting injected by a module, may not be visible to a code-only scan. The fix is to scan the plan: run terraform plan -out, convert it to JSON with terraform show -json, and point Checkov at that. The plan has fully-resolved values, so the scan sees what will actually be created.

SCANNING THE RESOLVED PLAN
1terraform plan -out
-out=tfplan.bin
2terraform show -json
convert plan to JSON
3checkov -f tfplan.json
scan the resolved plan
4Findings on real config
sees var- and module-driven values
The plan JSON has fully-resolved values, so the scan catches risk a code-only scan of raw .tf cannot see.
terminal
$ terraform init && terraform plan -out=tfplan.bin
$ terraform show -json tfplan.bin > tfplan.json
$ checkov -f tfplan.json
# now checks evaluate resolved values (module outputs, var-driven flags) that
# a scan of the raw .tf could not see

Code scan and plan scan together

The two are complementary, not either/or. Scanning source gives fast, early feedback right in the editor and on every commit, with file/line locations developers can jump to. Scanning the plan gives authoritative, fully-resolved results that catch what only appears after resolution — at the cost of needing init/plan (and cloud credentials for some providers). Mature pipelines run source scanning on commit and plan scanning as the gating step before apply.

A code-only scan can miss module- and variable-driven risk
If your Terraform gets its security-relevant settings from modules or variables (as most real code does), a scan of the raw .tf may report clean while the plan would create something insecure. Always scan the plan JSON as the authoritative gate before apply; treat the source scan as fast early feedback, not the final word.