Terraform Variables: Input, Locals, and Outputs
Published on
Terraform variables decouple static definitions from infrastructure code, allowing modules to remain reusable and environment-agnostic across Dev, Staging, and Production.
1. Variable Types at a Glance
| Construct | Purpose | Scope / Overridable |
|---|---|---|
Input (variable) | Parameterize execution; inject parameters | Overridable via CLI, files, and env vars |
Locals (locals) | Internal computed expressions and constants | Module-internal only (fixed during run) |
Output (output) | Expose resource attributes and runtime IDs | CLI readout or upstream module reference |
2. Input Variables (variable)
Basic Definition
# variables.tf
variable "environment" {
type = string
description = "Target deployment environment"
default = "dev"
}
variable "region" {
type = string
description = "Target AWS Region"
default = "us-east-1"
}
variable "app_prefix" {
type = string
description = "Unique resource prefix"
default = "ttwp"
}
Supported Type Constraints
- Primitives:
string,number,bool - Collections / Structural:
list(<TYPE>),set(<TYPE>),map(<TYPE>),object({...}),tuple([...]) - Dynamic / Special:
any(inferred at runtime),null(omits default)
3. Local Values (locals)
Use locals to compute standardized naming conventions, merge common tags, or avoid duplicate expression logic.
# locals.tf
locals {
# Composite resource names
bucket_name = "${var.app_prefix}-bucket-${var.environment}-${var.region}"
vpc_name = "${var.environment}-vpc"
# Standard organizational tags
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
# main.tf
resource "aws_s3_bucket" "storage" {
bucket = local.bucket_name
tags = merge(local.common_tags, {
Name = local.bucket_name
})
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = merge(local.common_tags, {
Name = local.vpc_name
})
}
localscannot be overwritten from the outside (CLI or.tfvars). Keep them reserved for pure transformation logic.
4. Outputs (output)
Export provisioned IDs, connection strings, or generated values.
# outputs.tf
output "vpc_id" {
description = "ID of provisioned VPC"
value = aws_vpc.main.id
}
output "instance_id" {
description = "EC2 Instance ID"
value = aws_instance.web.id
}
Retrieval Commands
# Query all exported outputs after apply
terraform output
# Extract single value in raw string format
terraform output -raw vpc_id
# Query as JSON
terraform output -json
Computed outputs (IDs, ARNs, DNS names) resolve during
terraform applyand display as<known after apply>during the plan stage.
5. Variable Precedence Order
When the same variable is supplied via multiple vectors, Terraform resolves priority from lowest to highest:
defaultinside thevariableblock- Environment variables:
TF_VAR_<variable_name> terraform.tfvarsterraform.tfvars.json*.auto.tfvarsor*.auto.tfvars.json(loaded alphabetically)- CLI flags:
-varor-var-file
Resolution Example
# 1. Default fallback: "dev"
# 2. Overridden via shell env:
export TF_VAR_environment="stage"
# 3. Overridden via tfvars file:
echo 'environment = "pre-prod"' > terraform.tfvars
# 4. Overridden via CLI runtime flag (highest priority):
terraform plan -var="environment=prod"
6. Common Gotchas
- Interpolation Syntax: Direct concatenation like
var.env-bucketwill fail. Wrap it in curly braces:"${var.env}-bucket". Direct variable assignment needs no wrapping:var.env.- Resource Block Typos: In the AWS provider, EC2 is declared as
resource "aws_instance" "name", notaws_ec2oraws_ec2_instance.- Plaintext Secret Exposure: Avoid setting secret credentials via
export TF_VAR_pass="..."or-var="pass=..."in shared CI runners, as values persist in~/.bash_historyand process inspection trees (ps aux).
Thank you for reading. This article is part of the PaperAstro starter template.