The three-file pattern
Terraform splits variable handling across three files by convention:
| File | Role |
|---|---|
variables.tf | Declares that a variable exists and its type. No value set here. |
terraform.tfvars | Sets the actual values. Terraform loads this automatically. |
main.tf (or any .tf) | Consumes values via var.<name>. |
# variables.tf — declaration only, like a C++ header file
variable "zone" {
type = string
}
# terraform.tfvars — the one file you edit when values change
zone = "us-central1-a"
# main.tf — consumption
resource "google_compute_instance" "vm" {
zone = var.zone
}The flow: terraform.tfvars feeds values into the declarations in variables.tf, and any .tf file consumes them via var.<name>.
Why split it this way
Writing zone = "us-central1-a" directly in main.tf works, but terraform.tfvars becomes the single place where all environment-specific values live — zone, region, project ID, machine type, etc. When something needs to change, you edit one file instead of hunting through resource definitions.
Scope: global within a module
var.zone is effectively a global within the module (the directory). Every .tf file in the same folder shares the same variable namespace — main.tf, network.tf, firewall.tf, all of them can reference var.zone without any import.
Variables do not leak into submodules. A child module has its own separate variable namespace and cannot see the parent’s var.zone automatically. You must pass values in explicitly when calling the module:
# parent main.tf
module "network" {
source = "./modules/network"
zone = var.zone # explicitly passed, not inherited
}The child module also needs its own variables.tf to declare what it accepts:
# modules/network/variables.tf
variable "zone" {
type = string
}Only then can the child use var.zone internally. Nothing from the parent’s scope leaks in — you only get what was explicitly handed to you. This mirrors calling a function with arguments rather than reading a global.
See also
- terraform-modules — how to read HCL, block types, references, meta-arguments, modules
- gcp-vm-terraform-gotchas — practical example of zone pinning via tfvars