Terraform Security Scanning in Practice: tfsec Configuration Guide
Terraform IaC security scanning in practice: install and run tfsec and Checkov, risky config samples, inline exemptions, GitHub Actions pipeline gates that block public S3 and wide-open security groups.
Scope
Terraform IaC security scanning fits these cases:
- Auto-detect high-risk config before merge: public S3 buckets, security groups allowing 0.0.0.0/0, unencrypted disks;
- CI/CD quality gates that block non-compliant Terraform plans from production;
- Full baseline audit when inheriting existing infrastructure code;
- Audit trail for MLPS, ISO 27001, and similar cloud-config compliance.
Prerequisites
- Terraform 1.5+ (`terraform -version` to verify);
- tfsec and/or Checkov: tfsec via Docker/binary; Checkov needs Python 3.8+;
- A test module with risky config (below);
- CI environment (GitHub Actions in this article).
How it works
Both tools are static analysis: they never call cloud APIs. They parse .tf files, build a resource graph, and check it against built-in policy packs (CIS Benchmark, AWS/Azure/GCP best practices). tfsec (now merged into Aqua Trivy; the command remains) is Terraform-focused, fast, and readable. Checkov covers more: Terraform, CloudFormation, Kubernetes, Dockerfile, plus custom policies in Python/YAML. Both exit non-zero on high-risk findings—that is the CI gate signal.
Steps
1. Sample code with risks
resource "aws_s3_bucket" "data" {
bucket = "demo-data"
}
resource "aws_s3_bucket_public_access_block" "data" {
bucket = aws_s3_bucket.data.id
block_public_acls = false
block_public_policy = false
}
resource "aws_security_group_rule" "open_ssh" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = "sg-0123456789abcdef0"
}
resource "aws_ebs_volume" "app" {
availability_zone = "ap-northeast-1a"
size = 100
}
Three typical issues: S3 allows public access, SSH open to the world, EBS volume unencrypted.
2. Install and run tfsec
# macOS
brew install tfsec
# Linux one-click install
curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash
# Run via Docker
docker run --rm -v "$PWD:/scan" aquasec/tfsec /scan
tfsec .
tfsec prints severity, rule IDs (e.g. aws-s3-enable-bucket-encryption), and fix links.
3. Install and run Checkov
pip install checkov
checkov -d .
# Output failures only, reduce log noise
checkov -d . --compact
Checkov prints CKV_AWS_* results. Use `-c CKV_AWS_20,CKV_AWS_23` to run selected rules only.
4. False positives: inline exemptions
resource "aws_ebs_volume" "app" {
availability_zone = "ap-northeast-1a"
size = 100
# checkov:skip=CKV_AWS_3:temporary volume does not store sensitive data, quarterly review
# tfsec:ignore:aws-ebs-encrypt-encrypted-volume
}
Exemption comments must state a reason for later audit. Do not let exemptions live forever.
5. GitHub Actions integration
name: iac-security
on:
pull_request:
paths: ["**/*.tf"]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: tfsec
uses: aquasecurity/tfsec-pr-commenter-action@v1.3.1
with:
github_token: ${{ github.token }}
- name: Checkov
uses: bridgecrewio/checkov-action@v12
with:
directory: .
framework: terraform
output_format: cli
Verify
Fix the sample as recommended (enable S3 block_public_acls, tighten security group CIDR, set encrypted = true on EBS) and rerun:
tfsec . && echo "tfsec PASS"
checkov -d . ; echo "checkov exit=$?"
Exit code 0 and 0 failures on both means the gate passes. Make this a required PR check so failures cannot merge.
FAQ
FAQ 1: Too many false positives; the team ignores alerts?
Severity tiers: CRITICAL/HIGH block merge; MEDIUM and below warn. Confirmed false positives get inline exemptions with an expiry date. Review the exemption list monthly; drop stale ones.
FAQ 2: tfsec or Checkov?
Pure Terraform, speed, simple CI: tfsec. Mixed IaC (CloudFormation, Kubernetes, Helm) or custom Python policies / Bridgecrew: Checkov. Running both is cheap and covers more rules.
Summary
Terraform IaC scanning stops infrastructure risk before the plan runs—cheaper than after-the-fact fixes. Three steps: baseline existing code with tfsec/Checkov, embed scans in the PR pipeline with severity gates, control noise with commented inline exemptions. Keep policy pack versions and the exemption list current, or IaC security becomes theater.