Blog

What are the Best Practices for Terraform File Structure? 

Best Practices for Terraform File Structure Featured img BDCC

When using Terraform to manage infrastructure as code (IaC), how you structure your code plays a vital role in maintaining scalability, readability, and collaboration. A poor file structure may work for smaller use cases, but as your infrastructure grows or your team expands, a well-planned layout becomes essential. 

In this blog, we will explore the best practices for Terraform directory structure, explain how to manage modules, environments, and variables effectively, and offer guidance for teams managing large and complex infrastructure. 

 

Why Terraform code organization matters 

Structuring your Terraform project correctly is not just about cleanliness—it supports many operational goals: 

  • Scalability: As your infrastructure grows, a clear structure helps maintain control and avoids duplication. 
  • Reusability: Modules can be defined once and reused across multiple environments and teams. 
  • Team collaboration: Well-separated responsibilities make it easier for teams to work in parallel without stepping on each other’s toes. 
  • Environment-specific management: Isolating configurations for dev, staging, and production prevents accidental changes. 
  • Security and compliance: Sensitive data, like backend state configurations and secrets, can be handled more securely with the right folder setup. 

Starting with a simple Terraform layout 

If you’re just getting started, a simple layout might look like this: 

terraform-project/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
├── backend.tf
 

This structure is acceptable for prototypes, personal projects, or small-scale infrastructure. However, it becomes difficult to scale or manage with multiple services, modules, and environments. 

Recommended Terraform directory structure for scalable projects 

As your Terraform usage matures, you should transition to a modular, environment-aware structure. Here’s a common pattern followed by many production teams: 

terraform-project/
├── modules/
│   ├── network/
│   ├── compute/
│   └── database/

├── environments/
│   ├── dev/
│   ├── staging/
│   └── prod/

├── global/
│   └── backend/
│       └── backend.tf

└── README.md
 

This layout supports clean separation between modules, environments, and global configurations. It improves code reuse, testing, and state management. 

Using Terraform modules the right way 

Modules are reusable units of Terraform configuration. Each module represents a logical component, such as a VPC, subnet, EC2 instance, or S3 bucket. 

Why modules are essential 

  • They reduce repetition by centralizing reusable code. 
  • They enforce consistency across different environments. 
  • They isolate logic, making your codebase easier to understand and maintain. 

A typical module structure looks like this: 

modules/
└── vpc/
    ├── main.tf
    ├── variables.tf
    ├── outputs.tf
 

Once created, modules can be used in environment-specific configurations: 

module "vpc" {
  source = "../../modules/vpc"
  cidr_block = var.vpc_cidr
}
 

Each module should have a single purpose and be documented properly in a README.md. 

Organizing environments for flexibility and isolation 

Every environment—like development, staging, and production—should have its own folder. This allows you to manage infrastructure states, variables, and backends independently. 

Benefits of using separate folders for environments 

  • Each environment can have its own state files, preventing cross-contamination. 
  • Different teams or pipelines can manage different environments with minimal risk. 
  • Promotions from dev to prod become easier, as you can maintain consistent logic and apply different configurations. 
Environment folder example: 
environments/
└── prod/
    ├── main.tf
    ├── variables.tf
    ├── outputs.tf
    ├── terraform.tfvars
 

Inside main.tf, you assemble modules and pass in environment-specific variables. Keep sensitive values in a secure secrets manager or use .tfvars files with proper access control. 

Managing backend configuration separately 

Terraform stores state files, and for most teams, remote backends (such as AWS S3 or Azure Storage) are preferred. To ensure clarity and avoid errors, define backend.tf separately and keep it out of version control when needed. 

A recommended structure: 

global/
└── backend/
    └── backend.tf
 

For example: 

terraform {
  backend "s3" {
    bucket = "my-terraform-states"
    key    = "prod/terraform.tfstate"
    region = "us-east-1"
  }
}
 

You can vary the key parameters per environment to ensure isolated state files. 

