Saturday, November 29, 2025

Real-World Terraform Project Structure and Patterns

Real-World Terraform Project Structure and Patterns - DevOps Preparation
Terraform Project Structure Best Practices Enterprise Modules Patterns

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.

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:

Select a structure pattern to see detailed implementation...

🏗️ Monolithic Structure

Best for: Small projects, single environment

📁 my-project/
📄 main.tf
📄 variables.tf
📄 outputs.tf
📄 terraform.tfvars
📄 providers.tf

🌳 Environment per Folder

Best for: Multiple environments, shared modules

📁 infrastructure/
📁 modules/
📁 environments/
📁 dev/
📁 staging/
📁 production/

Module Design Principles

Create reusable, maintainable Terraform modules.

Standard Module Structure

📁 modules/vpc/
📄 main.tf # Primary resources
📄 variables.tf # Input variables
📄 outputs.tf # Output values
📄 versions.tf # Version constraints
📄 README.md # Documentation

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

📁 infrastructure/
📁 global/
📄 backend.hcl
📄 terraform.tf
📁 regional/
📁 us-east-1/
📁 eu-west-1/
📁 environments/
📁 dev/
📁 staging/
📁 production/

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.

1

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
}
2

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!)
3

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

Enterprise Project Structure Example

📁 terraform-enterprise/
📁 modules/
📁 networking/
📁 compute/
📁 database/
📁 security/
📁 environments/
📁 dev/
📁 us-east-1/
📁 eu-west-1/
📁 staging/
📁 production/
📁 global/
📁 iam/
📁 dns/
📄 README.md
📄 versions.tf
📄 .terraform-version

This is Part 13 of The Ultimate Terraform Mastery Series.

Next: Advanced Terraform Patterns →

No comments:

Post a Comment

Linux Security & Permissions for DevOps

Linux Security & Permissions - DevOps Security Guide Linux Security & Permissions ...