Introduction
Today we focus on file structure and organising Terraform projects. Initially all resources were placed in a single main.tf file to simplify the learning process. What we are doing now is to improve the project structure by splitting resources and configurations into multiple files for better readability, collaboration and maintainablity.
The root directory of a Terraform project is called the root module.
Terraform File Loading
There is no strict mandatory naming convention for Terraform files.recommendations come from HashiCorp best practices but are flexible.
How Terraform loads files;
- Terraform loads all
.tffiles in the current directory - Files are loaded in lexicographical order (alphabetical)
- All
.tffiles are merged into a single configuration
Recommended File Structure and Best Practices
project-root/
├── backend.tf # Backend configuration
├── provider.tf # Provider configurations
├── variables.tf # Input variable definitions
├── locals.tf # Local value definitions
├── main.tf # Main resource definitions
├── vpc.tf # VPC-related resources
├── security.tf # Security groups, NACLs
├── compute.tf # EC2, Auto Scaling, etc.
├── storage.tf # S3, EBS, EFS resources
├── database.tf # RDS, DynamoDB resources
├── outputs.tf # Output definitions
├── terraform.tfvars # Variable values
└── README.md # Documentation
Common recommended files in the root module include:
-
main.tf: Contains primary resource definitions such as S3, VPC, EC2, etc. Alternatively, resources may be further subdivided into files named after the resource type (e.g.,
s3.tf,vpc.tf,ec2.tf), but this can become complex with many resources. - variables.tf: Dedicated file for input variable declarations (moved out from main.tf).
- outputs.tf: File for output declarations.
- providers.tf: Contains provider configuration.
- backend.tf: Contains backend configuration for state management.
- terraform.tfvars: File for input variable values (sensitive values should not be committed).
- terraform.tfvars.example: Template file for variable values to be shared publicly without exposing secrets.
- .gitignore: To exclude sensitive or unnecessary files from being committed to version control.
-
README: Documentation file.
Key best practice: Do not commit
.tfvarsfiles containing secrets or sensitive values to GitHub; instead, commit a.exampletemplate.
.gitignore Configuration and Files to Exclude
The .gitignore file should exclude:
-
.terraform/directory (Terraform metadata, plugins, and cache) -
*.tfstateand*.tfstate.backupfiles (Terraform state files) -
.terraform.lock.hcl(provider dependency lock file) -
*.logfiles (e.g., crash logs) terraform.tfvarsand any*.tfvars.jsonfiles (to avoid publishing sensitive data)These exclusions protect sensitive information and prevent unnecessary files from cluttering the repository.
Advanced Project Structures and Environment Management
-
Multiple environments: Separate folders or configurations for
dev,staging,prod, each with distinctmain.tffiles.
environments/
├── dev/
│ ├── backend.tf
│ ├── terraform.tfvars
│ └── main.tf
├── staging/
│ ├── backend.tf
│ ├── terraform.tfvars
│ └── main.tf
└── production/
├── backend.tf
├── terraform.tfvars
└── main.tf
- Modularisation: Using modules for logically grouped resources, e.g., networking, compute, security modules.
modules/
├── vpc/
├── security/
└── compute/
Another approach is to use the same Terraform configuration files for all environments but supply different terraform.tfvars files for each environment to inject environment-specific values.
Terraform should be readable first, functional second.
Day 6 helped me understand something deeper:
Good Terraform isn’t just about writing code—it’s about organizing it.
A clean structure makes your infrastructure:
- Easier to maintain
- Easier to scale
- Easier for team to collaborate
- Easier to troubleshoot
Below is the video that helped me understand this concept;
Top comments (0)