Terraform

Mastering Terraform Module Outputs: Practical outputs Patterns for Associate

2026-04-19
NicheeLab Editorial Team

ハブ記事: Terraform Modules: Complete Guide

Hub article covering the full picture of Terraform modules: design, distribution, and operations

Terraform outputs are the official mechanism for passing values between modules and for surfacing information after a run. Even at the Associate level, declaration, reference syntax, sensitive behavior, and evaluation ordering all show up frequently.

This article covers outputs designs that survive in production and the points exam questions tend to focus on. Where version-dependent behavior is involved, we stick to current stable specifications.

Outputs Basics and Exam-Relevant Points

An output block exposes a value computed inside a module to the outside. A parent module references it as module.<child>.output_name. Root-module outputs are shown after terraform apply and can be re-displayed with the terraform output command.

An output supports value (required), description (optional), sensitive (optional), and, when needed, depends_on (to make evaluation order explicit). Types are inferred and complex types (objects and maps) can be returned.

  • Basics: declare output in the child module; parent references it via module.child.name
  • CLI: terraform output, terraform output -json, terraform output -raw
  • sensitive=true masks on-screen display, but the value is still stored in state, so backend protection is mandatory
  • depends_on lets you make the order of output evaluation explicit (see below)

Minimal output configuration (child and parent)

# Child module: modules/network/outputs.tf
output "vpc_id" {
  value       = aws_vpc.main.id
  description = "ID of the VPC that was created"
}

# Parent module: main.tf
module "network" {
  source = "./modules/network"
}

output "network_vpc_id" {
  value = module.network.vpc_id
}

Designing Data Flow Between Modules

When wiring multiple modules together, the parent receives the child's outputs and re-exports them as needed. To pass values across workspaces, use the terraform_remote_state data source, but keep it loosely coupled and keep the contract (schema) stable.

Output values are finalized only after apply completes. Values that are unknown at plan time can propagate as unknown to the parent through dependency chains. Design defensively with conditionals and null-coalescing so the configuration survives an unknown.

  • Wiring within the same configuration: parent → child: input variables; child → parent: outputs
  • Cross-workspace integration: read outputs through data.terraform_remote_state (with permissions and encryption assumed)
  • Contract stability: keep the type, key names, and semantics fixed and avoid breaking changes

Output flow between parent and child modules

outputs: a_id, alb_dnsoutput: service_urlconsumed (a_id)Root (parent)consumes A,B / re-exportsChild Aoutput a_id, alb_dnsChild Binput a_id / output service_url

Re-export child outputs from the parent / reference another workspace's outputs via remote_state

# Reference child outputs from the parent and re-export them
module "web" { source = "./modules/web" }
module "svc" {
  source = "./modules/service"
  a_id   = module.web.a_id
}

output "alb_dns" {
  value = module.web.alb_dns
}

output "service_url" {
  value = module.svc.service_url
}

# Read outputs from another workspace (Terraform Cloud/Enterprise example)
data "terraform_remote_state" "network" {
  backend = "remote"
  config = {
    organization = "acme"
    workspaces = { name = "network-prod" }
  }
}

# Example of consuming the fetched output
module "app" {
  source = "./modules/app"
  vpc_id = data.terraform_remote_state.network.outputs.vpc_id
}

Build Stable outputs with Types and Collection Operations

Keeping output types stable makes parent-side references less brittle. In particular, when aggregating values from child modules created with for_each, make the order and keys of maps and lists explicit.

Make order explicit for collections without a defined order using sort() or toset()/tolist(), and pin keys with tomap() on objects. Document the contract of object outputs in description with future schema expansion in mind — operations get easier.

  • Aggregate outputs from multiple modules created with for_each as a map or a sorted list
  • Use sort() when order matters and toset() for unique sets
  • Evolve objects with key compatibility in mind: add new keys, never change existing ones

Example: stabilize and return outputs from for_each child modules

# Group of child modules (illustrative)
module "subnet" {
  source = "./modules/subnet"
  for_each = {
    a = { az = "ap-northeast-1a" }
    c = { az = "ap-northeast-1c" }
  }
  az = each.value.az
}

# Return as an order-independent map
output "subnet_ids_by_key" {
  value       = { for k, m in module.subnet : k => m.id }
  description = "Subnet IDs by key (logical names like a/c become the keys)"
}

# Return as a sorted list when order matters
output "subnet_ids_sorted" {
  value       = sort([for k, m in module.subnet : m.id])
  description = "Subnet IDs in stable sorted order"
}

# Make the contract explicit via an object
output "network_contract" {
  value = {
    vpc_id      = module.network.vpc_id
    subnet_ids  = sort([for k, m in module.subnet : m.id])
    region      = var.region
  }
  description = "Network contract exposed upward (extend by adding keys in the future)"
}

Secure Outputs: Handling sensitive and state

Setting sensitive=true hides the value in CLI output and the UI. However, Terraform state stores the actual value, so backend encryption, RBAC, and least privilege are mandatory. Designing to avoid emitting sensitive values is the first priority; when emission is unavoidable, minimize the visibility surface.

