
This post is part of a series on IaC Security: Securing Your Terraform and OpenTofu Infrastructure.
Terraform vulnerability scanning means automatically analyzing your infrastructure code to find and fix security issues before they ever reach a production environment. If your team cares about cloud security, it's one of the first things to set up, and it works the same way for Terraform and OpenTofu. This guide walks through the most common vulnerabilities, the tools that find them, and how to wire them into your workflow.

The most common risks in Terraform aren't clever hacks. They're misconfigurations you could have prevented, and these small mistakes are what cause big breaches. Here are some of the issues scanners catch most often.
One of the most common mistakes is creating a firewall rule or security group that exposes a sensitive port, like SSH (22), to the entire internet.
Insecure HCL Example:
# This security group allows SSH access from ANY IP address.
resource "aws_security_group" "allow_ssh" {
name = "allow-all-ssh"
description = "Allow SSH inbound traffic"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # DANGEROUS
}
}A scanner will immediately flag 0.0.0.0/0 as a high-severity issue, pushing you to restrict the IP range to a known, trusted source.
Putting secrets like API keys or passwords straight into your code is a serious mistake. Once that code is committed to version control, the secret is stuck in the repository's history for good.
Insecure HCL Example:
# A hardcoded password in a variable default.
variable "db_password" {
description = "Database admin password"
type = string
default = "SuperSecretPassword123!" # DANGEROUS
}
resource "aws_db_instance" "default" {
# ... other configuration
password = var.db_password
}Scanners are good at spotting high-entropy strings and common key formats and stopping them before they're saved. The right approach is to use a dedicated secrets manager like AWS Secrets Manager or HashiCorp Vault.
Failing to enable encryption for resources that store data, like S3 buckets or databases, can leave sensitive information vulnerable.
Insecure HCL Example:
# This S3 bucket is created without server-side encryption enabled.
resource "aws_s3_bucket" "unencrypted_data" {
bucket = "my-company-sensitive-data-bucket"
acl = "private"
# Missing encryption configuration
}A security scanner will detect the absence of an encryption block and guide you to add the necessary configuration.
There's a solid set of open-source tools that catch these and hundreds of other issues. Most are backed by commercial security companies and sit at the "core" of their larger platforms.
Here's how the leading tools compare:
| Feature | tfsec | Checkov | Terrascan | Trivy |
|---|---|---|---|---|
| Primary Focus | Terraform-Specific Security | Multi-Framework IaC Security | Multi-Framework IaC & Compliance | Unified Scanning (Containers, IaC, Secrets) |
| Backing Vendor | Aqua Security | Palo Alto Networks | Tenable | Aqua Security |
| Key Differentiator | Speed, simplicity, Terraform-native focus | Graph-based analysis for contextual awareness | Native OPA/Rego engine, drift detection | Single CLI for multiple security scanning tasks |
| Policy Language(s) | Rego, JSON, YAML | Python, YAML | Rego | Rego |
| Scans Plan File? | Yes | Yes | Yes | Yes |
Scanning only works if it's automated. The best place to run it is your Continuous Integration/Continuous Deployment (CI/CD) pipeline, where it becomes a security gate that checks every code change.
Here's a real example using Checkov in a GitHub Actions workflow. It runs on every pull request, scans the code, and uploads the results so they show up right on the pull request.
name: Terraform IaC Security Scan
# This workflow runs on every pull request that targets the main branch
on:
pull_request:
branches: [ main ]
jobs:
checkov-scan:
runs-on: ubuntu-latest
steps:
# Step 1: Check out the repository's code
- name: Checkout code
uses: actions/checkout@v3
# Step 2: Set up Python, which Checkov needs to run
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
# Step 3: Install the Checkov scanner
- name: Install Checkov
run: pip install checkov
# Step 4: Run the scan on the current directory (-d .)
# The job will fail if Checkov finds any issues.
# It outputs results in the SARIF format for GitHub to display.
- name: Run Checkov scan
run: checkov -d . --output sarif --output-file-path results.sarif
# Step 5: Upload the SARIF file to GitHub Advanced Security
# This step runs even if the scan fails, ensuring results are always visible.
- name: Upload SARIF file
if: always()
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarifSave this as .github/workflows/security_scan.yml.
Once this workflow is in place, anyone trying to merge code with a security misconfiguration gets blocked, and they see exactly what's wrong right there in the pull request.
By integrating automated scanners into your CI/CD pipeline, you can catch and fix security misconfigurations before they reach production, so risky changes get blocked at merge time instead of surfacing in a live environment.
If you want to deepen your understanding of security tooling for Terraform, these go well with this guide:
1. https://www.redhat.com/en/topics/devops/what-is-devsecops
2. https://www.paloaltonetworks.com/cyberpedia/what-is-cloud-security-posture-management
3. https://developer.hashicorp.com/terraform/language/manage-sensitive-data
