
In the previous parts of this series, we've laid the conceptual groundwork for robust Infrastructure as Code (IaC). Part 1 highlighted why structure is critical and introduced foundational elements. Part 2 delved into mastering module design and navigating the strategic choice between monorepos and polyrepos. Now, in Part 3, we get down to brass tacks: translating these concepts into tangible, practical approaches for organizing your Terraform and OpenTofu code on the filesystem and effectively managing multiple environments and state.
How you arrange your directories and files significantly impacts the clarity, maintainability, and scalability of your IaC. While there's no one-size-fits-all solution, several common and effective patterns have emerged. The ideal structure often depends on your chosen repository strategy (monorepo vs. polyrepo), the complexity of your infrastructure, and your team's workflow.
Common Folder Structure Patterns:
Pros: Clear separation of concerns per environment; easy to manage environment-specific configurations and state.
Cons: Can lead to some duplication of main.tf structure if not carefully managed with modules; deploying a single component across all environments requires changes in multiple directories.
Pros: Good for service-oriented architectures; promotes component ownership.
Cons: Managing environment-specific nuances within each component can become complex if not handled with clear variable strategies or workspace configurations.
Hybrid Approaches: Many organizations adopt a hybrid, for instance, organizing by business unit, then by application, then by environment. The key is consistency and clarity.
Structuring Module Sources:
modules/ directory within the same repository. Root configurations then reference these using relative paths (e.g., source = "../../modules/vpc").Interaction with Repository Strategy:
modules/ directory would also reside here.Component-First (Top-Level Components/Services): Organizes code by logical service or infrastructure component, with environments as subdirectories or managed via workspaces/variable files.
├── components/
│ ├── networking/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── dev.tfvars
│ │ ├── prod.tfvars
│ │ └── backend-configs/ # Or manage backend per workspace
│ │ ├── dev.s3.tfbackend
│ │ └── prod.s3.tfbackend
│ ├── application_A/
│ │ ├── ... (similar structure)
│ └── database_cluster/
│ ├── ...
├── modules/
│ ├── ...
Environment-First (Top-Level Environments): A common approach, especially for managing distinct deployment environments.
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf # Specific backend config for dev state
│ ├── staging/
│ │ ├── ... (similar structure)
│ └── prod/
│ ├── ... (similar structure)
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── ec2_instance/
│ │ ├── ...
└── global/ # Optional: for resources shared across all environments
├── iam_roles/
│ ├── ...
└── s3_buckets_shared/
├── ...
Managing infrastructure consistently across multiple environments (e.g., development, staging, production) and potentially across multiple cloud regions or accounts is a core challenge that good structure helps address.
.tfvars Files: The most common method. Define variables in variables.tf and provide environment-specific values in separate .tfvars files (e.g., dev.tfvars, prod.tfvars). Use terraform apply -var-file="dev.tfvars" to target an environment.terraform.workspace in your code to introduce conditional logic or naming differences (e.g., name = "my-resource-${terraform.workspace}"). This is often suitable for simpler environment distinctions within a single configuration directory.Terraform state is critical. How you manage it across different environments, regions, and components is fundamental to safe and reliable IaC operations.
terraform apply operations from corrupting the state file.key (in S3/Azure) or path/prefix (in GCS/Consul) within your remote backend determines where the state file is stored. Structure these paths logically and consistently.terraform-state/<PROJECT_NAME>/<ENVIRONMENT>/<REGION>/<COMPONENT>/terraform.tfstate (e.g., terraform-state/my-app/prod/us-east-1/vpc/terraform.tfstate)By implementing practical folder structures, thoughtfully managing environmental differences, and establishing robust state management practices, you create an IaC foundation that is not only organized but also resilient and adaptable to the evolving needs of your platform.
Next in the Series (Part 4): Scaling Structures and Advanced IaC Patterns.
