Skip to main content

How to Structure a Terraform Project - Best Practices 2025

Key Takeaway

The definitive guide to structuring Terraform projects with modules, environments, and workspaces. Step-by-step guide with code examples and best practices f...

Table of Contents

How Should You Structure a Terraform Project?

A well-structured Terraform project separates concerns, enables reuse, and scales with your team. Here is the recommended structure used by production teams at scale.

terraform-project/
├── modules/
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── compute/
│   └── database/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   └── prod/
├── modules.tf
├── variables.tf
├── outputs.tf
└── versions.tf

Key Principles

1. Separate Environments

Each environment (dev, staging, prod) should have its own directory with its own state file. This prevents accidental changes to production when working on development.

2. Use Modules for Reusable Components

Modules encapsulate related resources. A networking module might include VPC, subnets, route tables, and NAT gateways. This promotes consistency and reduces duplication.

3. Pin All Versions

Always pin Terraform version, provider versions, and module versions:

terraform {
  required_version = ">= 1.5.0, < 2.0.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

4. Use Remote State

Store state in a remote backend (S3, GCS, Terraform Cloud) with locking enabled:

backend "s3" {
  bucket         = "company-terraform-state"
  key            = "env/dev/terraform.tfstate"
  region         = "us-east-1"
  dynamodb_table = "terraform-locks"
  encrypt        = true
}

5. Naming Conventions

Use consistent, descriptive names: resource_type-project-environment-region-purpose.

Anti-Patterns to Avoid

  • Monolithic configurations — one giant main.tf with hundreds of resources
  • Hardcoded values — use variables and locals instead
  • Shared state across environments — always separate state per environment
  • No version pinning — leads to unexpected breaking changes

Learn More

🚀

Level Up Your Terraform Skills

Hands-on courses, books, and resources from Luca Berton

Luca Berton
Written by

Luca Berton

DevOps Engineer, AWS Partner, Terraform expert, and author. Creator of Ansible Pilot, Terraform Pilot, and CopyPasteLearn.