
Checkov is an open-source static code analysis tool for infrastructure-as-code (IaC) security. Bridgecrew built it, and Palo Alto Networks later acquired Bridgecrew. It scans cloud infrastructure configurations written in Terraform, CloudFormation, Kubernetes, Dockerfile, and ARM templates, then flags misconfigurations and security vulnerabilities before deployment. The checks run against predefined policies that catch the usual problems: unencrypted storage, too many permissions, open security groups, publicly exposed resources. Since it plugs into CI/CD pipelines and tells you how to fix what it finds, teams use it to move security earlier in development instead of auditing after the fact.
This post focuses on the Terraform and OpenTofu side of Checkov. Scalr has a native integration with Checkov, which we cover near the bottom.
Most infrastructure-as-code security incidents don't start with a clever attack. They start with a misconfigured resource: an unencrypted database, an IAM role that grants too much, a storage bucket left open. As more teams adopt IaC, those gaps multiply. Checkov scans your configuration files early in development and catches the problems before they reach production, where a single misconfiguration can turn into a data breach or a compliance violation. Fixing an issue in a pull request is also far cheaper than fixing it after it ships. Running the same checks on every change keeps your infrastructure aligned with standards like CIS, HIPAA, and SOC2 over time.
First, install Checkov using pip or brew. The full instructions are here.
To understand the basics of Checkov, create a small Terraform module with a few deliberate security holes in it. Save the following as main.tf:
provider "aws" {
region = "us-west-2"
}
resource "aws_s3_bucket" "example" {
bucket = "my-example-bucket"
}
resource "aws_s3_bucket_public_access_block" "example" {
bucket = aws_s3_bucket.example.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}This configuration creates an AWS S3 bucket but deliberately leaves every public-access control disabled. Now run Checkov against it:
checkov -f main.tfThe output looks something like this:
terraform scan results:
Passed checks: 2, Failed checks: 4, Skipped checks: 0
Check: CKV_AWS_41: "Ensure no hard coded AWS access key and secret key exists in provider"
PASSED for resource: aws.default
File: /main.tf:1-3
Check: CKV_AWS_93: "Ensure S3 bucket policy does not lockout all but root user"
PASSED for resource: aws_s3_bucket.example
File: /main.tf:5-7
Check: CKV_AWS_53: "Ensure S3 bucket has block public ACLs enabled"
FAILED for resource: aws_s3_bucket_public_access_block.example
File: /main.tf:9-16
Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-19
Check: CKV_AWS_54: "Ensure S3 bucket has block public policy enabled"
FAILED for resource: aws_s3_bucket_public_access_block.example
File: /main.tf:9-16
Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-20
Check: CKV_AWS_55: "Ensure S3 bucket has ignore public ACLs enabled"
FAILED for resource: aws_s3_bucket_public_access_block.example
File: /main.tf:9-16
Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-21
Check: CKV_AWS_56: "Ensure S3 bucket has 'restrict_public_buckets' enabled"
FAILED for resource: aws_s3_bucket_public_access_block.example
File: /main.tf:9-16
Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-22Every failed check points back to the same block:
resource "aws_s3_bucket_public_access_block" "example" {
bucket = aws_s3_bucket.example.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}Checkov has identified several security issues with the Terraform code: public access isn't blocked, public ACLs aren't blocked, and public policies aren't blocked. To fix these issues, update the Terraform file to include proper security configurations:
provider "aws" {
region = "us-west-2"
}
resource "aws_s3_bucket" "example" {
bucket = "my-example-bucket"
}
resource "aws_s3_bucket_versioning" "example" {
bucket = aws_s3_bucket.example.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_logging" "example" {
bucket = aws_s3_bucket.example.id
target_bucket = aws_s3_bucket.example.id
target_prefix = "log/"
}
resource "aws_s3_bucket_public_access_block" "example" {
bucket = aws_s3_bucket.example.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}Run Checkov against main.tf again to verify the updates worked.
Here are some advanced Checkov commands for finer control over what you scan and how Checkov reports it, for both Terraform and OpenTofu.
# Skip specific checks
checkov -f main.tf --skip-check CKV_AWS_18,CKV_AWS_21
# Check only specific checks
checkov -f main.tf --check CKV_AWS_53,CKV_AWS_54
# Specify a custom policy directory
checkov -f main.tf --external-checks-dir /path/to/custom/checks# Create and use custom policies
checkov -f main.tf --external-checks-git https://github.com/your-org/custom-policies.git
# Use a specific branch for custom policies
checkov -f main.tf --external-checks-git https://github.com/your-org/custom-policies.git --branch dev# Generate JSON output
checkov -f main.tf --output json
# Generate JUnit XML for CI integration
checkov -f main.tf --output junitxml
# Save results to a file
checkov -f main.tf --output json > results.json
# Generate SARIF format (for GitHub code scanning)
checkov -f main.tf --output sarif# Soft-fail (return code 0 even with failures)
checkov -f main.tf --soft-fail# Scan a Terraform plan file
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
checkov -f tfplan.json
# Scan for secrets in your code
checkov -f main.tf --framework secrets
# Scan with multiple frameworks at once
checkov -d . --framework terraform,secrets,dockerfile
# Scan specific variables
checkov -f main.tf --var-file prod.tfvars# Integrate with the Bridgecrew / Prisma Cloud platform
checkov -f main.tf --bc-api-key <your_api_key>Checkov supports custom policies in YAML format through its policy-as-code (PaC) framework, using a simplified YAML syntax. Custom policies require a metadata section that identifies the general information about the policy, and a definition section that defines the actual rules. Inside definition, cond_type tells Checkov what kind of check this is (attribute, connection, or a logical operator like and/or/not), and resource_types, attribute, and operator sit alongside it, not nested underneath it.
AWS provider v4 split S3 server-side encryption out of the aws_s3_bucket resource and into its own aws_s3_bucket_server_side_encryption_configuration resource, the same split this post already used above for versioning and logging. A custom policy needs to target that resource directly, not an attribute on aws_s3_bucket itself. Here's a policy that checks a default encryption algorithm is set:
metadata:
id: "CUSTOM_AWS_001"
name: "Ensure S3 bucket server-side encryption specifies a default algorithm"
category: "encryption"
severity: "MEDIUM"
definition:
cond_type: "attribute"
resource_types:
- "aws_s3_bucket_server_side_encryption_configuration"
attribute: "rule.apply_server_side_encryption_by_default.sse_algorithm"
operator: "exists"Save this as s3_encryption_policy.yaml in a directory structure like:
custom_policies/
└── yaml/
└── s3_encryption_policy.yamlThen run Checkov against your custom policy:
checkov -f main.tf --external-checks-dir custom_policiesCheckov isn't the only static scanner in the IaC security space. tfsec, Terrascan, Trivy, and Snyk all catch overlapping sets of misconfigurations, so which one fits depends on how much you value multi-framework compliance coverage versus Terraform-native speed.
| Feature | Checkov | tfsec | Terrascan | Snyk |
|---|---|---|---|---|
| Primary focus | Multi-framework IaC security | Terraform-specific security | Multi-framework IaC & compliance | Code, dependency, and IaC security |
| Backing vendor | Palo Alto Networks | Aqua Security | Tenable | Snyk |
| Built-in checks written in | Python | Go | Rego | Proprietary rule engine |
| Custom policy language(s) | Python or YAML | Rego (or JSON/YAML for simple checks) | Rego | Rego (via OPA) |
| Scans Terraform plan output? | Yes | Yes | Yes | Yes |
Checkov's edge is its graph-based analysis, which follows resource references across a module instead of checking each block in isolation, plus one of the largest built-in policy libraries of the group.
One caveat on tfsec: Aqua Security announced in February 2023 that it's consolidating tfsec's engineering effort into Trivy, and tfsec shipped its final standalone release in May 2025. It still runs and still works, but if you're picking a scanner today, Trivy is the actively developed option going forward.
For a broader rundown of how these tools stack up, see our guide to Terraform vulnerability scanning, and for a deeper look at Snyk specifically, see using Snyk with Terraform.
Scalr has a native integration with Checkov that automatically scans the Terraform or OpenTofu code before the Terraform plan runs. To use Checkov in Scalr, enable the integration, pick the version, and pass any custom parameters you need.

