Real-World Terraform Project Structure and Patterns
Published on: November 24, 2023 | Author: DevOps Engineering Team
Discover enterprise-grade Terraform project structures, scalable patterns, and battle-tested practices for managing complex infrastructure as code.
What You'll Learn
Project Structure Patterns
Choose the right structure based on your organization's size and complexity.
Project Structure Explorer
Select a structure pattern to see detailed implementation:
🏗️ Monolithic Structure
Best for: Small projects, single environment
🌳 Environment per Folder
Best for: Multiple environments, shared modules
Module Design Principles
Create reusable, maintainable Terraform modules.
Standard Module Structure
Composite Module Example
# modules/eks-cluster/main.tf
module "vpc" {
source = "../vpc"
name = var.cluster_name
cidr_block = var.vpc_cidr
availability_zones = var.availability_zones
}
module "eks" {
source = "../eks"
cluster_name = var.cluster_name
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
node_group_name = var.node_group_name
}
Module Design Best Practices
- Keep modules focused and single-purpose
- Use semantic versioning for modules
- Provide comprehensive documentation
- Include examples in each module
- Validate inputs with conditionals
Environment Strategies
Manage multiple environments effectively with Terraform.
✅ Workspace Benefits
- Single codebase for all environments
- Easy to create new environments
- Consistent configuration
- Simplified CI/CD pipelines
❌ Workspace Challenges
- State isolation complexity
- Limited environment-specific customization
- Risk of cross-environment contamination
- Harder to manage different configurations
| Approach | Workspaces | Folder per Environment | Branch per Environment |
|---|---|---|---|
| State Management | Single backend, multiple states | Separate backends | Separate backends |
| Code Reuse | High | Medium (via modules) | Low |
| Environment Isolation | Medium | High | High |
| Complexity | Low | Medium | High |
State Management
Secure and scalable Terraform state management strategies.
🔐 Remote State Configuration
# terraform.tf
terraform {
required_version = ">= 1.5.0"
backend "s3" {
bucket = "my-company-tf-state"
key = "production/network/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# providers.tf
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "terraform"
}
}
}
🏗️ State Isolation Pattern
Best Practices
Enterprise-grade practices for scalable Terraform codebases.
📝 Naming Conventions
# Resources: {project}_{component}_{name}
resource "aws_s3_bucket" "myapp_logs_primary" {}
# Variables: descriptive with type
variable "database_instance_count" {
type = number
default = 2
}
# Outputs: {component}_{attribute}
output "vpc_main_id" {
value = aws_vpc.main.id
}
🔧 Configuration Management
# Use tfvars for environment config
# dev.tfvars
environment = "dev"
instance_type = "t3.micro"
database_size = 10
# production.tfvars
environment = "production"
instance_type = "m5.large"
database_size = 100
# Apply with:
terraform apply -var-file=dev.tfvars
📚 Code Organization
# Separate files by concern:
# - providers.tf # Provider configuration
# - main.tf # Primary resources
# - variables.tf # Input variables
# - outputs.tf # Output values
# - data.tf # Data sources
# - locals.tf # Local values
# - versions.tf # Version constraints
🛡️ Security & Compliance
# versions.tf - Pin versions
terraform {
required_version = "~> 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Use validation blocks
variable "instance_type" {
type = string
description = "EC2 instance type"
validation {
condition = contains(["t3.micro", "t3.small"], var.instance_type)
error_message = "Invalid instance type."
}
}
Avoid These Common Mistakes
- ❌ Hardcoding values instead of using variables
- ❌ Not pinning provider and Terraform versions
- ❌ Mixing environments in single state
- ❌ Not using remote state with locking
- ❌ Creating overly complex modules
- ❌ Ignoring security scanning tools
Migration Patterns
Strategies for evolving your Terraform project structure.
Monolithic to Modular
# Before: Everything in main.tf
resource "aws_vpc" "main" { ... }
resource "aws_subnet" "public" { ... }
resource "aws_instance" "web" { ... }
# After: Extract to modules
module "network" {
source = "./modules/network"
# ... inputs
}
module "compute" {
source = "./modules/compute"
# ... inputs
}
State Migration
# Move resources to new state
# 1. Pull current state
terraform state pull > current.tfstate
# 2. Move specific resource
terraform state mv \\
'aws_instance.web' \\
'module.compute.aws_instance.web'
# 3. For complex migrations, use:
# - terraform import
# - Multiple state moves
# - State file editing (carefully!)
Backend Migration
# Update backend configuration
terraform {
backend "s3" {
# New backend config
bucket = "new-state-bucket"
key = "path/to/state"
region = "us-west-2"
}
}
# Then run:
terraform init -migrate-state
# Confirm migration when prompted
# Verify state is accessible
No comments:
Post a Comment