What Checkov is
Checkov (open source, from Prisma Cloud) is a static analysis tool for IaC, written in Python. It understands relationships between resources (a graph), so it can detect issues such as "no public access block resource is attached to this bucket".
pip install checkov
checkov -d . # scan a folder
checkov -f main.tf # scan one file
checkov -d . --framework terraform
checkov -d . --compact --quiet # failed checks only, compact
checkov -d . -o sarif --output-file-path results/
Reading results
Check: CKV_AWS_24: "Ensure no security groups allow ingress from 0.0.0.0:0 to port 22"
FAILED for resource: aws_security_group.web
File: /network.tf:3-20
Guide: https://docs.prismacloud.io/...
Each check has an ID (CKV_AWS_*, CKV_AZURE_*, CKV_K8S_*, CKV_DOCKER_*), the offending resource, file, lines and remediation guide. The exit code is non-zero when checks fail, so CI stops.
Scanning the plan instead of the code
Code uses variables and modules, so Checkov sometimes cannot know the final value. Scanning the plan JSON is more accurate, with values resolved:
terraform plan -out tfplan
terraform show -json tfplan > tfplan.json
checkov -f tfplan.json --framework terraform_plan
Selecting checks and exceptions
checkov -d . --check CKV_AWS_20,CKV_AWS_57 # run only these checks
checkov -d . --skip-check CKV_AWS_18 # skip globally
The best exception lives inside the resource, with a reason, where reviewers see it:
resource "aws_s3_bucket" "site" {
# checkov:skip=CKV_AWS_18:Static website bucket, access logs are kept in CloudFront
bucket = "example-org-site"
}
A .checkov.yaml file holds shared repo settings (frameworks, skips, output), so people and CI run the same way.
flowchart LR
C["Code .tf"] --> CK["checkov -d ."]
P["Plan JSON"] --> CK2["checkov -f tfplan.json"]
CK --> R{"Failed?"}
CK2 --> R
R -- "yes" --> X["Block the PR"]
R -- "no" --> OK["Allow apply"]
Quick comparison
| Tool | Strengths |
|---|---|
| Checkov | Many frameworks, graph analysis, custom policies in Python or YAML |
| Trivy config (formerly tfsec) | One binary shared with the image scanner |
| tflint | Provider errors and style, not security-focused |
| KICS | Many platforms, queries in Rego |
Traps: using a global --skip-check for something only one resource needs; scanning only code when modules use dynamic values; letting hundreds of failed checks pile up until nobody reads the output.