Stop hardcoding values. Declare typed input variables, set them with .tfvars files and environment variables, and reject bad input with validation rules.
Why: variables make a configuration reusable instead of hardcoded. Declare one with a type and an optional default, then read it as var.name. A variable with no default becomes required — Terraform will prompt for it if you do not supply it.
variable "environment" {
type = string
default = "dev"
}
variable "instance_count" {
type = number
description = "How many copies to create"
# no default -> required
}
resource "local_file" "env" {
filename = "env.txt"
content = var.environment # read it with var.<name>
}Why: types catch mistakes before apply. Beyond string, number, and bool, Terraform has collection types — list, set, map — and structural ones like object. A typed variable rejects a value of the wrong shape at plan time instead of failing deep in a provider call.
variable "ports" {
type = list(number)
default = [80, 443]
}
variable "tags" {
type = map(string)
default = { team = "web", tier = "frontend" }
}
variable "server" {
type = object({
name = string
cpu = number
})
}Why: you keep values out of the code and per-environment in a .tfvars file. Terraform auto-loads terraform.tfvars and any *.auto.tfvars; pass others with -var-file. This is how the same configuration deploys dev and prod — only the values file changes.
# dev.tfvars
environment = "dev"
instance_count = 1
ports = [80]
# prod.tfvars
environment = "prod"
instance_count = 5
ports = [80, 443]Why: values can come from several places. -var sets one inline; -var-file loads a file; and any environment variable named TF_VAR_<name> is picked up automatically — the standard way to inject secrets in CI without writing them to a file. Later sources override earlier ones.
Inline
terraform apply -var="environment=staging"From a file
terraform apply -var-file="prod.tfvars"From the environment (great for CI secrets)
export TF_VAR_instance_count=3terraform applyWhy: a validation block rejects bad values with a clear message at plan time, instead of letting them cause a confusing failure later. The condition must be true for the value to be accepted. Use it to enforce allowed values, ranges, or naming rules.
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be dev, staging, or prod."
}
}