Terraform 28: 1: Organizing your .tf files
easy⏱ 5 mincourseterraform
One directory, many files, one module
Terraform concatenates all .tf files in a directory into a single root module before evaluating anything. Order and file boundaries do not matter to the engine — a resource in main.tf can reference a variable declared in variables.tf. The split exists purely so a reviewer can find things fast.
# The conventional layout of a single root module:
#
# .
# ├── main.tf # resources & data sources
# ├── variables.tf # input variable declarations
# ├── outputs.tf # output values
# ├── providers.tf # provider + required_providers
# └── backend.tf # remote state backend config
What lives in each file
providers.tf pins the provider source and versions. variables.tf declares inputs (no values). main.tf holds the actual resources. outputs.tf exposes values other modules or humans need. backend.tf says where state is stored.
# providers.tf
terraform {
required_version = ">= 1.6.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
}
# variables.tf
variable "location" {
type = string
description = "Azure region for all resources."
default = "westus2"
}
# main.tf
resource "azurerm_resource_group" "orders" {
name = "myorg-orders-dev-rg"
location = var.location
}
# outputs.tf
output "resource_group_name" {
value = azurerm_resource_group.orders.name
}
Big files? Split by concern, not by count
When main.tf gets long, split it by domain — network.tf, storage.tf, keyvault.tf — rather than arbitrarily. Terraform still merges them, and the file name becomes documentation. Avoid one giant file and avoid a hundred tiny ones.
Model the file split
Build a fileRoles object mapping each file name (main.tf, variables.tf, outputs.tf, providers.tf, backend.tf) to a one-line description of what goes in it. Log fileRoles["variables.tf"].