Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Checkov: Scan Terraform for Misconfigurations

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.

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.

IaC Security Pipeline with CheckovDeveloperTerraform HCLGit PushPull RequestCheckov ScanMisconfig checksSafe Applyterraform applyCommon Terraform Misconfigurations Checkov CatchesPublic S3Open SG RulesNo EncryptionWeak IAM PolicyMissing Logging
Checkov scan Terraform for misconfigurations in the pull-request path before infrastructure reaches production

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.

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.

Checkov Terraform Scan FlowTerraform HCL.tf and .tfvarsParse GraphModule expansionPolicy EngineCKV_* rulesReportOutput FormatsCLI tableJSONSARIFJUnitParse JSON output with a formatter tool when triaging large repos
How Checkov parses Terraform, evaluates CKV policies, and emits CLI, JSON, or SARIF results

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

  1. terraform fmt -check — formatting only.
  2. terraform validate — syntax and provider schema.
  3. Checkov — security and compliance misconfigurations.
  4. terraform plan — intended changes against remote state.
  5. 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.

CI/CD Gate: Checkov Before Terraform PlanfmtvalidateCheckovsecurity gateplanapplyFail Fast: No Cloud Credentials RequiredCheckov runs on fork PRs safely — plan/apply stay on protected branchesPair with remote state locks from manage-terraform-state-safely practices
Recommended CI order: format, validate, Checkov security scan, then Terraform plan and apply

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.

CriteriaCheckovtfsecTerrascan
Primary languagePythonGoGo (Rego policies)
Terraform depthStrong; module download supportStrong; fast binaryStrong; graph-based
Multi-framework IaCTerraform, CFN, K8s, Dockerfile, ARM, Bicep, moreTerraform focus; some cloud templatesBroad cloud and K8s coverage
Custom policiesPython or YAMLRego (limited)Rego (Open Policy Agent)
CI integrationsOfficial Actions, Docker, SARIFGitHub Action, simple CLICLI, admission controller modes
Baseline / skip UXBaseline file + inline skip commentsInline ignoresConfig suppressions
Best fitTeams wanting one scanner for many IaC typesMinimal Go binary, Terraform-only shopsOPA-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.

Which IaC Scanner Fits Your Stack?Need Terraform scanning?Multi-IaC repoTerraform onlyChoose CheckovK8s + TF + Dockertfsec or TrivyFast Go binaryNeed OPA/Rego?Try TerrascanOPA policy teamsCheckov: Scan Terraform for Misconfigurations as default for mixed IaC pipelines
Decision tree: pick Checkov for multi-framework repos, tfsec/Trivy for Terraform-only speed, Terrascan for OPA-native teams

Common gotchas that look like Checkov bugs

  • Unresolved variables — Checkov may skip checks when values are unknown. Use --evaluate-variables and sensible defaults in terraform.tfvars.example.
  • External modules not downloaded — enable download-external-modules or scan after terraform init in 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 true on every Terraform repo before merging infrastructure changes.
  • Store team defaults in .checkov.yaml and 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 validate and before plan so 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

Checkov is an open-source static analysis tool for infrastructure-as-code, maintained by Palo Alto Networks through the Bridgecrew lineage. It reads Terraform HCL without touching your cloud account, so a successful terraform plan can still hide dangerous designs like public S3 buckets, unencrypted RDS, or security groups open to 0.0.0.0/0 on port 22. Checkov ships hundreds of built-in policies mapped to CIS, PCI-DSS, and HIPAA, each with a stable ID such as CKV_AWS_20. On teams that treat IaC like application code, it sits beside terraform validate and terraform fmt and stops bad infrastructure from merging before production.

Checkov requires Python 3.9 or newer. The article recommends pip inside a dedicated virtual environment for CI reproducibility: create a venv, upgrade pip, then pip install checkov and verify with checkov --version. On Ubuntu servers I manage, I keep Checkov isolated from system Python to avoid package conflicts. Alternatives are brew install checkov on macOS, or Docker with bridgecrew/checkov:latest mounted at /tf. Pin the version in CI so local scans and pipeline results match. Test the install with a deliberately insecure security group file, then delete it before committing.

Run checkov -d . --framework terraform against your repo root, or point -f at a single .tf file. Add --download-external-modules true when modules live on the Terraform Registry.

No. Checkov performs static analysis on HCL files locally or in CI and never calls cloud APIs during a standard scan, so forked pull requests can be scanned safely without Terraform plan credentials.

