7.2 Output Values

Key Takeaways

  • An output block requires value; description, sensitive, depends_on, and ephemeral are optional on Terraform 1.12
  • Root-module outputs print after apply and appear on the HCP Terraform workspace overview; terraform output only lists those root outputs
  • A parent module can read a child only through the child's output blocks, as module.vpc.vpc_id — never the child's internal resource addresses
  • terraform output -raw prints a string, number, or bool with no quotes; -json is the stable format for objects, lists, and automation
  • sensitive redacts CLI display but still writes the value into state; ephemeral (1.10+) omits state and plan and is legal only on child-module outputs
Last updated: August 2026

7.2 Output Values

Quick Answer: output "NAME" { value = ... } is a return value. Root outputs print after apply and show in the HCP Terraform UI. A parent reads a child only as module.vpc.vpc_id. Use terraform output, -raw (string/number/bool), or -json. sensitive = true redacts CLI text but still stores the value in state. ephemeral = true omits state and plan and is allowed only on child-module outputs.

Objective 4c continues: use variables and outputs. Official pages: Use outputs to expose module data, the output block reference, and terraform output. Product version is Terraform 1.12.

What an output is for

HashiCorp lists four jobs for output blocks:

  1. A child module exposes selected attributes to its parent.
  2. A root module prints values on the CLI after apply.
  3. Another configuration reads those root outputs through data "terraform_remote_state" (including HCP Terraform state sharing).
  4. Automation scrapes values after a run (terraform output -json).

An output is not a resource. It does not create infrastructure. It exports an expression.

output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.web.id
}

output "instance_ip" {
  description = "Private IP address of the EC2 instance"
  value       = aws_instance.web.private_ip
}

After a successful apply, Terraform prints root outputs. In HCP Terraform the workspace overview lists the same values. Child-module outputs do not appear in that list unless the root re-exports them.

Arguments on the Terraform 1.12 output block

ArgumentRequired?DefaultExam meaning
valueYesnoneAny expression. This is the only required argument. Terraform evaluates it and (unless ephemeral) stores the result in state.
descriptionNononeWritten for the consumer: what the value is and how to use it.
sensitiveNofalseRedacts the value in plan and apply CLI output as (sensitive value). Still stored in state.
ephemeralNofalseTerraform 1.10+. Omits the value from state and plan files. Child modules only — you cannot mark a root output ephemeral. The value must come from an ephemeral context and can only be read in ephemeral contexts.
depends_onNononeExplicit dependency list when the value expression does not already reference the object that must finish first.
preconditionNononeOptional check before Terraform exposes the value or writes it to state. Chapter 9 covers the condition language.

Terraform 1.12 does not require a type argument on outputs. Do not invent one for 004.

depends_on on an output is the classic "IP is useless until the security-group rule exists" case. Prefer an implicit reference when you can; if you add depends_on, comment why.

output "instance_ip_addr" {
  value       = aws_instance.server.private_ip
  description = "Private IP of the main server."

  depends_on = [
    aws_security_group_rule.local_access,
  ]
}

Child module outputs are the only values a parent can read

A parent cannot write module.vpc.aws_subnet.private[0].id. That address is private to the child. The child must declare an output, and the parent reads that name:

# modules/vpc/outputs.tf
output "vpc_id" {
  description = "ID of the VPC this module manages"
  value       = aws_vpc.this.id
}

output "private_subnet_ids" {
  description = "Private subnet IDs"
  value       = aws_subnet.private[*].id
}
# root
module "vpc" {
  source = "./modules/vpc"
}

resource "aws_instance" "app" {
  subnet_id = module.vpc.private_subnet_ids[0]
}

output "app_vpc" {
  value       = module.vpc.vpc_id
  description = "Re-export so terraform output and remote state can see it"
}

The reference shape is module.<CHILD_MODULE_NAME>.<OUTPUT_NAME>. That is the same rule section 6.2 stated from the resource-address side. 004 will show module.vpc.aws_vpc.this.id as a distractor. It is not valid in the parent.

terraform output also only shows root outputs. To print a child value on the CLI, re-export it from the root as app_vpc does above.

terraform output, -raw, and -json

InvocationWhat you get
terraform outputHuman-readable dump of every root output. Sensitive names print as <sensitive>.
terraform output NAMEThat one root output. On the 1.12 CLI, naming a sensitive output prints the raw value — redaction is for the listing and for apply logs, not a guarantee on a named lookup.
terraform output -raw NAMEThe value as a bare string, no quotes, no extra whitespace. Works only for values Terraform can convert to a string: string, number, bool. Use this in shell scripts.
terraform output -json / terraform output -json NAMEStable JSON. Required for lists, maps, and objects. Pipe to jq.

-json and -raw always display sensitive values in plain text. That is official, and it is an exam trap: sensitive is not an access-control system. Anyone who can run terraform output -json against the state can read the password.

terraform output -raw lb_address
# my-app-alb-1657023003.us-east-1.elb.amazonaws.com

terraform output -json instance_ips | jq -r '.[0]'
# 54.43.114.12

Do not parse the default human-readable format in CI. HashiCorp says that format can change. -json is the contract.

Ephemeral outputs never appear in terraform output, even by name, because they were never stored in state.

Sensitive versus ephemeral

output "database_password" {
  description = "Auto-generated password for the RDS instance"
  value       = aws_db_instance.main.password
  sensitive   = true
}

After apply you see database_password = (sensitive value). terraform output lists it as <sensitive>. The state file still contains the password. Remote state consumers and anyone with backend access can read it.

ephemeral = true is the other switch. It is for short-lived tokens you must pass between modules without persisting them. Restrictions that 004 can test:

  • Legal only on child-module outputs. A root output cannot be ephemeral (there would be nothing left to print or to store).
  • The value must come from an ephemeral context (ephemeral variable, ephemeral resource, another ephemeral output, write-only-related flow).
  • Consumers can use it only in ephemeral contexts: another ephemeral output, a write-only argument, an ephemeral variable, provider configuration, or a provisioner.

If you need a password in the HCP Terraform UI or in terraform_remote_state, you cannot make that output ephemeral. You mark it sensitive and protect the state backend.

004 traps for outputs

  • value is required. An output with only description is invalid.
  • module.vpc.vpc_id is a child output. module.vpc.aws_vpc.this.id is not visible to the parent.
  • terraform output does not walk into child modules.
  • -raw rejects objects and lists; use -json.
  • sensitive does not remove the value from state. ephemeral does — and you cannot put ephemeral on a root output.
  • -json and -raw print secrets in clear text.
Loading diagram...
How output values leave a module
Test Your Knowledge

A parent calls module "vpc" { source = "./modules/vpc" }. The child manages aws_vpc.this and declares output "vpc_id" { value = aws_vpc.this.id }. How does the parent read the VPC ID?

A
B
C
D
Test Your Knowledge

You mark a root output database_password as sensitive = true. Which statement is true on Terraform 1.12?

A
B
C
D
Test Your Knowledge

Which combination correctly describes terraform output and ephemeral outputs on Terraform 1.12?

A
B
C
D