Comprehensive toolkit for validating, linting, testing, and automating Terraform configurations and HCL files. Use this skill when working with Terraform files (.tf, .tfvars), validating infrastructure-as-code, debugging Terraform configurations, performing dry-run testing with terraform plan, or working with custom providers and modules.
Overall
score
100%
Does it follow best practices?
Validation for skill structure
Comprehensive security validation checklist for Terraform configurations. Use this reference when performing security reviews or auditing infrastructure-as-code.
When reporting Checkov findings, use this table to find the relevant section in this document:
| Checkov Check | Section | Description |
|---|---|---|
CKV_AWS_24 | "Overly Permissive Security Groups" | SSH open to 0.0.0.0/0 |
CKV_AWS_260 | "Overly Permissive Security Groups" | HTTP open to 0.0.0.0/0 |
CKV_AWS_16 | "Encryption at Rest" | RDS storage encryption |
CKV_AWS_17 | "RDS Databases" | RDS publicly accessible |
CKV_AWS_130 | "Network Security" | Public subnet exposure |
CKV_AWS_53-56 | "Public S3 Buckets" | S3 public access blocks |
CKV_AWS_* (IAM) | "IAM Security" | IAM policy issues |
CKV_AWS_79 | "EC2/EKS Security" | IMDSv1 usage |
| Hardcoded passwords | "Hardcoded Credentials" | Secrets in code |
| Sensitive outputs | "Sensitive Output Exposure" | Exposed sensitive data |
Risk: Secrets committed to version control can be exposed.
Detection:
# Search for common secret patterns
grep -rE "(password|secret|api_key|access_key)\s*=\s*\"[^$]" *.tf
grep -rE "private_key\s*=\s*\"" *.tf
grep -rE "token\s*=\s*\"[^$]" *.tfRemediation:
sensitive = true.tfvars files with secretsExample - Insecure:
resource "aws_db_instance" "example" {
username = "admin"
password = "hardcoded_password123" # SECURITY ISSUE
}Example - Secure:
variable "db_password" {
type = string
sensitive = true
}
resource "aws_db_instance" "example" {
username = "admin"
password = var.db_password
}Risk: Sensitive data exposed in terraform state or plan output.
Detection:
Remediation:
output "db_password" {
value = aws_db_instance.example.password
sensitive = true # Prevents display in console
}Risk: Unrestricted access to resources from the internet.
Detection Patterns:
# SECURITY ISSUE: SSH open to world
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# SECURITY ISSUE: All ports open
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}Best Practices:
Example - Secure:
variable "admin_cidr" {
description = "CIDR block for admin access"
type = string
}
resource "aws_security_group" "app" {
ingress {
description = "SSH from admin network only"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.admin_cidr]
}
}Risk: Data exposure through public S3 access.
Detection:
# SECURITY ISSUE: Public bucket
resource "aws_s3_bucket_public_access_block" "example" {
bucket = aws_s3_bucket.example.id
block_public_acls = false # Should be true
block_public_policy = false # Should be true
ignore_public_acls = false # Should be true
restrict_public_buckets = false # Should be true
}Best Practices:
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
}Resources to Check:
Example - RDS Encryption:
resource "aws_db_instance" "example" {
storage_encrypted = true # Required
kms_key_id = aws_kms_key.db.arn # Use customer-managed keys
}Example - S3 Encryption:
resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
bucket = aws_s3_bucket.example.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.arn
}
}
}Risk: Data intercepted during transmission.
Best Practices:
Example - ALB HTTPS:
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.example.arn
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01"
certificate_arn = aws_acm_certificate.cert.arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.example.arn
}
}
# Redirect HTTP to HTTPS
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.example.arn
port = "80"
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}Risk: Privilege escalation and unauthorized access.
Detection Patterns:
# SECURITY ISSUE: Admin access
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
# SECURITY ISSUE: Too broad
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}Best Practices:
Example - Least Privilege:
data "aws_iam_policy_document" "s3_read_only" {
statement {
effect = "Allow"
actions = [
"s3:GetObject",
"s3:ListBucket"
]
resources = [
aws_s3_bucket.app_data.arn,
"${aws_s3_bucket.app_data.arn}/*"
]
}
}Best Practice:
data "aws_iam_policy_document" "require_mfa" {
statement {
effect = "Deny"
actions = ["*"]
resources = ["*"]
condition {
test = "BoolIfExists"
variable = "aws:MultiFactorAuthPresent"
values = ["false"]
}
}
}Risk: Unauthorized access from other AWS accounts.
Best Practices:
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
principals {
type = "AWS"
identifiers = ["arn:aws:iam::123456789012:root"]
}
actions = ["sts:AssumeRole"]
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = [var.external_id]
}
}
}Risk: No audit trail for API calls.
Best Practice:
resource "aws_cloudtrail" "main" {
name = "main-trail"
s3_bucket_name = aws_s3_bucket.cloudtrail.id
include_global_service_events = true
is_multi_region_trail = true
enable_logging = true
event_selector {
read_write_type = "All"
include_management_events = true
}
}Best Practice:
resource "aws_flow_log" "vpc" {
vpc_id = aws_vpc.main.id
traffic_type = "ALL"
iam_role_arn = aws_iam_role.flow_logs.arn
log_destination = aws_cloudwatch_log_group.flow_logs.arn
}Best Practice:
resource "aws_cloudwatch_log_group" "app" {
name = "/aws/app/logs"
retention_in_days = 90
kms_key_id = aws_kms_key.logs.arn # Encrypt logs
}storage_encrypted = truepublicly_accessible = falseat_rest_encryption_enabled = truetransit_encryption_enabled = trueRisk: State files contain sensitive data in plaintext.
Best Practices:
Terraform 1.11+ (S3 Native Locking - Recommended):
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true # Required
kms_key_id = "arn:aws:kms:..."
use_lockfile = true # S3 native locking (1.11+)
}
}Note: Terraform 1.11 introduced S3 native state locking via the
use_lockfileargument. This uses S3's conditional writes to implement locking without requiring DynamoDB. The DynamoDB-based locking (dynamodb_table) is now deprecated but still supported for backward compatibility.
Legacy (Terraform < 1.11 or backward compatibility):
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true # Required
kms_key_id = "arn:aws:kms:..."
dynamodb_table = "terraform-locks" # State locking (deprecated in 1.11+)
}
}Checklist:
use_lockfile = true for 1.11+ or DynamoDB for older versions)Best Practice:
locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
Owner = var.owner
CostCenter = var.cost_center
Compliance = "HIPAA" # If applicable
}
}
resource "aws_instance" "example" {
# ... other config ...
tags = merge(local.common_tags, {
Name = "app-server"
})
}Risk: Unexpected behavior from provider updates.
Best Practice:
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Pin major version
}
}
}Risk: Malicious code from untrusted modules.
Best Practices:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2" # Pin specific version
}Tools to integrate:
Note: Terrascan was archived by Tenable on November 20, 2025 and is no longer maintained. Use Checkov or Trivy instead for OPA/Rego-style policy enforcement.
Trivy is Aqua Security's unified scanner that absorbed tfsec. It scans Terraform, CloudFormation, Kubernetes, Helm, and more.
Version Note:
Warning: Trivy v0.60.0 has known regression issues that can cause panics when scanning Terraform configurations. If you experience crashes or unexpected behavior, downgrade to v0.59.x until v0.61.0+ is released with fixes.
To install a specific version:
# macOS brew install trivy@0.59.1 # Linux - specify version in install script curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.59.1
Installation:
# macOS
brew install trivy
# Linux
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# Docker
docker pull aquasec/trivyUsage:
# Scan Terraform directory
trivy config ./terraform
# Scan with specific severity
trivy config --severity HIGH,CRITICAL ./terraform
# Scan with JSON output
trivy config -f json -o results.json ./terraform
# Scan specific file
trivy config main.tf
# Skip specific checks
trivy config --skip-dirs .terraform ./terraform
# Scan Terraform plan JSON (more accurate)
terraform show -json tfplan > tfplan.json
trivy config tfplan.json
# Use tfvars files for accurate variable resolution
trivy config --tf-vars prod.terraform.tfvars ./terraform
# Exclude downloaded modules from scanning
trivy config --tf-exclude-downloaded-modules ./terraformCommon Trivy Checks for Terraform:
AVD-AWS-0086 - S3 bucket encryptionAVD-AWS-0089 - S3 bucket versioningAVD-AWS-0132 - Security group unrestricted ingressAVD-AWS-0107 - RDS encryption at restAVD-AWS-0078 - EBS encryptionOutput Formats:
table - Human-readable table (default)json - JSON format for CI/CD integrationsarif - SARIF format for IDE integrationtemplate - Custom template outputIgnore Findings:
# trivy:ignore:AVD-AWS-0086
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
}Advanced Trivy Configuration (trivy.yaml):
# trivy.yaml
exit-code: 1
severity:
- HIGH
- CRITICAL
scan:
scanners:
- vuln
- secret
- misconfig
misconfiguration:
terraform:
tfvars-files:
- prod.tfvarsCheckov 3.0 introduces major improvements for Terraform scanning with enhanced graph policies and deeper analysis.
Key 3.0 Features:
Deep Analysis Mode: Fully resolve for_each, dynamic blocks, and complex configurations:
# Enable deep analysis with plan file
checkov -f tfplan.json --deep-analysis --repo-root-for-plan-enrichment .Baseline Feature: Track only new misconfigurations (ignore existing):
# Create baseline from current state
checkov -d . --create-baseline
# Run subsequent scans against baseline
checkov -d . --baseline .checkov.baselineEnhanced Policy Language: 36 new operators including:
SUBSET - Check if values are subset of allowed valuesjsonpath_* operators - Deep JSON path queriesImproved Dynamic Block Support:
# Scan with full dynamic block resolution
checkov -d . --download-external-modules trueCheckov 3.0 Commands:
# Basic scan
checkov -d .
# Deep analysis with Terraform plan
terraform plan -out=tf.plan
terraform show -json tf.plan > tfplan.json
checkov -f tfplan.json --deep-analysis
# Create and use baseline
checkov -d . --create-baseline
checkov -d . --baseline .checkov.baseline
# Compact output (failures only)
checkov -d . --compact
# Skip specific checks
checkov -d . --skip-check CKV_AWS_20,CKV_AWS_21
# Run only specific frameworks
checkov -d . --framework terraform| Tool | Focus | Policy Language | Built-in Policies | Best For |
|---|---|---|---|---|
| trivy | Security | Rego | 1000+ | All-in-one scanning, container + IaC |
| checkov | Security/Compliance | Python/YAML | 3000+ | Multi-framework, compliance, deep analysis |
Note: tfsec has been deprecated and merged into Trivy. Terrascan was archived in November 2025. New users should use Trivy or Checkov.
# Check for hardcoded secrets
grep -r "password\s*=\s*\"" . --include="*.tf"
grep -r "secret\s*=\s*\"" . --include="*.tf"
# Find public security groups
grep -r "0.0.0.0/0" . --include="*.tf"
# Find unencrypted resources
grep -r "encrypted\s*=\s*false" . --include="*.tf"
# Check for missing backup configurations
grep -r "backup_retention_period\s*=\s*0" . --include="*.tf"Install with Tessl CLI
npx tessl i pantheon-ai/terraform-validator