How to Structure a Terraform Project: Best Practices and Example
To structure a Terraform project, organize your code into
modules for reusable components and separate environments (like dev, staging, prod) into folders with their own state files. Use a clear folder hierarchy and keep variables, outputs, and provider configurations well defined to maintain clean and scalable infrastructure code.Syntax
A typical Terraform project structure includes:
- modules/: reusable code blocks for resources
- environments/: folders for each deployment environment
- main.tf: main configuration file
- variables.tf: input variables definitions
- outputs.tf: output values
- terraform.tfvars: variable values per environment
This structure helps separate reusable logic from environment-specific settings.
plaintext
terraform-project/ ├── modules/ │ └── network/ │ ├── main.tf │ ├── variables.tf │ └── outputs.tf ├── environments/ │ ├── dev/ │ │ ├── main.tf │ │ ├── variables.tf │ │ └── terraform.tfvars │ └── prod/ │ ├── main.tf │ ├── variables.tf │ └── terraform.tfvars └── README.md
Example
This example shows a simple Terraform project with a network module and two environments: dev and prod. Each environment calls the module with its own variables and keeps separate state files.
terraform
terraform-project/ ├── modules/ │ └── network/ │ ├── main.tf │ ├── variables.tf │ └── outputs.tf ├── environments/ │ ├── dev/ │ │ ├── main.tf │ │ ├── variables.tf │ │ └── terraform.tfvars │ └── prod/ │ ├── main.tf │ ├── variables.tf │ └── terraform.tfvars # modules/network/main.tf resource "aws_vpc" "main" { cidr_block = var.vpc_cidr tags = { Name = var.vpc_name } } # modules/network/variables.tf variable "vpc_cidr" { type = string } variable "vpc_name" { type = string } # modules/network/outputs.tf output "vpc_id" { value = aws_vpc.main.id } # environments/dev/main.tf module "network" { source = "../../modules/network" vpc_cidr = var.vpc_cidr vpc_name = "dev-vpc" } # environments/dev/variables.tf variable "vpc_cidr" { type = string } # environments/dev/terraform.tfvars vpc_cidr = "10.0.0.0/16" # environments/prod/main.tf module "network" { source = "../../modules/network" vpc_cidr = var.vpc_cidr vpc_name = "prod-vpc" } # environments/prod/variables.tf variable "vpc_cidr" { type = string } # environments/prod/terraform.tfvars vpc_cidr = "10.1.0.0/16"
Output
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
vpc_id = vpc-0a1b2c3d4e5f6g7h8
Common Pitfalls
Common mistakes when structuring Terraform projects include:
- Mixing environment configurations in one folder, causing state conflicts.
- Not using modules, leading to duplicated code and harder maintenance.
- Storing sensitive data in plain
terraform.tfvarsfiles instead of secure secrets management. - Using a single state file for multiple environments, risking accidental resource changes.
Always separate environments and use modules for reusable parts.
terraform
### Wrong: Single folder for all environments # main.tf resource "aws_vpc" "main" { cidr_block = var.vpc_cidr } # terraform.tfvars vpc_cidr = "10.0.0.0/16" # Used for all environments, no separation --- ### Right: Separate folders per environment environments/dev/main.tf module "network" { source = "../../modules/network" vpc_cidr = var.vpc_cidr } # terraform.tfvars (dev) vpc_cidr = "10.0.0.0/16" environments/prod/main.tf module "network" { source = "../../modules/network" vpc_cidr = var.vpc_cidr } # terraform.tfvars (prod) vpc_cidr = "10.1.0.0/16"
Quick Reference
- Use modules to keep reusable code separate.
- Separate environments into folders with their own state files.
- Keep variables and outputs organized per module and environment.
- Use remote state backends for team collaboration and state safety.
- Secure sensitive data with environment variables or secret managers.
Key Takeaways
Organize Terraform code into modules for reusable infrastructure components.
Separate environments into distinct folders with their own state files to avoid conflicts.
Keep variables and outputs clearly defined per module and environment.
Use remote state backends for safe collaboration and state management.
Avoid storing sensitive data in plain files; use secure secrets management.