Wire Checkov into the same stage as terraform plan so it blocks merges, not just manual runs. In GitHub Actions, use bridgecrewio/checkov-action@v12 with directory, framework terraform, download_external_modules true, and output_format sarif uploaded via github/codeql-action/upload-sarif so findings appear inline on the pull request diff. In GitLab CI, run the bridgecrew/checkov Docker image in a validate stage before plan, pinning the image tag instead of :latest for reproducible builds. Trigger on changes to .tf, .tfvars, and .checkov.yaml files only.

Store team-wide scan defaults so every developer and CI job uses the same ruleset. The article shows branch main, compact true, framework terraform, download-external-modules true, a skip-check list for noisy rules like CKV_AWS_144, soft-fail false, and evaluate-variables true. The evaluate-variables option lets Checkov resolve Terraform variables when possible, catching more issues than raw HCL alone, though it still cannot know runtime values from remote state. Reference this file with --config-file .checkov.yaml or let Checkov pick it up automatically at the repo root.

Use explicit suppressions auditors can trace. For intentional exceptions, add an inline comment such as #checkov:skip=CKV_AWS_21: followed by a reason string; empty reasons should fail review. For brownfield repos with thousands of existing findings, create a baseline with checkov --create-baseline, commit .checkov.baseline, and fail only on new violations going forward. Schedule quarterly baseline burns to shrink debt rather than letting the baseline rot for years. When built-in CKV rules miss org requirements, write custom Python policies instead of blanket skips.

No single scanner covers everything, and many teams pick one primary gate to reduce alert fatigue. Checkov wins for multi-framework repos covering Terraform, CloudFormation, Kubernetes, and Dockerfiles, with Python or YAML custom policies and official SARIF CI integrations. tfsec is a minimal Go binary strong for Terraform-only shops; note it merged into Trivy's ecosystem, so evaluate overlap if you already scan containers with Trivy. Terrascan suits OPA-native teams wanting Rego policies and graph-based analysis. Compare all three on your actual code, then standardise rather than running duplicate suppressions across tools.

By default Checkov exits with code 1 when any check fails, which correctly fails CI jobs. Use --soft-fail to report violations but exit 0 during initial adoption while you triage noise.

Yes. Pass --download-external-modules true on the CLI or set download-external-modules true in .checkov.yaml. Checkov downloads and expands external modules similarly to terraform init, then evaluates policies against the full resource graph instead of skipping module contents.

Place it after terraform fmt and terraform validate, but before terraform plan and apply. Checkov is fast, needs no cloud credentials, and catches security misconfigurations statically, which keeps forked pull requests safe because plan jobs require secrets. The recommended order is format check, validate, Checkov security scan, then plan and apply. This aligns with mature IaC pipelines where state locking and security scanning solve different problems but both belong before production changes reach infrastructure.

Scanning a legacy repo with thousands of findings blocks all progress if you enforce everything on day one. Instead run checkov -d terraform/ --framework terraform --create-baseline, commit the resulting .checkov.baseline file, and configure future scans to fail only on new violations above that baseline. Treat the baseline as temporary debt, not permanent permission to ignore risk. Schedule intentional quarterly burns where teams fix batches of old findings and regenerate a smaller baseline. Pair this with inline skip comments for genuinely intentional exceptions that need documented reasons.

Several patterns look like Checkov bugs but are configuration gaps. Unresolved variables cause Checkov to skip checks; enable evaluate-variables in .checkov.yaml and provide sensible defaults in terraform.tfvars.example. External modules are invisible unless you set download-external-modules true or scan after terraform init in CI. Exclude provider cache directories and scan source HCL only to avoid .terraform.lock.hcl noise. Deeply nested Terragrunt or vanilla module paths can confuse first-time scans until module graphs stabilise. Running Checkov alongside tfsec or Terrascan without harmonised suppressions also creates duplicate findings that feel like missed issues.

When built-in CKV rules miss requirements like mandatory Environment tags or naming conventions, add Python checks in a policies directory. Extend BaseResourceCheck, define a unique id such as CKV_CUSTOM_NP_1, specify supported_resources, and implement scan_resource_conf returning PASSED or FAILED. Run them with --external-checks-dir policies/checkov/. Document org rules beside Terraform modules so consumers inherit the same expectations. Custom policies fit Nepal data-residency constraints, tagging standards, and other rules no generic CIS benchmark covers. Keep suppressions separate from custom checks unless the exception is truly permanent and documented.

Yes. Checkov is open-source static analysis software. You pay only for the CI runner time and engineer effort to triage findings, not for the scanner itself.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: