Variables, outputs & locals

Parameterize and expose values.

Beginner12 min · lesson 5 of 15

Hard-coded values make a config rigid and un-reusable. Terraform has three tools to fix that: input variables (values you pass in), outputs (values you expose out), and locals (named expressions computed inside). Together they turn a one-off script into a parameterized, composable configuration — the same code deploying dev and prod by changing inputs, and feeding values to other configs through outputs.

variables.tf
variable "instance_type" {
type = string
default = "t3.micro"
description = "EC2 size for the web tier"
}
variable "environment" {
type = string
validation { # reject bad input early
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be dev, staging, or prod."
}
}

Passing variables in

You reference a variable as var.name, and supply its value one of several ways, in a defined precedence: a default in the block, a terraform.tfvars file, -var on the command line, or a TF_VAR_name environment variable (highest-priority, and the clean way to inject secrets in CI). Precedence means an environment variable or -var overrides a tfvars file, which overrides the default — so the same code adapts per environment without edits.

terminal
# a prod.tfvars file, or CLI, or env var — same variable, different value
$ terraform apply -var-file=prod.tfvars
$ terraform apply -var="instance_type=t3.large"
$ TF_VAR_instance_type=t3.large terraform apply # env var (good for secrets/CI)

Outputs and locals

Outputs expose values after apply — an instance’s IP, a database endpoint — printed at the end and, importantly, readable by other configurations through remote state, which is how modules and stacks pass data to each other. Locals name a computed expression so you write it once and reuse it (a naming convention, a merged tag set). Mark sensitive outputs so Terraform redacts them from CLI output.

outputs.tf
locals {
name_prefix = "${var.environment}-web" # computed once, reused
}
resource "aws_instance" "web" {
instance_type = var.instance_type
tags = { Name = local.name_prefix }
}
output "public_ip" {
value = aws_instance.web.public_ip
}
output "db_password" {
value = aws_db_instance.main.password
sensitive = true # redacted from CLI output
}
sensitive hides output, not state
Marking an output sensitive = true stops Terraform printing it to the terminal — useful — but the value is still written in plaintext to the state file. sensitive is display hygiene, not encryption. Real secrets still demand a secured, encrypted remote backend and, ideally, sourcing the value from a secrets manager rather than putting it in Terraform at all (covered in the advanced security lesson).