· PathShield Team · Tutorials  · 5 min read

How to Scan Terraform Code for Security Issues (Tools & Techniques)

Discover the best Terraform security scanning tools and learn how to integrate them into your CI/CD pipeline to catch misconfigurations before deployment.

Discover the best Terraform security scanning tools and learn how to integrate them into your CI/CD pipeline to catch misconfigurations before deployment.

How to Scan Terraform Code for Security Issues (Tools & Techniques)

Infrastructure as Code (IaC) has revolutionized cloud deployments, but with great power comes great responsibility. A single misconfiguration in your Terraform code can expose your entire infrastructure to security risks. This guide explores the best tools and techniques for scanning Terraform code to catch security issues before they reach production.

Why Terraform Security Scanning Matters

Terraform misconfigurations account for a significant portion of cloud security incidents. Common issues include:

  • Open security groups allowing unrestricted access
  • Unencrypted storage buckets and databases
  • Overly permissive IAM policies
  • Missing logging and monitoring configurations
  • Exposed secrets and hardcoded credentials

The good news? Most of these issues can be caught automatically before deployment.

Top Terraform Security Scanning Tools

1. Checkov - The Comprehensive Scanner

Checkov is an open-source static analysis tool that scans Terraform (and other IaC) for security and compliance misconfigurations.

Installation:

pip install checkov

Basic Usage:

checkov -d . --framework terraform

Key Features:

  • 1000+ built-in policies
  • Custom policy support
  • Multiple output formats
  • IDE integrations

Example Output:

Check: CKV_AWS_23: "Ensure every security group rule has a description"
	FAILED for resource: aws_security_group.web
	File: /main.tf:15-25
	Guide: https://docs.bridgecrew.io/docs/networking_31

2. tfsec - Purpose-Built for Terraform

tfsec focuses specifically on Terraform security scanning with deep AWS, Azure, and GCP knowledge.

Installation:

brew install tfsec
# or
go install github.com/aquasecurity/tfsec/cmd/tfsec@latest

CI/CD Integration Example:

name: tfsec
on: [push, pull_request]
jobs:
  tfsec:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: aquasecurity/tfsec-action@v1.0.0
        with:
          format: sarif
          out: tfsec.sarif
      - uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: tfsec.sarif

Custom Rules Example:

# .tfsec/custom_rule.yaml
rules:
  - code: CUSTOM-001
    description: S3 buckets must have specific tags
    severity: HIGH
    requiredTypes:
      - resource
    requiredLabels:
      - aws_s3_bucket
    matchSpec:
      name: tags
      action: contains
      value: Environment

3. Terrascan - Policy as Code Engine

Terrascan uses Open Policy Agent (OPA) for highly customizable security policies.

Installation:

curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Darwin_x86_64.tar.gz")" > terrascan.tar.gz
tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz
sudo mv terrascan /usr/local/bin

Scanning with Policy Filters:

terrascan scan -t aws -i terraform --severity high,critical

4. Snyk Infrastructure as Code

Snyk IaC combines security scanning with remediation guidance and developer-friendly integrations.

CLI Usage:

snyk iac test main.tf

Fixing Issues:

# Get remediation advice
snyk iac test main.tf --json | jq '.infrastructureAsCodeIssues[0].remediation'

5. KICS (Keeping Infrastructure as Code Secure)

KICS supports multiple IaC languages and provides extensive query libraries.

Docker Usage:

docker run -v "$(pwd)":/path checkmarx/kics:latest scan -p /path -o /path/results

Integrating Security Scans into Your Workflow

Pre-Commit Hooks

Catch issues before they’re even committed:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.77.0
    hooks:
      - id: terraform_tfsec
      - id: terraform_checkov
        args:
          - --args=--quiet --compact --framework terraform

CI/CD Pipeline Integration

Example GitLab CI:

terraform-security:
  stage: test
  image: python:3.9
  script:
    - pip install checkov
    - checkov -d . --output-format json --output-file checkov-report.json
  artifacts:
    reports:
      junit: checkov-report.json
  only:
    changes:
      - "**/*.tf"

VS Code Integration

Install extensions for real-time feedback:

  • Checkov extension
  • tfsec extension
  • Terraform extension with security linting

