Terraform Code Review Rules
Security (Critical)
- Interpolation Safety: Never use
${} or {{}} interpolation with unvalidated or undeclared input. Always sanitize and declare variables prior to use
- Comment Hygiene: HTML comment syntax (
<!-- -->) is not valid in Terraform files and must be flagged as an error if present. Use only valid Terraform comment syntax (# or //). Provide reasons for non-obvious configurations directly alongside related resources
- Variable Declaration: All variables and locals must be declared before use. Flag undeclared references
- Never hardcode secrets, credentials, or API keys
- Use environment variables or secret managers for sensitive values
- Mark sensitive variables and outputs with
sensitive = true
- Enable encryption at rest for storage resources
- Apply least privilege IAM policies
- Use private subnets and security groups appropriately
File Organization (Essential)
- Use consistent file structure:
main.tf, variables.tf, outputs.tf, providers.tf
- Group related resources logically
- Use
terraform.tfvars for environment-specific values (never commit secrets)
- Keep
*.tfstate files out of version control
- Add inline comments to clarify complex logic
- Document non-obvious resource relationships
Variables (Essential)
- Declare all variables in
variables.tf
- Provide
description for all variables
- Use
type constraints (string, number, bool, list, map, object)
- Set sensible
default values where appropriate
- Use
sensitive = true for secrets
Variables (Advanced)
- Apply
validation blocks for input constraints
State Management (Essential)
- Use remote state backends (S3, Azure Blob, GCS, Terraform Cloud)
- Enable state locking to prevent concurrent modifications
- Enable state encryption at rest
- Use workspaces or separate state files per environment
- Never store state locally in production
State Management (Advanced)
- Implement secret rotation mechanisms where supported
- Review and plan for secret/key rotation lifecycle
- Use cloud provider secret rotation features
Modules
- Create reusable modules for common patterns
- Pin module versions explicitly
- Use outputs to expose necessary values
- Document module inputs and outputs
- Follow module structure:
main.tf, variables.tf, outputs.tf, README.md
- Review module sources for security and trustworthiness
- Audit third-party modules before use
- Check module license compatibility
- Prefer official/verified modules from trusted sources
Resource Naming
- Use consistent naming conventions
- Include environment, region, and purpose in names
- Use
random provider for unique suffixes when needed
- Follow cloud provider naming rules and restrictions
Best Practices
- Use
terraform fmt for consistent formatting
- Run
terraform validate before applying
- Use
terraform plan to review changes before apply
- Prefer
count or for_each over duplicate resource blocks
- Use
locals for computed values and reduce repetition
- Use
depends_on only when implicit dependencies aren't sufficient
- Pin provider versions in
required_providers block
- Use static analysis tools (e.g.,
tflint, checkov) for quality and security
- Implement automated testing with
terraform test or similar
- Detect and mitigate infrastructure drift with
terraform plan
- Use lifecycle rules (
create_before_destroy, prevent_destroy) appropriately
- Prevent data loss with
prevent_destroy on critical resources
- Use
create_before_destroy to avoid downtime during updates
Outputs
- Output only values needed by other configurations
- Mark sensitive outputs with
sensitive = true
- Include resource IDs and endpoints for downstream use
- Document outputs with
description
Example Patterns
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
locals {
base_name = "myapp-${var.environment}"
common_tags = {
Environment = var.environment
ManagedBy = "terraform"
}
}
resource "aws_s3_bucket" "main" {
bucket = "${local.base_name}-data"
tags = local.common_tags
}
output "bucket_arn" {
description = "ARN of the S3 bucket"
value = aws_s3_bucket.main.arn
}
1---2name: terraform3description: Terraform IaC patterns, state management, security, and modular design4---56## Terraform Code Review Rules78### Security (Critical)9- **Interpolation Safety**: Never use `${}` or `{{}}` interpolation with unvalidated or undeclared input. Always sanitize and declare variables prior to use10- **Comment Hygiene**: HTML comment syntax (`<!-- -->`) is not valid in Terraform files and must be flagged as an error if present. Use only valid Terraform comment syntax (`#` or `//`). Provide reasons for non-obvious configurations directly alongside related resources11- **Variable Declaration**: All variables and locals must be declared before use. Flag undeclared references12- Never hardcode secrets, credentials, or API keys13- Use environment variables or secret managers for sensitive values14- Mark sensitive variables and outputs with `sensitive = true`15- Enable encryption at rest for storage resources16- Apply least privilege IAM policies17- Use private subnets and security groups appropriately1819### File Organization (Essential)20- Use consistent file structure: `main.tf`, `variables.tf`, `outputs.tf`, `providers.tf`21- Group related resources logically22- Use `terraform.tfvars` for environment-specific values (never commit secrets)23- Keep `*.tfstate` files out of version control24- Add inline comments to clarify complex logic25- Document non-obvious resource relationships2627### Variables (Essential)28- Declare all variables in `variables.tf`29- Provide `description` for all variables30- Use `type` constraints (string, number, bool, list, map, object)31- Set sensible `default` values where appropriate32- Use `sensitive = true` for secrets3334### Variables (Advanced)35- Apply `validation` blocks for input constraints3637### State Management (Essential)38- Use remote state backends (S3, Azure Blob, GCS, Terraform Cloud)39- Enable state locking to prevent concurrent modifications40- Enable state encryption at rest41- Use workspaces or separate state files per environment42- Never store state locally in production4344### State Management (Advanced)45- Implement secret rotation mechanisms where supported46- Review and plan for secret/key rotation lifecycle47- Use cloud provider secret rotation features4849### Modules50- Create reusable modules for common patterns51- Pin module versions explicitly52- Use outputs to expose necessary values53- Document module inputs and outputs54- Follow module structure: `main.tf`, `variables.tf`, `outputs.tf`, `README.md`55- Review module sources for security and trustworthiness56- Audit third-party modules before use57- Check module license compatibility58- Prefer official/verified modules from trusted sources5960### Resource Naming61- Use consistent naming conventions62- Include environment, region, and purpose in names63- Use `random` provider for unique suffixes when needed64- Follow cloud provider naming rules and restrictions6566### Best Practices67- Use `terraform fmt` for consistent formatting68- Run `terraform validate` before applying69- Use `terraform plan` to review changes before apply70- Prefer `count` or `for_each` over duplicate resource blocks71- Use `locals` for computed values and reduce repetition72- Use `depends_on` only when implicit dependencies aren't sufficient73- Pin provider versions in `required_providers` block74- Use static analysis tools (e.g., `tflint`, `checkov`) for quality and security75- Implement automated testing with `terraform test` or similar76- Detect and mitigate infrastructure drift with `terraform plan`77- Use lifecycle rules (`create_before_destroy`, `prevent_destroy`) appropriately78- Prevent data loss with `prevent_destroy` on critical resources79- Use `create_before_destroy` to avoid downtime during updates8081### Outputs82- Output only values needed by other configurations83- Mark sensitive outputs with `sensitive = true`84- Include resource IDs and endpoints for downstream use85- Document outputs with `description`8687### Example Patterns88```hcl89terraform {90 required_version = ">= 1.0"91 required_providers {92 aws = {93 source = "hashicorp/aws"94 version = "~> 5.0"95 }96 }97 backend "s3" {98 bucket = "my-terraform-state"99 key = "prod/terraform.tfstate"100 region = "us-east-1"101 encrypt = true102 dynamodb_table = "terraform-locks"103 }104}105106variable "environment" {107 description = "Environment name"108 type = string109 validation {110 condition = contains(["dev", "staging", "prod"], var.environment)111 error_message = "Environment must be dev, staging, or prod."112 }113}114115variable "db_password" {116 description = "Database password"117 type = string118 sensitive = true119}120121locals {122 base_name = "myapp-${var.environment}"123 common_tags = {124 Environment = var.environment125 ManagedBy = "terraform"126 }127}128129resource "aws_s3_bucket" "main" {130 bucket = "${local.base_name}-data"131 tags = local.common_tags132}133134output "bucket_arn" {135 description = "ARN of the S3 bucket"136 value = aws_s3_bucket.main.arn137}138```