Skip to content

TerraGuard

Shift security left. Catch Terraform misconfigurations before they reach production.

TerraGuard is a lightweight static analysis tool for Terraform that identifies high-impact cloud misconfigurations (public storage buckets, overly permissive firewall rules, missing encryption, wildcard IAM policies) at code review time, not after deployment.

View on GitHub


Why Before Deployment?

Terraform can silently encode dangerous defaults. By the time a misconfiguration surfaces in production: a public S3 bucket, an NSG that allows 0.0.0.0/0, an IAM policy with wildcard actions: the blast radius is real. TerraGuard runs in seconds during a PR, giving engineers and security teams a catch-it-first layer before anything touches infrastructure.

No cloud credentials required. No network calls. Pure static analysis.


Features

  • Multi-cloud coverage: AWS, Azure and GCP checks out of the box
  • CI/CD native: structured exit codes designed for pipeline integration
  • SARIF output: plug directly into GitHub Security, Azure DevOps, and other code scanning dashboards
  • Configurable thresholds: tune severity levels and exclusion patterns per repo
  • Lightweight: minimal Python dependencies, fast cold start

Installation

pip install terraguard
git clone https://github.com/skellman-io/terraguard.git
cd terraguard
pip install -e .
git clone https://github.com/skellman-io/terraguard.git
cd terraguard
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

Quick Start

# Scan a directory (auto-detects .tf files)
terraguard scan ./infra

# Set a minimum severity threshold
terraguard scan ./infra --severity-threshold medium

# Exclude third-party modules
terraguard scan ./infra --exclude '**/modules/third_party/**'

# Output a SARIF file for code scanning dashboards
terraguard scan ./infra --format sarif > results.sarif

Options

Flag Description Default
--severity-threshold Minimum level to report: low medium high critical low
--format Output format: table json sarif table
--exclude Glob pattern to skip (repeatable)
--config Path to a YAML config file
-v / --verbose Verbose output off

Persistent config file

Create terraguard.yml in your repo root for settings that apply on every run:

exclude:
  - '**/modules/third_party/**'
  - '**/examples/**'

Then run with terraguard scan ./infra --config terraguard.yml.


Security Checks

AWS

Rule ID What it catches Severity
AWS_S3_PUBLIC S3 bucket with public ACL High
AWS_S3_ENCRYPTION S3 bucket missing server-side encryption High
AWS_SG_OPEN Security group allows 0.0.0.0/0 to critical ports High
AWS_IAM_WILDCARD IAM policy uses wildcard Action or Resource High
AWS_RDS_ENCRYPTION RDS instance storage encryption disabled High

Azure

Rule ID What it catches Severity
AZURE_STORAGE_PUBLIC Storage account allows blob public access High
AZURE_STORAGE_HTTP Storage account allows HTTP (non-TLS) traffic High
AZURE_NSG_OPEN NSG rule permits inbound from any source High

GCP

Rule ID What it catches Severity
GCP_GCS_UBA GCS bucket disables uniform bucket-level access High
GCP_FW_OPEN Firewall allows 0.0.0.0/0 to critical ports High

CI/CD Integration

GitHub Actions

name: TerraGuard
on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install terraguard
      - run: terraguard scan . --format sarif > results.sarif
      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif

The SARIF upload sends findings directly to the repository's Security → Code scanning tab.

GitLab CI

terraguard:
  image: python:3.11-slim
  script:
    - pip install terraguard
    - terraguard scan . --severity-threshold high

Pre-commit Hook

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: terraguard
        name: TerraGuard
        entry: terraguard scan
        language: python
        types: [terraform]
        pass_filenames: false

Output Formats

Human-readable terminal output with color-coded severity indicators. Default for local use.

Machine-readable output for custom tooling or reporting pipelines:

[
  {
    "file": "main.tf",
    "resource": "aws_s3_bucket.logs",
    "rule_id": "AWS_S3_PUBLIC",
    "severity": "high",
    "message": "S3 bucket allows public ACL.",
    "rationale": "Use private ACL and block public access.",
    "start_line": 5
  }
]

SARIF 2.1.0 format that integrates with GitHub Security, Azure DevOps, and any SARIF-compatible code scanning platform.


Exit Codes

Code Meaning
0 No findings at or above the threshold
1 Findings detected — pipeline should fail
2 Tool error (bad arguments, parse failure)
130 Interrupted (SIGINT)

Predictable exit codes mean TerraGuard works cleanly as a pipeline gate without extra scripting.


Requirements

  • Python 3.9+
  • Dependencies: python-hcl2, rich, pyyaml

Extending TerraGuard

Custom check contributions are documented in DevGuide.md in the repository. The check interface is intentionally simple: a function that receives parsed HCL and returns findings.


MIT License — Copyright © 2026 Skellman.io