Common Security Issues and How to Fix Them

1. Unencrypted S3 Buckets

Vulnerable Code:

resource "aws_s3_bucket" "data" {
  bucket = "my-data-bucket"
}

Secure Version:

resource "aws_s3_bucket" "data" {
  bucket = "my-data-bucket"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
  bucket = aws_s3_bucket.data.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "data" {
  bucket = aws_s3_bucket.data.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

2. Open Security Groups

Vulnerable Code:

resource "aws_security_group" "web" {
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Secure Version:

resource "aws_security_group" "web" {
  ingress {
    description = "SSH from corporate VPN"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.corporate_vpn_cidr]
  }
}

3. Hardcoded Secrets

Vulnerable Code:

resource "aws_db_instance" "database" {
  password = "SuperSecret123!"  # Never do this!
}

Secure Version:

resource "random_password" "db_password" {
  length  = 32
  special = true
}

resource "aws_secretsmanager_secret" "db_password" {
  name = "rds-password"
}

resource "aws_secretsmanager_secret_version" "db_password" {
  secret_id     = aws_secretsmanager_secret.db_password.id
  secret_string = random_password.db_password.result
}

resource "aws_db_instance" "database" {
  password = random_password.db_password.result
}

Building a Security-First Terraform Culture

1. Establish Baseline Policies

Create a minimum security standard for your organization:

# modules/security-baseline/main.tf
resource "aws_ebs_encryption_by_default" "main" {
  enabled = true
}

resource "aws_s3_account_public_access_block" "main" {
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

2. Use Terraform Modules for Security

Create reusable, pre-secured modules:

module "secure_vpc" {
  source = "./modules/secure-vpc"
  
  enable_flow_logs = true
  enable_guardduty = true
  enable_vpc_endpoints = true
}

3. Regular Security Reviews

Schedule monthly reviews of:

  • Failed security scans
  • Policy violations
  • New security rules needed
  • Module updates

Advanced Scanning Techniques

Custom Policy Development

Create organization-specific rules:

# custom_checkov_check.py
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
from checkov.common.models.enums import CheckResult, CheckCategories

class S3BucketMustHaveLifecycle(BaseResourceCheck):
    def __init__(self):
        name = "Ensure S3 buckets have lifecycle policies"
        id = "CUSTOM_AWS_1"
        supported_resources = ['aws_s3_bucket']
        categories = [CheckCategories.GENERAL_SECURITY]
        super().__init__(name=name, id=id, categories=categories, 
                         supported_resources=supported_resources)

    def scan_resource_conf(self, conf):
        if 'lifecycle_rule' in conf:
            return CheckResult.PASSED
        return CheckResult.FAILED

check = S3BucketMustHaveLifecycle()

Combining Multiple Scanners

Use a meta-scanner approach:

#!/bin/bash
# security-scan.sh

echo "Running Terraform security scans..."

# Run multiple scanners
tfsec . --format json > tfsec-results.json
checkov -d . --output json > checkov-results.json
terrascan scan -i terraform --format json > terrascan-results.json

# Aggregate results
python aggregate_results.py

# Fail if any critical issues
if [ -f "critical-issues.txt" ]; then
    echo "Critical security issues found!"
    exit 1
fi

Measuring and Improving Security Posture

Key Metrics to Track

  • Number of security issues per deployment
  • Time to remediation
  • Policy compliance percentage
  • False positive rate

Continuous Improvement Process

  1. Baseline: Establish current security posture
  2. Target: Set improvement goals
  3. Implement: Add scanning tools and policies
  4. Measure: Track metrics over time
  5. Iterate: Refine rules and processes

Conclusion

Terraform security scanning is not optional—it’s essential for maintaining secure cloud infrastructure. Start with one tool, integrate it into your workflow, and gradually expand your security coverage. Remember: the best security issue is the one that never makes it to production.

Action Items:

  • Install at least one scanning tool today
  • Run it against your existing Terraform code
  • Fix the top 5 critical issues found
  • Set up pre-commit hooks or CI/CD integration

By making security scanning a natural part of your Terraform workflow, you’ll sleep better knowing your infrastructure is secure by design.

Back to Blog

Related Posts

View All Posts »