Project Structure
A consistent project structure prevents confusion as your team and codebase grow. Use this pattern for medium to large projects:
project/
โโโ main.tf # Root module โ call child modules
โโโ variables.tf # All input variable declarations
โโโ outputs.tf # All output declarations
โโโ providers.tf # Provider and version constraints
โโโ versions.tf # terraform {} block with required_version
โโโ locals.tf # Local values (avoid inline locals)
โโโ data.tf # Data source lookups
โโโ terraform.tfvars.example # Example values (no secrets!)
โโโ modules/
โโโ networking/
โโโ compute/
โโโ database/
State Management
State is the most operationally sensitive part of Terraform. Follow these rules:
- Always use remote backends โ S3+DynamoDB, Azure Blob, GCS, or Terraform Cloud
- Enable state locking โ DynamoDB for S3, always enabled for Terraform Cloud
- Enable state encryption โ
encrypt = truein your S3 backend - Never commit state files โ add
*.tfstateand*.tfstate.backupto.gitignore - Separate state per environment โ dev, staging, prod must never share state
terraform {
backend "s3" {
bucket = "company-tf-state-prod"
key = "services/api/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-locks"
}
}
Module Design
Modules should be small, focused, and reusable. Follow these principles:
- One module per logical resource group (VPC, compute cluster, database)
- Never nest more than 3 levels deep
- Always document input/output variables with
description - Pin module versions when using the Terraform Registry
- Use
countorfor_eachinstead of duplicating resources
variable "instance_count" {
description = "Number of EC2 instances to launch"
type = number
default = 2
validation {
condition = var.instance_count >= 1 && var.instance_count <= 20
error_message = "Instance count must be between 1 and 20."
}
}
Security Best Practices
- Use OIDC for CI/CD authentication โ no long-lived AWS access keys in GitHub Secrets
- Least-privilege IAM โ your Terraform IAM user/role should have only what it needs
- Secrets via Secrets Manager โ never put passwords or API keys in
.tfvarsor state - Run tfsec or Checkov โ catch misconfigurations before they reach production
- Enable S3 versioning on state bucket โ allows rolling back accidental state corruption
Testing Terraform Code
Treat infrastructure code with the same rigour as application code:
terraform validateโ syntax and type checking, run in every PR- tflint โ provider-specific linting rules for AWS, Azure, GCP
- Checkov / tfsec โ security scanning before
apply - Terratest (Go) โ write unit and integration tests for modules
- Infracost โ cost estimation before merge to prevent surprise cloud bills
Try CloudFormation Free
Design infrastructure visually and generate production-ready Terraform in seconds โ 693 resources across 9 cloud providers.
๐ Open the Designer โ