File naming and logical separation inside each folder 

Consistency across Terraform files enhances understanding and team productivity. Use a standard pattern: 

  • main.tf: Core resource declarations. 
  • variables.tf: Input variable declarations. 
  • outputs.tf: Output definitions. 
  • terraform.tfvars: Variable values for the environment. 
  • providers.tf: Provider configurations (if not global). 
  • backend.tf: Backend configuration (if not globally handled). 

Keeping files small and focused prevents merge conflicts and improves clarity during code reviews. 

Should you use Terraform workspaces? 

Terraform workspaces allow you to manage multiple environments using the same configuration. However, for medium to large-scale infrastructure, or when working in teams, using separate directories per environment is preferred. 

Pros of workspaces: 

  • Quick to set up for simple use cases. 
  • Fewer directories to manage. 

Cons of workspaces: 

  • Makes state management more abstract and harder to track. 
  • Limits flexibility when configuring provider-specific or backend-specific values. 
  • Harder to audit and test for larger teams. 

Use workspaces only if your environments are lightweight and your team is small. 

If you’re exploring deeper into how Terraform workspaces, backends, and data sources function together in complex projects, check out this insightful guide: Advanced Terraform Features: Workspaces, Backends, and Data Sources. It offers hands-on examples and explains how these components work in real-world scenarios. 

Version control and collaboration tips 

Version control is critical when working with Terraform. Use Git or a similar system and follow best practices like: 

  • Use descriptive and consistent folder names: dev, staging, prod. 
  • Use lowercase with hyphens in naming conventions. 
  • Commit only relevant Terraform files; ignore .terraform/, .tfstate, and .tfstate.backup. 

Include a .gitignore: 

.terraform/
*.tfstate
*.tfstate.backup
terraform.tfvars
 

Add README.md files in both your module and environment folders to document usage and requirements. 

Production-ready Terraform file structure 

Here’s how a complete Terraform project layout for multiple environments might look: 

terraform-infrastructure/
├── modules/
│   ├── vpc/
│   ├── rds/
│   └── ec2/

├── environments/
│   ├── dev/
│   ├── staging/
│   └── prod/

├── global/
│   └── backend/
│       └── backend.tf

├── scripts/
│   └── plan-apply.sh

└── .gitignore
 

Each environment contains its own set of configuration files and state management, while modules encapsulate reusable logic. 

Best way to organize Terraform code in teams 

For larger teams, structure your project to support collaboration and automation: 

  • Assign ownership of specific modules or environments. 
  • Create pull request workflows with code reviews and automatic validation using terraform validate and terraform fmt. 
  • Use CI/CD pipelines to run Terraform commands securely and consistently. 
  • Leverage Terraform services like Terraform Cloud or Terraform Enterprise for remote state management, policy enforcement, and team collaboration.
  • Store sensitive secrets in secure tools like Vault, AWS Secrets Manager, or environment variables in your pipeline. 
  • Document policies and usage in a top-level README.md. 

Automation makes Terraform safer and easier to use across teams and production deployments. 

Final thoughts: plan for scale, not just simplicity 

A scalable and well-organized Terraform file structure improves every aspect of infrastructure management. It promotes code reuse, enhances security, reduces risk in production, and helps teams collaborate more efficiently. 

Whether you’re a solo developer or part of an enterprise DevOps team, these best practices will help you write clean, maintainable Terraform code that grows with your infrastructure. 

The following two tabs change content below.
BDCC

BDCC

Co-Founder & Director, Business Management
BDCC Global is a leading DevOps research company. We believe in sharing knowledge and increasing awareness, and to contribute to this cause, we try to include all the latest changes, news, and fresh content from the DevOps world into our blogs.
BDCC

About BDCC

BDCC Global is a leading DevOps research company. We believe in sharing knowledge and increasing awareness, and to contribute to this cause, we try to include all the latest changes, news, and fresh content from the DevOps world into our blogs.