A Checkov scan of Terraform source passes a bucket whose `acl = var.acl` is public-read only in `prod.tfvars` — why, and how do you make Checkov see the production value?
answer
- which values Checkov resolves
- Terraform's automatic tfvars files
- unresolved means UNKNOWN
- the plan already has values
basics
~10 sCheckov renders variables only from defaults, TF_VAR_ variables and Terraform's automatic tfvars files, so it judged the default. Pass --var-file prod.tfvars, or scan the plan JSON with the terraform_plan framework.
solid answer
~40 sCheckov evaluates variables and locals by default, but from the same automatic sources Terraform uses: variable defaults, `TF_VAR_` environment variables, `terraform.tfvars`, `terraform.tfvars.json` and `*.auto.tfvars`. A `prod.tfvars` that the pipeline passes to Terraform with `-var-file` is not read unless you give Checkov `--var-file prod.tfvars` too, which works only with `--directory`. So the bucket was judged against its default — `private` passes. With no default at all, a value check on that attribute returns UNKNOWN, which is recorded as neither passed nor failed, so the bucket silently vanishes from that check. The alternative is to scan `terraform show -json` output: Checkov's `terraform_plan` framework picks up the JSON, and `--repo-root-for-plan-enrichment` maps findings back to the HCL lines.
code
bash · 7 lines# source scan, rendered with the production variables
checkov -d infra/ --var-file infra/env/prod.tfvars -o cli -o junitxml --output-file-path console,checkov-prod.xml
# plan scan of the same configuration
terraform -chdir=infra plan -var-file=env/prod.tfvars -out tfplan.binary
terraform -chdir=infra show -json tfplan.binary > tfplan.json
checkov -f tfplan.json --repo-root-for-plan-enrichment infra/go deeper
Remember that Checkov fills in variable values itself when scanning .tf files, and that it does not know which environment file your pipeline uses.
Explain the automatic tfvars sources, what --var-file adds, and why an unresolved value gives no result instead of a failure.
Show you can make a source scan judge the production values, or move to the terraform_plan framework with enrichment, and say what each costs.
Decide per estate whether environment-specific values are scanned at source, at plan time or both, given credentials, speed and who reads the findings.
## How Checkov treats a Terraform variable When Checkov scans Terraform **source** (`.tf` files), it does not run Terraform. It parses the HCL and then performs **variable rendering**: it replaces `var.` and `local.` references with values it can find, so that checks compare real values rather than expressions. This is on by default (`--evaluate-variables`, environment variable `CKV_EVAL_VARS`). The CLI output shows what it did, for example: ``` Variable acl (of /variables.tf) evaluated to value "private" in expression: acl = ${var.acl} ``` and the JSON output carries an `evaluations` block per result with the variable's file, value and the expressions that used it. ## Where the values come from Checkov mirrors Terraform's **automatic** variable sources, in this order of precedence (later wins): 1. `default` values in `variable` blocks; 2. `TF_VAR_<name>` environment variables; 3. `terraform.tfvars`, then `terraform.tfvars.json`; 4. `*.auto.tfvars` and `*.auto.tfvars.json`, in lexical order; 5. files named with `--var-file`. The last one is the trap. Many teams keep one file per environment — `dev.tfvars`, `prod.tfvars` — and pass it to Terraform with `-var-file`. Checkov has no idea which file your pipeline uses unless you tell it with **`--var-file`**, which can be repeated, needs `--directory` rather than `--file`, and works for Terraform source and Helm scans. ## Two ways the bucket slips through | `variable "acl"` | `prod.tfvars` | What Checkov sees without `--var-file` | Result | |---|---|---|---| | `default = "private"` | `acl = "public-read"` | `private` | the check **passes** against the wrong environment | | no default | `acl = "public-read"` | an unresolved `var.acl` | **UNKNOWN** — no result recorded | The second row deserves attention. When a value comparison meets an attribute that still depends on an unrendered Terraform variable, Checkov's check returns **UNKNOWN**. The report only records PASSED, FAILED and SKIPPED, so an UNKNOWN result simply does not appear: the bucket is missing from that check's results, the summary counts do not mention it, and the job stays green. ## Fix one: render per environment - Run Checkov once per environment with the same variable file Terraform uses: `checkov -d . --var-file prod.tfvars`. - Keep the variable file path in the pipeline definition next to the Terraform step, so the two cannot drift. - Read the `evaluations` data in the JSON output when a result surprises you; it says which file supplied each value. This stays a source scan: fast, no cloud credentials, findings point at your `.tf` lines. Common mistakes on this route: - passing `--var-file` together with `-f`, which the flag does not support — it needs `--directory`; - scanning only the development variables because that is what the pull-request job uses, while production values differ; - assuming an empty result for a check means a pass, when it can mean UNKNOWN. ## Fix two: scan the plan with `terraform_plan` A Terraform plan already contains resolved values. Checkov's **`terraform_plan`** framework scans the JSON form of a plan: ``` terraform plan -out tfplan.binary terraform show -json tfplan.binary > tfplan.json checkov -f tfplan.json ``` Points that matter in practice: - Checkov recognises a plan by its JSON content (a top-level `terraform_version` key); the binary plan file is not scanned. - `--repo-root-for-plan-enrichment <dir>` maps findings back to code blocks and resource IDs in the HCL and makes skip comments in the `.tf` files apply to the plan scan; `--deep-analysis` additionally combines the plan graph with the HCL graph to recover connections the plan lacks, such as those through locals. - A few checks that read the `lifecycle` block, which the plan does not store, are ignored for plans: `CKV_AWS_217`, `CKV_AWS_233`, `CKV_AWS_237` and `CKV_GCP_82`. - Values that are "known after apply" are not in the plan either; an experimental `EVAL_TF_PLAN_AFTER_UNKNOWN` setting reads the plan's `after_unknown` section. The plan route needs state access and provider credentials to produce, and the plan can contain secrets, which is why Checkov's documentation advises running it only in a secured pipeline. Choosing between the two routes as a testing strategy is a broader Terraform workflow decision; the Checkov-specific point is that **source scans only see the values you hand them**. ## What to say in the interview A strong answer names both failure modes — the wrong environment's value passing, and an unresolved value producing no result — then gives the two fixes with their costs: `--var-file` per environment keeps the scan cheap and credential-free, while the `terraform_plan` framework sees exactly what Terraform will do but needs a plan, its credentials and care with the secrets inside it.
- How do you confirm which value Checkov used for a variable in a given result?The CLI output prints a line per evaluated variable — its file, the value and the expression — under each result, and `-o json` includes an `evaluations` object per check result with `var_file`, `value` and the `definitions` that referenced it. If the file shown is `variables.tf`, the default was used, not your environment file.
- After switching to plan scanning, the team's inline skip comments in .tf files stop working. Why?A plain plan scan sees only the JSON, which carries no comments. Passing `--repo-root-for-plan-enrichment` with the directory of the HCL that produced the plan lets Checkov map plan resources back to source blocks, so code snippets and skip comments from the `.tf` files apply again.
saying these in an interview costs you the question
- Checkov reads every .tfvars file in the directory automatically.
- An unresolved variable makes the check fail, so nothing can slip through.
- Checkov runs terraform plan internally to resolve variables.
- You can scan the binary plan file Terraform writes with -out.
- --var-file works the same with -f as with -d.