Enabling the Checkov integration in Scalr, with version and parameter settings
Once enabled, Checkov can be enforced on all Scalr environments or on select environments. Once enforced on an environment, every run within that environment is forced to go through a Checkov scan before the Terraform plan executes:

Checkov scan enforced on a Scalr environment, running before the Terraform plan executes
If the Checkov scan is in hard-failure mode, the Terraform run automatically stops before the plan can execute.
Optionally, custom parameters can be passed to change Checkov's behavior in Scalr. For example, the --soft-fail parameter returns a code of 0, which allows the run to continue. Other options like --skip-check or --external-checks can also be passed as custom parameters. See all available parameters here.
Scalr supports importing and enforcing custom Checkov policies from external repositories. Users specify a VCS provider, repository, branch, and folder containing their custom Checkov checks, which are evaluated as part of policy enforcement in designated environments.

Scalr's custom Checkov policy configuration, with VCS repository settings
Anyone who has used Checkov knows that jumping from one version to another can take planning. Scalr lets you run multiple versions of Checkov on a single environment so you can stage the upgrade. Keep your current version on its strict setting and add the new version in soft-fail mode, so anything the newer version flags won't block runs yet. That gives your team time to review whatever the new version finds in their Terraform code and fix it before the upgrade is enforced.
Checkov catches Terraform misconfigurations automatically before they reach production, when a fix is a quick code change instead of a production incident. Running it on every change also keeps your configurations consistent with frameworks like CIS, HIPAA, and SOC2.
If you want the quickest path, the Scalr integration runs Checkov during development without any CI/CD wiring on your side.
This blog has been verified for Terraform and OpenTofu.
