What is the difference between variables, locals, and outputs?
Quick Answer
**Variables** are inputs supplied from outside the module (CLI, tfvars, env vars). **Locals** (`locals { ... }`) are named expressions computed *inside* a module to avoid repeating logic — they can't be overridden from outside. **Outputs** (`output "name" { value = ... }`) expose values *out* of a module, either for the CLI (`terraform output`), for another module that calls this one, or for remote state consumers (`terraform_remote_state`). Together they form a module's public interface: variables in, outputs out, locals as private helpers in between.
Detailed Answer
These three constructs — variables, locals, and outputs — form the complete "interface" of any Terraform module: inputs in, computed helpers in the middle, values out.
Variables — inputs
variable "environment" {
type = string
}
Set from outside the module: CLI flags, tfvars files, environment variables, or (for a child module) the calling module's module block arguments. A module's author defines what variables exist; the caller decides their values.
Locals — internal, derived values
locals {
name_prefix = "${var.project}-${var.environment}"
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
}
}
Locals compute a value once, inside the module, from variables/other locals/resource attributes, and are referenced as local.name_prefix. They exist purely to avoid repeating the same expression across many resource blocks — they cannot be set or overridden from outside the module; they're private implementation detail.
Outputs — what the module exposes
output "bucket_arn" {
value = aws_s3_bucket.assets.arn
description = "ARN of the created assets bucket"
}
Outputs expose values out of a module for three audiences:
- The CLI, via
terraform output(handy for scripting or quick inspection). - A parent module, referenced as
module.<name>.bucket_arn. - Anyone consuming this configuration's remote state via
terraform_remote_state, letting a completely separate configuration read values it produced.
Putting it together
variables (external input)
│
▼
locals (internal computation, DRY helpers)
│
▼
resources (actually created, using vars + locals)
│
▼
outputs (external-facing results)
A well-designed module minimizes what it exposes as variables/outputs (a clean, stable public interface) while doing as much repeated computation as possible in locals — keeping the resource blocks themselves simple and readable.