Terraform Static Analysis
Introduction
Static analysis is a method of debugging and analyzing your code without actually executing it. For Terraform, this means identifying potential issues, security vulnerabilities, and style inconsistencies in your infrastructure code before you apply it to your environment.
Unlike dynamic testing which verifies that your infrastructure behaves correctly at runtime, static analysis looks at your Terraform code in isolation to find problems early in the development process. This preventative approach helps catch issues before they make it to production, saving time and reducing risks.
Why Static Analysis Matters for Terraform
Infrastructure as Code (IaC) has revolutionized how we manage cloud resources, but with this power comes responsibility. Errors in Terraform code can lead to:
- Misconfigured resources that don't work as expected
- Security vulnerabilities exposing your infrastructure
- Compliance violations that might affect your organization
- Unexpected costs from inefficient resource definitions
Static analysis tools help address these concerns by automatically scanning your code for common pitfalls and best practice violations.
Popular Terraform Static Analysis Tools
1. Terraform validate
The simplest form of static analysis comes built into Terraform itself with the terraform validate
command.
terraform validate
Example output for valid configuration:
Success! The configuration is valid.
Example output with errors:
Error: Invalid resource type
on main.tf line 15, in resource "aws_ec2_instanc" "example":
15: resource "aws_ec2_instanc" "example" {
The provider provider.aws does not support resource type "aws_ec2_instanc".
Did you mean "aws_ec2_instance"?
While terraform validate
is useful for catching syntax errors and undefined variables, it has limitations. It doesn't check for security best practices or style issues, which is where specialized tools come in.
2. TFLint
TFLint is a powerful linter specifically designed for Terraform. It goes beyond basic validation by checking for:
- Possible errors that
terraform validate
might miss - Deprecated syntax or provider features
- Provider-specific issues and recommendations
- Naming conventions and stylistic problems
Installation:
# For macOS
brew install tflint
# For Linux
curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash
Basic usage:
tflint
Example configuration (.tflint.hcl):
plugin "aws" {
enabled = true
version = "0.21.1"
source = "github.com/terraform-linters/tflint-ruleset-aws"
}
rule "terraform_documented_outputs" {
enabled = true
}
rule "terraform_documented_variables" {
enabled = true
}
Example output:
1 issue(s) found:
Warning: The "t2.micro" instance type is previous generation (aws_instance_previous_type)
on main.tf line 23:
23: instance_type = "t2.micro"
Reference: https://github.com/terraform-linters/tflint-ruleset-aws/blob/master/docs/rules/aws_instance_previous_type.md
3. Checkov
Checkov is an open-source static analysis tool focused on security and compliance checks for infrastructure as code. It scans your Terraform files for misconfigurations that could lead to security vulnerabilities.
Installation:
pip install checkov
Basic usage:
checkov -d .
Example output:
Check: CKV_AWS_79: "Ensure Instance Metadata Service Version 1 is not enabled"
FAILED for resource: aws_instance.example
File: /main.tf:23-45
23 | resource "aws_instance" "example" {
24 | ami = "ami-0c55b159cbfafe1f0"
25 | instance_type = "t2.micro"
Check: CKV_AWS_126: "Ensure that detailed monitoring is enabled for EC2 instances"
FAILED for resource: aws_instance.example
File: /main.tf:23-45
23 | resource "aws_instance" "example" {
24 | ami = "ami-0c55b159cbfafe1f0"
25 | instance_type = "t2.micro"
...
Summary:
Passed checks: 7
Failed checks: 4
Skipped checks: 0
4. Terrascan
Terrascan is another security-focused static analysis tool that detects compliance and security violations across Infrastructure as Code.
Installation:
# For macOS
brew install terrascan
# For Linux
curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz
tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz
sudo install terrascan /usr/local/bin && rm terrascan
Basic usage:
terrascan scan
Example output:
Violation Details -
Description : Ensure all data stored in the S3 bucket is securely encrypted at rest
File : main.tf
Line : 42
Severity : HIGH
Rule Name : aws_s3_bucket_encryption
Rule ID : AWS.S3Bucket.EncryptionandKeyManagement.High.0405
Resource Name : aws_s3_bucket.data
Resource Type : aws_s3_bucket
Category : Encryption & Key Management
Implementing Static Analysis in Your Workflow
To get the most out of static analysis, integrate it into your development workflow. Here's a step-by-step guide:
1. Set Up Pre-commit Hooks
Pre-commit hooks can run static analysis tools automatically before you commit your changes.
Example .pre-commit-config.yaml:
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.77.1
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_tflint
- id: checkov
- id: terrascan
2. Integrate with CI/CD Pipelines
Adding static analysis to your CI/CD pipeline ensures all code is checked before deployment.
Example GitHub Actions workflow (.github/workflows/terraform-analysis.yml):
name: "Terraform Static Analysis"
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Format Check
run: terraform fmt -check -recursive
- name: Terraform Validate
run: |
terraform init -backend=false
terraform validate
- name: Setup TFLint
uses: terraform-linters/setup-tflint@v3
- name: Run TFLint
run: tflint --format=compact
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: terraform
Real-world Example: Static Analysis in Action
Let's examine a Terraform configuration with multiple issues and see how static analysis tools can help identify them.
Original main.tf with issues:
provider "aws" {
region = "us-west-2"
}
resource "aws_s3_bucket" "log_bucket" {
bucket = "my-tf-log-bucket"
acl = "public-read" # Security issue: public access
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t1.micro" # Deprecated instance type
root_block_device {
volume_size = 8
# Missing encryption
}
# Missing tags
}
resource "aws_security_group" "allow_all" {
name = "allow_all"
description = "Allow all inbound traffic"
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"] # Security issue: open to the world
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
After running static analysis tools, we would find issues like:
- TFLint would flag the deprecated
t1.micro
instance type - Checkov would identify:
- Public S3 bucket (security risk)
- Unencrypted EBS volume
- Missing resource tags
- Overly permissive security group
Corrected main.tf:
provider "aws" {
region = "us-west-2"
}
resource "aws_s3_bucket" "log_bucket" {
bucket = "my-tf-log-bucket"
}
resource "aws_s3_bucket_acl" {
bucket = aws_s3_bucket.log_bucket.id
acl = "private" # Corrected: private access
}
resource "aws_s3_bucket_server_side_encryption_configuration" {
bucket = aws_s3_bucket.log_bucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro" # Corrected: current generation
root_block_device {
volume_size = 8
encrypted = true # Added encryption
}
monitoring = true # Added detailed monitoring
tags = { # Added tags
Name = "WebServer"
Environment = "Production"
ManagedBy = "Terraform"
}
}
resource "aws_security_group" "web_access" {
name = "web_access"
description = "Allow web traffic"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"] # Corrected: restricted network
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"] # Corrected: restricted network
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
Best Practices for Terraform Static Analysis
To maximize the effectiveness of static analysis in your Terraform projects:
- Run static analysis early and often - Catch issues during development, not during deployment
- Use multiple tools - Different tools catch different problems
- Customize rules based on your needs - Not all warnings are relevant to every project
- Fix issues incrementally - Address high-severity issues first
- Document exceptions - When you must ignore a warning, document why
- Keep tools updated - New rules are added as cloud providers evolve
Custom Rules and Policies
Many static analysis tools allow you to create custom rules for organization-specific requirements.
Example custom TFLint rule (.tflint.hcl):
rule "terraform_required_tags" {
enabled = true
tags = ["Environment", "Owner", "Project"]
}
Example custom Checkov check:
from checkov.common.models.enums import CheckResult, CheckCategories
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
class RequireInstanceMonitoring(BaseResourceCheck):
def __init__(self):
name = "Ensure detailed monitoring is enabled for EC2 instances"
id = "CKV_CUSTOM_1"
supported_resources = ['aws_instance']
categories = [CheckCategories.MONITORING]
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
def scan_resource_conf(self, conf):
if 'monitoring' in conf.keys():
if conf['monitoring'][0]:
return CheckResult.PASSED
return CheckResult.FAILED
check = RequireInstanceMonitoring()
Visualizing Static Analysis Results
Visual representation of static analysis results can help teams understand and prioritize issues.
Summary
Static analysis is an essential practice for maintaining high-quality Terraform code. By integrating tools like TFLint, Checkov, and Terrascan into your development workflow, you can catch issues early, improve security, and ensure compliance with best practices.
Remember that static analysis is just one part of a comprehensive testing strategy for infrastructure as code. It works best when combined with dynamic testing, peer reviews, and proper documentation.
Additional Resources and Exercises
Resources
- Terraform Documentation on terraform validate
- TFLint GitHub Repository
- Checkov Documentation
- Terrascan Documentation
Exercises
-
Basic Setup: Install TFLint and run it against an existing Terraform project. How many issues does it find?
-
Tool Comparison: Run both Checkov and Terrascan against the same Terraform configuration. Compare the results and identify which issues are found by both tools and which are unique to each.
-
Custom Rules: Create a custom TFLint rule that enforces a naming convention for your resources.
-
CI Integration: Set up a GitHub Actions workflow that runs static analysis on your Terraform code when you push changes.
-
Fix and Verify: Take a Terraform configuration with known issues, run static analysis, fix the identified problems, and verify the improvements with a second scan.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)