100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
HCL

Outputs and Locals

Output values expose data from a module for use by callers or the CLI, while local values let you name and reuse computed expressions within a module. Together they make configurations readable and composable.

HCL Language BasicsBeginner7 min readJul 9, 2026
Analogies

Outputs and Locals

Outputs and locals solve two different but complementary problems. Output values are how a module communicates data outward — a child module exposes an output so its caller (the root module or a parent module) can use that value, and a root module's outputs are what terraform output prints to the CLI or what remote state data sources read from a different configuration. Local values, in contrast, are purely internal: they let you assign a name to an expression so it can be reused within the same module without repeating logic, improving readability and reducing duplication.

🏏

Cricket analogy: A junior academy's scouting report is an output — it hands a promising batsman's stats outward to the national selectors — while a coach's private shorthand notes on 'form rating' used only within that academy's own meetings are purely internal, like a local value.

Declaring Outputs

An output block takes one label (its name) and typically a value argument holding an expression. Outputs can also carry a description, a sensitive flag to redact the value from CLI output (though, like sensitive variables, this does not encrypt it in state), and a depends_on argument for the rare cases where Terraform can't infer the dependency automatically.

🏏

Cricket analogy: A player-of-the-match announcement carries one label and a value — the chosen name — plus an optional note explaining the choice, and a broadcaster can mark an injury detail as 'sensitive' to withhold from the live feed until confirmed, without hiding it from the medical team.

hcl
output "vpc_id" {
  description = "ID of the created VPC"
  value       = aws_vpc.main.id
}

output "db_password" {
  description = "Generated database password"
  value       = random_password.db.result
  sensitive   = true
}

locals {
  name_prefix = "${var.project}-${var.environment}"
  common_tags = {
    Project     = var.project
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_s3_bucket" "data" {
  bucket = "${local.name_prefix}-data"
  tags   = local.common_tags
}

Locals: Naming Computed Values

A locals block can define any number of named values in one place, and each local can reference variables, resource attributes, or even other locals declared earlier. This is especially useful for values that are computed once and reused several times — like a naming prefix, a merged tags map, or a conditional expression — because it avoids repeating the same expression (and risking inconsistency) across multiple resource blocks. Unlike variables, locals cannot be set by the caller; they are computed entirely within the module.

🏏

Cricket analogy: A team's analyst maintains a single sheet of named values — average partnership runs, a merged fielding-position map, a form-adjusted rating — computed once and reused across every pre-match briefing instead of recalculating from raw scores each time, unlike player stats the board itself supplies.

A useful mental model: variables are a module's inputs, outputs are a module's outputs, and locals are a module's private scratch space — internal working values that never cross the module boundary in either direction.

Overusing locals for values that are only ever used once can make configuration harder to read by adding an unnecessary layer of indirection. Reach for a local when a value is reused multiple times or when naming it clarifies intent — not reflexively for every expression.

When you call a child module, its outputs become accessible in the parent as module.<name>.<output>. This is the primary mechanism for passing data between modules — for example, a networking module might output subnet IDs that a compute module then consumes as an input variable. At the root level, outputs are also what a terraform_remote_state data source reads when one Terraform configuration needs to consume values produced by a separate, independently-applied configuration.

🏏

Cricket analogy: A fielding coach's drill outputs a player's reaction-time score, which the head coach then consumes as an input when picking the slip cordon — the same way a networking module's subnet ID output feeds directly into a compute module's input variable.

  • Output blocks expose values outward: from a child module to its caller, or from a root module to the CLI/remote state readers.
  • Local values name an expression for internal reuse within the same module; they are not settable by callers.
  • Child module outputs are accessed in the parent as module.<name>.<output>.
  • sensitive = true on an output redacts CLI display but does not encrypt the value in state.
  • Locals can reference variables, resource attributes, and other locals declared earlier.
  • Overusing locals for single-use values adds unnecessary indirection; use them when a value is reused or naming aids clarity.

Practice what you learned

Was this page helpful?

Topics covered

#HCL#TerraformInfrastructureAsCodeStudyNotes#DevOps#OutputsAndLocals#Outputs#Locals#Declaring#Naming#StudyNotes#SkillVeris