BlogIaC

Scanning Terraform with Checkov before you apply

Catch public buckets and open security groups in the plan — in CI, with suppressions that expire and a clean baseline.

Feb 12, 2026·4 min readIntermediate·By the SecOpsLog team · command-tested

Most Terraform incidents are boring in hindsight: an S3 bucket policy with Principal: *, a security group with 0.0.0.0/0 on port 22, or an RDS instance without encryption. Checkov scans IaC statically — no cloud credentials required for the scan itself — and fails the pipeline when policy-as-code rules match your .tf files. The goal is not zero findings forever; it is catching regressions before terraform apply publishes them to the internet.

This note wires Checkov into a local pre-commit hook and a CI job, shows how to baseline known debt without silencing new violations, and sets suppressions that expire. Pair it with Terraform for the modules being scanned and the dedicated Checkov course for custom policies and OPA integration.

Checkov in the Terraform pipeline

Scan runs on every pull request against the plan directory. Fail on new CRITICAL/HIGH unless suppressed with owner and expiry.

1pip installcheckovpin version in CI image2checkov -d .scan .tf and .yaml3Parse SARIF/JSONupload to GitHub Security4Baseline fileskip known legacy debt5New finding failsPRblock merge6Suppress withcomment# checkov:skip=CKV_AWS_207Re-scan on applydefense in depth

Run Checkov locally and in CI

Start with the built-in AWS, Azure, and Kubernetes policies — hundreds of checks maintained by Bridgecrew/Palo Alto. Output JSON in CI so you can diff findings between branches. Soft-fail locally (--soft-fail) while onboarding; hard-fail in main once the team trusts the signal.

Scope scans to directories that actually deploy — terraform/, modules/, Helm charts — and skip .terraform/ provider caches. In monorepos, path filters on pull requests keep CI fast. When a finding is a false positive, fix the policy upstream or document the exception with an owner; silent --skip-check flags in root modules become permanent blind spots within two quarters.

.github/workflows/checkov.yml
- name: Checkov scan
run: |
pip install checkov==3.2.40
checkov -d terraform/ \
--framework terraform \
--output sarif \
--output-file results.sarif \
--soft-fail-on LOW \
--hard-fail-on HIGH

Suppressions that expire

Permanent # checkov:skip= comments rot into permanent blind spots. Prefer a .checkov.baseline for legacy stacks you are chipping away at, and inline skips only with a ticket id and expiry metadata where your policy allows. Review suppressions in PRs the same way you review security group changes.

Custom policies in YAML or Python extend Checkov to org rules — mandatory cost-center tags, approved AMIs, required VPC endpoints. Export SARIF to GitHub Advanced Security so findings appear inline on the diff the author is already looking at. The scan should fail before terraform plan posts to the PR comment, not after merge when apply is one click away.

main.tf
# Legacy public CloudFront origin — ticket INFRA-442, remove by Q3
# checkov:skip=CKV_AWS_86:Origin is behind WAF + OAC
resource "aws_s3_bucket" "assets" {
bucket = "acme-cdn-origin"
}
bash — scan and filter by severitylive
checkov -d terraform/prod --framework terraform --compact
Check: CKV_AWS_20 — S3 bucket ACL allows public read
Check: CKV_AWS_23 — Security group allows 0.0.0.0/0:22
checkov -d terraform/prod --check CKV_AWS_20 --quiet
Passed checks: 1, Failed checks: 0
Static scan is not a penetration test
Checkov reads files on disk. It cannot see runtime misconfigurations, IAM privilege escalation paths in live accounts, or resources created outside Terraform. Run it before every apply — but still enable CloudTrail, Config, and periodic review of what actually deployed.
When to fail vs warn
Hard-fail in CI
Public S3 / open SSH
Unencrypted RDS or EBS
Admin * on security groups
New findings vs baseline
Warn or baseline
Tagging policy gaps
Legacy stacks under migration
Checks pending false-positive review
LOW severity style rules

Where this goes next

Wire Checkov output into SARIF so GitHub or GitLab surfaces findings on the diff. Add custom policies for org-specific requirements — mandatory Environment tags, approved instance types, required KMS keys. Checkov covers custom YAML policies and OPA; pair with Terraform so modules ship secure defaults instead of suppressions.

Go deeper in a courseCheckovPolicy-as-code scanning for Terraform, Kubernetes, and CI pipelines.View course

Related posts