Terraform Best Practices
Following Terraform best practices ensures your infrastructure is secure, maintainable, and scalable. This guide covers production-ready practices for Terraform.
File Organization
Standard Project Structure
terraform-project/
├── main.tf # Main configuration
├── variables.tf # Input variables
├── outputs.tf # Output values
├── terraform.tfvars # Variable values
├── versions.tf # Provider requirements
├── README.md # Documentation
├── .gitignore # Git ignore rules
└── modules/ # Reusable modules
├── vpc/
├── ec2/
└── rds/
Separate Environments
environments/
├── dev/
│ ├── main.tf
│ └── terraform.tfvars
├── staging/
│ ├── main.tf
│ └── terraform.tfvars
└── production/
├── main.tf
└── terraform.tfvars
Version Management
Pin Terraform Version
# versions.tf
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
💡 Tip: Pin to specific versions in production:
version = "5.0.0"
State Management
Use Remote State
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
✅ Best Practices:
- ✅ Always use remote state in production
- ✅ Enable state locking (DynamoDB, etc.)
- ✅ Enable encryption for state files
- ✅ Enable versioning on state storage
- ✅ Never commit state files to Git
Security
Never Commit Secrets
# .gitignore
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl
*.tfvars
!terraform.tfvars.example
*.tfplan
Use Environment Variables
# For AWS credentials
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
# Or use AWS profiles
export AWS_PROFILE="production"
Use Secret Management
# Use AWS Secrets Manager
data "aws_secretsmanager_secret" "db_password" {
name = "prod/db/password"
}
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = data.aws_secretsmanager_secret.db_password.id
}
Variables Best Practices
Always Provide Descriptions
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
validation {
condition = can(regex("^t[23]\\.", var.instance_type))
error_message = "Instance type must be t2 or t3 family."
}
}
Use Sensitive Variables
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
Resource Management
Use Tags Consistently
locals {
common_tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
Owner = var.team_name
}
}
resource "aws_instance" "web" {
tags = merge(local.common_tags, {
Name = "${var.environment}-web-server"
})
}
Use Data Sources
# Instead of hardcoding AMI IDs
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
}
Module Best Practices
✅ Module Guidelines:
- ✅ One module per logical component
- ✅ Use descriptive module names
- ✅ Document modules with README.md
- ✅ Pin module versions in production
- ✅ Expose all important outputs
- ✅ Use variables for all configurable values
CI/CD Integration
Terraform Cloud/Enterprise
# terraform.yml (GitHub Actions)
name: Terraform
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
- name: Terraform Init
run: terraform init
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan
continue-on-error: true
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve
Common Mistakes to Avoid
⚠️ Common Mistakes:
- ❌ Committing state files to Git
- ❌ Hardcoding credentials
- ❌ Not using remote state
- ❌ Not pinning versions
- ❌ Not using modules for reusable code
- ❌ Not documenting variables
- ❌ Ignoring terraform plan output
- ❌ Not testing changes in dev/staging first
Next Steps
- Modules - Creating reusable modules
- State Management - Managing Terraform state
- Core Concepts - Deep dive into fundamentals