
September 10, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Checkov: Scan Terraform for Misconfigurations before every plan and apply. A single public S3 bucket or wide-open security group can undo months of careful application work. Terraform makes infrastructure repeatable, but repeatability also means you can ship the same mistake to production at scale. Static analysis with Checkov catches those errors in pull requests, long before cloud bills and incident calls start. This guide walks through install, local scans, CI wiring, suppressions, and custom policies—the workflow I use alongside Infrastructure as Code with Terraform on real deployments.
checkov -d . --framework terraform to scan HCL for misconfigurations like public resources, weak encryption, and missing logging. Add it to CI so every pull request fails on new violations before terraform apply.What is Checkov and why should you scan Terraform for misconfigurations?
Checkov is an open-source static analysis tool for infrastructure-as-code. Palo Alto Networks maintains it through the Bridgecrew lineage. It reads Terraform, CloudFormation, Kubernetes manifests, Dockerfiles, and several other formats without touching your cloud account.
That matters because Terraform plans show what will change, not whether the design is safe. A plan can succeed while creating an RDS instance with no encryption, an IAM policy with * actions, or an EC2 security group open to 0.0.0.0/0 on port 22.
Checkov ships with hundreds of built-in policies mapped to CIS benchmarks, PCI-DSS, HIPAA, and vendor best practices. Each finding gets a stable ID like CKV_AWS_20, which makes triage and suppression predictable across teams.
On teams that treat IaC like application code, Checkov sits next to terraform validate and terraform fmt. It does not replace a cloud posture management tool that reads live APIs. It prevents bad Terraform from merging in the first place.
If you already compare scanners, see the broader picture in IaC security scan with tfsec, Checkov, and Terrascan. The goal here is a Checkov-first workflow you can ship this week.
How do you install Checkov on Linux or macOS?
Checkov runs on Python 3.9 or newer. Pick one install path and pin the version in CI so local and pipeline results match.
Install with pip (recommended for CI runners)
python3 -m venv .venv-checkov
source .venv-checkov/bin/activate
pip install --upgrade pip
pip install checkov
checkov --version On Ubuntu servers I manage for client workloads, I keep Checkov in a dedicated venv rather than system Python. That avoids conflicts with other packages and makes upgrades reversible. The same pattern applies to Linux system administration environments where multiple Python tools coexist.
Install with Homebrew or Docker
brew install checkov
checkov --version docker pull bridgecrew/checkov:latest
docker run --tty --volume $(pwd):/tf bridgecrew/checkov:latest -d /tf Docker works well when you cannot install Python packages on a shared runner. Mount the repo at /tf and pass the same CLI flags you would use locally.
Verify the install against sample Terraform
Create a minimal file with an intentional flaw:
cat > bad-sg.tf <<'EOF'
resource "aws_security_group" "bad" {
name = "bad-sg"
description = "Intentionally insecure for testing"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
EOF
checkov -f bad-sg.tf --framework terraform You should see failed checks referencing open SSH from the internet. Delete the test file before committing.
How do you run Checkov against Terraform code locally?
Local scans are where you learn Checkov's noise profile before blocking merges. Start permissive, then tighten.
Scan a directory or single file
checkov -d ./terraform --framework terraform
checkov -f ./terraform/main.tf --framework terraform
checkov -d . --framework terraform --download-external-modules true The --download-external-modules true flag matters when modules live on the Terraform Registry or in private Git repos. Without it, Checkov may skip resources defined inside external modules and give a false sense of safety.
Useful CLI flags for daily work
--check CKV_AWS_18,CKV_AWS_19— run only specific policy IDs during focused fixes.--skip-check CKV_AWS_145— temporarily skip a noisy rule while you refactor.--soft-fail— report failures but exit 0; useful during initial adoption.--compact— shorter CLI output for logs.--output json > checkov.json— machine-readable output for dashboards or a JSON formatter.--config-file .checkov.yaml— centralise flags for the whole repo.
Project-level configuration with .checkov.yaml
Store scan defaults at the repo root so every developer and CI job uses the same ruleset:
branch: main
compact: true
framework:
- terraform
download-external-modules: true
skip-check:
- CKV_AWS_144
- CKV2_AWS_6
soft-fail: false
evaluate-variables: true The evaluate-variables option lets Checkov resolve Terraform variables when possible. Static analysis still cannot know runtime values from remote state, but it catches more issues than raw HCL alone.
Scan only what changed
Large monorepos benefit from scoping. Pair Checkov with Git diff logic in CI:
git diff --name-only origin/main...HEAD | grep '\.tf$' | while read f; do
checkov -f "$f" --framework terraform --compact
done This is faster than scanning ten years of modules on every push. Keep a nightly full scan scheduled separately so drift in untouched files still surfaces.
How do you integrate Checkov into CI/CD pipelines?
Checkov earns its keep when it blocks merges, not when someone runs it manually once a month. Wire it into the same stage as terraform plan.
GitHub Actions example
name: terraform-security
on:
pull_request:
paths:
- '.tf'
- '.tfvars'
- '.checkov.yaml'
jobs:
checkov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bridgecrewio/checkov-action@v12
with:
directory: terraform/
framework: terraform
download_external_modules: true
soft_fail: false
output_format: sarif
output_file_path: checkov.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: checkov.sarif SARIF upload puts findings inline on the pull request diff. Developers fix issues where they write code instead of hunting log files. For a fuller pipeline layout, see Terraform CI/CD with GitHub Actions.
GitLab CI example
Several production sites I maintain run GitLab CI before Deployer releases. The Checkov job fits naturally before plan:
checkov:
stage: validate
image:
name: bridgecrew/checkov:latest
entrypoint: [""]
script:
- checkov -d terraform/ --framework terraform --compact --output cli
rules:
- changes:
- terraform/**/*
- .checkov.yaml Pin the Docker image tag instead of :latest when you need reproducible builds. Bump the tag deliberately after testing new Checkov releases locally.
Where Checkov sits in the Terraform workflow
terraform fmt -check— formatting only.terraform validate— syntax and provider schema.- Checkov — security and compliance misconfigurations.
terraform plan— intended changes against remote state.- Manual or policy-gated
terraform apply.
Checkov runs before plan because it is fast and needs no cloud credentials. That keeps forked pull requests safe. Plan jobs require secrets; static analysis does not.
Projects like Adventure Third Pole Trek rely on predictable deploy pipelines. Adding Checkov early prevents security regressions from reaching the same paths that carry application releases.
Align this with manage Terraform state safely and remote backend practices. Security scanning and state locking solve different problems, but both belong in a mature IaC pipeline.
How do you suppress false positives and write custom Checkov policies?
Every Terraform scanner produces noise at first. Public ALB listeners, intentional break-glass IAM roles, and legacy modules trigger findings you may accept temporarily. Handle suppressions explicitly so auditors can trace decisions.
Inline skip comments in Terraform
resource "aws_s3_bucket" "logs" {
#checkov:skip=CKV_AWS_21:Company policy uses centralized logging bucket with ACL disabled elsewhere
bucket = "central-logs-prod"
} Each skip needs a reason string. Empty reasons fail review on teams that treat IaC like production code. That is the right bar.
Baseline existing debt
Adopting Checkov on a brownfield repo with thousands of findings blocks all progress. Create a baseline instead:
checkov -d terraform/ --framework terraform --create-baseline
git add .checkov.baseline
git commit -m "Add Checkov baseline for existing Terraform debt" Future scans fail only on new violations. Schedule quarterly baseline burns to shrink debt intentionally. Do not let the baseline rot for years.
Custom Python policies
When built-in CKV rules miss org-specific requirements, add a custom check. Checkov discovers Python files in a configured directory:
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
from checkov.common.models.enums import CheckResult, CheckCategories
class S3BucketMustHaveEnvironmentTag(BaseResourceCheck):
def __init__(self):
super().__init__(
name="S3 bucket must include Environment tag",
id="CKV_CUSTOM_NP_1",
categories=[CheckCategories.GENERAL_SECURITY],
supported_resources=["aws_s3_bucket"],
)
def scan_resource_conf(self, conf):
tags = conf.get("tags", [{}])[0]
if tags.get("Environment"):
return CheckResult.PASSED
return CheckResult.FAILED
check = S3BucketMustHaveEnvironmentTag() Run custom checks with:
checkov -d terraform/ --framework terraform --external-checks-dir policies/checkov/ Org-specific tagging rules, naming conventions, and Nepal data-residency constraints fit custom policies well. Document them beside your Terraform modules so module consumers inherit the same expectations.
Pre-commit hook for instant feedback
repos:
- repo: https://github.com/bridgecrewio/checkov
rev: '' # pin a release tag
hooks:
- id: checkov
args: ["-d", "terraform/", "--framework", "terraform", "--compact"] Pre-commit catches issues before push. CI remains the enforcement gate because developers can bypass local hooks.
Checkov vs tfsec vs Terrascan: which Terraform scanner should you use?
No single scanner covers everything. Many teams run Checkov as the primary gate and keep Trivy or tfsec for overlap on critical repos. The comparison is pragmatic, not religious.
| Criteria | Checkov | tfsec | Terrascan |
|---|---|---|---|
| Primary language | Python | Go | Go (Rego policies) |
| Terraform depth | Strong; module download support | Strong; fast binary | Strong; graph-based |
| Multi-framework IaC | Terraform, CFN, K8s, Dockerfile, ARM, Bicep, more | Terraform focus; some cloud templates | Broad cloud and K8s coverage |
| Custom policies | Python or YAML | Rego (limited) | Rego (Open Policy Agent) |
| CI integrations | Official Actions, Docker, SARIF | GitHub Action, simple CLI | CLI, admission controller modes |
| Baseline / skip UX | Baseline file + inline skip comments | Inline ignores | Config suppressions |
| Best fit | Teams wanting one scanner for many IaC types | Minimal Go binary, Terraform-only shops | OPA-native policy teams |
tfsec merged into Trivy's ecosystem; if you already run Trivy for containers and IaC, evaluate overlap before doubling tools. Checkov's breadth wins when Terraform, Kubernetes manifests, and Dockerfiles live in one repo.
For Terraform Cloud users comparing policy engines, Sentinel policy as code runs inside HashiCorp's platform. Checkov stays attractive on self-managed GitLab, GitHub, or Azure DevOps pipelines where Sentinel is not available.
Common gotchas that look like Checkov bugs
- Unresolved variables — Checkov may skip checks when values are unknown. Use
--evaluate-variablesand sensible defaults interraform.tfvars.example. - External modules not downloaded — enable
download-external-modulesor scan afterterraform initin CI. - Generated .terraform.lock.hcl noise — exclude provider cache dirs; scan source HCL only.
- Duplicate findings across tools — harmonise suppressions or pick one primary scanner to avoid alert fatigue.
Structure modules with Terragrunt or vanilla modules consistently. Deeply nested paths confuse first-time scans until module graphs stabilise.
Key Takeaways
- Run
checkov -d . --framework terraform --download-external-modules trueon every Terraform repo before merging infrastructure changes. - Store team defaults in
.checkov.yamland pin the Checkov version in CI Docker tags or pip constraints. - Use baselines for legacy debt, inline skip comments with reasons for intentional exceptions, and custom Python policies for org rules.
- Place Checkov after
validateand beforeplanso forked pull requests scan safely without cloud credentials. - Export SARIF or JSON for review tooling; CLI output alone does not scale on large monorepos.
- Compare Checkov with tfsec and Terrascan on your actual code, then standardise on one primary gate to reduce noise.
People Also Ask
Does Checkov need AWS credentials to scan Terraform?
No. Checkov performs static analysis on HCL files locally or in CI. It never calls cloud APIs during a standard scan. That makes it safe on untrusted fork pull requests where you would not expose Terraform plan credentials.
Can Checkov scan Terraform modules from the registry?
Yes, when you pass --download-external-modules true or set the equivalent option in .checkov.yaml. Checkov downloads and expands modules similarly to terraform init, then evaluates policies against the full graph.
What exit code does Checkov return on failures?
By default Checkov exits with code 1 when any check fails, which fails CI jobs as expected. Use --soft-fail during adoption if you want exit 0 while still printing failures. Remove soft-fail once the team addresses baseline debt.
How is Checkov different from terraform plan?
terraform plan compares desired state to remote state and needs backend credentials. Checkov reads source files only and evaluates security policies against resource definitions. Use both: Checkov for policy, plan for change review. Official Terraform plan documentation covers the plan side; Checkov covers pre-merge misconfiguration detection.
Ship safer Terraform starting this week
Checkov: Scan Terraform for Misconfigurations is the lowest-friction security upgrade most IaC repos can make. Install it locally, add a CI job, baseline existing noise, and tighten suppressions over time. Pair scanning with solid module design, pinned providers from provider version pinning, and ongoing testing and optimization practices.
If you want help wiring Checkov into GitLab CI, hardening EC2 or VPS Terraform, or auditing an existing stack, review the portfolio or reach out through contact us. For broader platform work, see custom software development and support and maintenance options. More context on the author lives on about me and the home page.
Official references: the Checkov quick start guide and the Checkov GitHub repository stay current with flags, frameworks, and release notes.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

