What IaC scanning finds
Misconfigurations, before deploy.
You can proofread a recipe all afternoon. Every ingredient spelled right, the oven temperature a real number, nobody asking for a cup of concrete. None of that tells you whether the cake rises. Infrastructure code has the same gap. A linter (a tool that checks syntax and style) confirms your Terraform parses. A runtime scanner catches a public bucket after it already exists. IaC static analysis (reading infrastructure-as-code files without running them) sits in the middle. It reads your Terraform, Kubernetes manifests, CloudFormation templates and Dockerfiles, then flags insecure configuration before any of it reaches a cloud account. An unencrypted volume. A security group open to 0.0.0.0/0, which means the entire internet. An IAM (identity and access management) policy with Action:*, which grants every permission there is. A container running as root. Those are the mistakes that ship quietly when nobody reads the diff closely enough.
Checkov is one of those readers. It is a Python package from Prisma/Bridgecrew, it ships with hundreds of built-in policies, and you run it on your laptop or in CI (continuous integration, the automation that fires on every push). The shape of the work is identical across Checkov, tfsec, Trivy, Terrascan and KICS: read config, match rules, report violations. Learn that loop once and every scanner in the family clicks into place.
Where the scan sits in your pipeline
Shift-left is the habit of moving a check earlier, closer to the person who can fix it in a minute. For infrastructure it looks like this. You write code, you open a pull request, and a scanner grades every resource against a catalog of checks before anyone can merge. A source scan needs no cloud credentials at all. Checkov parses the files sitting on disk and compares each attribute to a rule. Cheap enough to run on every commit, fast enough that a developer gets an answer in minutes. The gate stops being "deploy and hope" and becomes "fix the misconfiguration while the change is still a diff."
$ checkov -d ./infra --framework terraform --compact --quiet
terraform scan results:Passed checks: 38, Failed checks: 4, Skipped checks: 0Check: CKV_AWS_20: "S3 Bucket has public access block"FAILED for resource: aws_s3_bucket.uploadsFile: /infra/s3.tf:3-11Guide: https://docs.prismacloud.io/...Check: CKV_AWS_79: "Ensure Instance Metadata Service V2 is required"FAILED for resource: aws_instance.appFile: /infra/ec2.tf:14-28
What a scanner is genuinely good at
Checkov is strongest on the boring, well-known failure classes, because each one maps to a field you can read straight off the page: encryption switched off, something exposed publicly, an over-broad IAM policy, missing logging, weak TLS (transport layer security, the encryption behind https) settings, containers running as root, GitHub Actions pinned to a moving tag instead of a fixed version. Most checks boil down to one question. Does this attribute equal the safe value? Graph checks, the ones with a CKV2_ prefix, ask a bigger one. Does the relationship between these resources create exposure? The built-in catalog covers AWS, Azure, GCP, Kubernetes, Docker, CloudFormation and more, thousands of rules kept current by the vendor and the community.
$ checkov -d . --check CKV_AWS_18,CKV_AWS_21,CKV_K8S_22 --compact
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"FAILED for resource: aws_s3_bucket.logsCheck: CKV_AWS_21: "Ensure all data stored in the S3 bucket have versioning enabled"FAILED for resource: aws_s3_bucket.logsCheck: CKV_K8S_22: "Use read-only root filesystem"FAILED for resource: Deployment.default.payments-api
What a scanner cannot see
A green scan proves less than it feels like it proves. A scanner cannot watch runtime behaviour, cannot resolve business logic that only settles after deploy, and cannot see risk that never appears in config at all. Your bucket can pass every encryption check while the application quietly writes secrets into a plain text file. Rules of your own making, like "only these instance types in prod", need a custom check or OPA/Conftest (Open Policy Agent and its file-checking companion) running alongside Checkov. Static scanning is one layer. Policy-as-code, secrets detection, image CVE (common vulnerabilities and exposures) scanning and runtime detection cover the rest. Checkov is a gate you need, never the whole story.
Check IDs are the shared vocabulary
Every finding arrives with a stable identifier: CKV_AWS_18, CKV2_AWS_6, and so on. Treat that string the way a mechanic treats a part number. You will type these IDs into suppressions, baselines, CI config and Slack threads, and they mean exactly the same thing in every one of those places. A CKV_ check evaluates a single resource. A CKV2_ check is a graph check that spans relationships between resources. The middle of the ID tells you the framework: CKV_AWS_*, CKV_K8S_*, CKV_DOCKER_*, CKV_GHA_*. When you triage, read the ID before the sentence beside it. The sentence is a friendly summary. The ID is the handle every other tool understands.
$ checkov -d . --framework terraform -o json | jq -r '.results.failed_checks[] | "\(.check_id) \(.resource)"' | head
CKV_AWS_18 aws_s3_bucket.logsCKV_AWS_79 aws_instance.appCKV2_AWS_6 aws_s3_bucket.uploads
Source scan vs plan scan (a preview)
Reading source is like reading a blueprint. Reading a plan is like watching the builder lay out the actual materials on site. A source scan reads the .tf and YAML files on disk: fast, no cloud credentials, ideal in the editor and on every commit. It has one blind spot. Values that only settle once terraform plan runs, such as variables, module outputs and dynamic blocks, may never appear as the insecure value the plan would really create. Plan scanning, covered in cv-plan, closes that gap by reading terraform show -json output. Teams who have been at this a while run both. Source for speed, plan for authority before apply.
$ checkov -d . --framework terraform --compact$ checkov -f tfplan.json --framework terraform_plan --compact
terraform scan results:Passed checks: 812, Failed checks: 37terraform_plan scan results:Passed checks: 156, Failed checks: 2# plan may surface failures invisible in source — both layers matter
Where this course goes
The topics ahead follow the order you meet these problems in real life: first install, then reading a failure, then repos with four frameworks jammed into them, then suppressions, custom rules, plan scanning, baselines, how the rival scanners compare, where tools overlap, CI gates, and finally running the whole thing as a programme with owners and deadlines. Every topic stands on its own, so start wherever your current problem is.
The layers either side of the scanner
A seatbelt, an airbag and good brakes do not replace each other, and nobody argues that they should. Pair Checkov with secrets detection (Gitleaks at commit time, Checkov's secrets framework as a backstop), image CVE scanning (Trivy or Grype), and runtime controls (Falco, cloud guardrails). Checkov catches misconfiguration in code. It will not rotate a leaked key or patch an operating system package inside a container image. Give each domain one owner so your gates do not end up arguing with each other.
$ checkov -d . --framework terraform --compact --quiet$ gitleaks detect --source . --redact$ trivy config . --severity HIGH,CRITICAL
checkov Failed checks: 4gitleaks: no leaks foundTrivy HIGH/CRITICAL misconfigurations: 3# three layers, three owners — not three copies of the same gate
Graph checks (CKV2_) catch the risk that lives in the wiring. A security group rule can look perfectly sensible on its own and turn dangerous the moment it is attached to an instance with a public IP address. Plan scanning and baselines are the other two things Checkov does its own way, and scanners built purely on Rego (the policy language behind OPA) tend to handle both quite differently.
These findings are boring right up until they are catastrophic. A public S3 bucket. A security group open to the world. An IAM policy with admin rights. A container running as root. Boring mistakes cause most incidents, and a scanner performs the boring review tirelessly, which frees human attention for architecture and threat modelling instead of checking whether logging is enabled on bucket number forty.
How this compares to runtime tools and policy-as-code
Runtime tools answer a different question. CloudTrail alerts, GuardDuty and Falco tell you what is happening in the account right now. Checkov tells you what you will build if this merge lands. OPA with Conftest evaluates Rego against structured input, which is powerful for policy specific to your company, though you still have to decide what input to feed it: manifests, plans, something else. Checkov turns up with hundreds of ready-made checks and a command your developers can run in thirty seconds. Most teams take Checkov (or a sibling scanner) for breadth and add OPA where Rego already powers their admission control.
Secrets scanners find credentials committed to git. Image scanners find known vulnerabilities buried in image layers. Checkov finds buckets without encryption and pods without resource limits. None of the three substitutes for the others. Reading infrastructure code before it deploys is the job Checkov was built for.
$ checkov -d . --framework terraform,kubernetes,dockerfile --compact$ checkov -d . --framework secrets --quiet
terraform: Passed 812, Failed 37kubernetes: Passed 204, Failed 11dockerfile: Passed 12, Failed 2secrets: Failed 1Check: CKV_SECRET_6 on /app/config.yml
One repository, several frameworks, one scanner across all of them. That mix is what most real codebases look like, which is why the topics ahead handle frameworks before they get clever about custom rules and gates. Start with install (cv-install) and move at whatever pace the repo you are stuck with demands.
Try this
Do this: make a throwaway folder holding a single Terraform file, or clone a small public sample, install Checkov if you have not already, and run the commands below. You are proving the loop works. Point at files, get check IDs back, confirm the resources were actually parsed. You are not proving your cloud is safe.
$ mkdir -p /tmp/cv-what && cd /tmp/cv-what$ printf 'resource "aws_s3_bucket" "demo" { bucket = "demo-example" }\n' > main.tf$ checkov -d . --framework terraform --compact --quiet$ checkov -d . -o json | jq 'if type=="array" then .[].summary else .summary end'
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"FAILED for resource: aws_s3_bucket.demo{"passed": 4,"failed": 6,"skipped": 0,"parsing_errors": 0,"resource_count": 1}# resource_count: 1 means Checkov saw the bucket — not a hollow green scan
Takeaway
Remember: the trade-off in IaC static scanning is speed against certainty. Source scans cost nothing and need no credentials, so you can run them on every single commit, but they cannot see values that only appear after terraform plan and they cannot prove anything about runtime behaviour. Checkov is the smoke detector you fit while reading the blueprint, not the fire brigade for the building you already live in. Give secrets detection, image CVE scanning and cloud runtime controls their own layer so each one owns exactly one question.
Next up: install Checkov on your own laptop (cv-install) and read real failure lines until the check ID, the resource address and the Guide link stop looking like noise. Once that vocabulary sticks, triage (cv-results) and CI gates read like a worklist instead of alien output.