Sensitivity propagates through expressions. Computed results that include a sensitive value automatically become sensitive, and you only strip the flag with nonsensitive() when truly needed (watch out for leaks via display and logs). In the CLI, terraform output hides sensitive outputs by default. When automation needs the value, use -json or -raw only from a secure execution environment.

  • Rule: do not put passwords or secrets into outputs
  • When exceptions are needed: combine sensitive=true with backend protection and thorough auditing of the read path
  • Use nonsensitive() as little as possible and forbid side effects such as logging
TargetDisplay behavior (CLI/UI)Persisted in stateTypical use
output (sensitive=false)Displayed in plain textActual value is storedEndpoint URL, ID, DNS name, etc.
output (sensitive=true)Masked or hidden by defaultActual value is stored (must be protected)Minimal secrets (when unavoidable)
variable (sensitive=true)Masked in plan/apply logsMay end up in state (when reflected on resource attributes)Hiding input (for example, when passing an external secret)

Choosing between sensitive and nonsensitive

# Case where the child module must return a sensitive value
output "db_password" {
  value     = random_password.db.result
  sensitive = true
  description = "DB password (masked on display)"
}

# Re-export only part of it from the parent (avoid in principle)
output "db_connection_info" {
  value = {
    endpoint = module.db.endpoint
    username = var.db_user
    // Do not emit the password; if you must, set sensitive=true
  }
}

# Recompute only the publicly safe portion as non-sensitive
output "public_url" {
  value = nonsensitive(module.web.public_url)
  description = "Public URL (contains no secrets)"
}

Controlling Output Evaluation Order with depends_on

Normally, an output's dependencies are resolved automatically from the value expression. Occasionally, when you want to evaluate an output only after a side-effecting prerequisite is satisfied (for example, waiting for a completion signal from an auxiliary module), you can add depends_on to the output to make the order explicit.

depends_on on an output is not for changing resource creation order directly; it guarantees that output evaluation waits for a specific completion.

  • Add to depends_on any target you need to wait for but do not reference in value
  • Control the timing of apply outputs to stabilize downstream automation

Example: explicitly delay output evaluation

# Emit true only after app and db both finish
output "bootstrap_complete" {
  value      = true
  depends_on = [module.app, module.db]
  description = "Completion signal for the main modules"
}

# Another example: surface the endpoint only after the secret is issued
output "service_endpoint_ready" {
  value      = module.svc.endpoint
  depends_on = [aws_secretsmanager_secret_version.api_token]
}

Refactoring While Preserving Compatibility

Renaming an output or changing its type breaks the parent module. Treat outputs as the module's public interface and keep names, types, and meaning stable. For deprecation, the safe path is to keep the old output around for a while and clearly mark it in description.

When breaking changes are unavoidable, cut a new major version tag and provide updated CHANGELOG, samples, and migration instructions for module consumers.

  • Prioritize compatibility for output names and types (change them only as a last resort)
  • Keep deprecated outputs around for a transition period and clearly point to the replacement
  • Contract tests: run terraform validate / terraform plan -json in CI to catch breakage

Example: keep a deprecated output alongside the new one

# Old: base_url is deprecated. New: use service_url
output "base_url" {
  value       = module.web.public_url
  description = "[DEPRECATED] Use service_url instead"
}

output "service_url" {
  value       = module.web.public_url
  description = "Public URL of the service (canonical name)"
}

Check Your Understanding

Associate

問題 1

The child module modules/web defines output "alb_dns". Which is the correct way to re-export this value under a different name from the root module?

  1. frontend_dns
  2. frontend_dns
  3. frontend_dns
  4. alb_dns
  5. frontend_dns

正解: A

Within the same configuration, reference values as module.<child_module_name>.<output_name>. terraform_remote_state is used to read outputs from a different workspace.

Frequently Asked Questions

What are the practical terraform output commands?

Use terraform output to list everything, terraform output name for a single value, -json for machine-readable output, and -raw name for a single value without quotes or newlines. Note that sensitive values are hidden by default.

Is it OK to put secrets in outputs?

As a rule, avoid it. If you must, set sensitive=true and enforce backend encryption and access control. Read the value only from least-privileged automation runners, and never log it.

What is the best way to reference values from another workspace?

The common pattern is to use the terraform_remote_state data source under a stable contract while keeping the producer loosely coupled. For organization-wide use, pair it with backend authorization, encryption, and version management.

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Terraform

HCL Syntax: Terraform's Configuration Language (2026)

HCL2 fundamentals for Terraform — blocks, attributes, expres...

Terraform

Terraform Authoring & Operations Pro: Complete Guide (2026)

Tactics for the Terraform Pro exam — module authoring, works...

Terraform

Terraform Providers: Plugin Management Fundamentals (2026)

Provider mechanics — required_providers, versions, mirrors, ...

Terraform

Terraform Resource Blocks: Declarative Infra Units (2026)

Resource block fundamentals — addresses, references, common ...

Terraform

Terraform Data Sources: Read-Only External Data (2026)

Data source basics — declaration, refresh behavior, dependen...

Browse all Terraform articles (102)
© 2026 NicheeLab All rights reserved.