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
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 asmodule.vpc.vpc_id. Useterraform output,-raw(string/number/bool), or-json.sensitive = trueredacts CLI text but still stores the value in state.ephemeral = trueomits 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:
- A child module exposes selected attributes to its parent.
- A root module prints values on the CLI after apply.
- Another configuration reads those root outputs through
data "terraform_remote_state"(including HCP Terraform state sharing). - 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
| Argument | Required? | Default | Exam meaning |
|---|---|---|---|
value | Yes | none | Any expression. This is the only required argument. Terraform evaluates it and (unless ephemeral) stores the result in state. |
description | No | none | Written for the consumer: what the value is and how to use it. |
sensitive | No | false | Redacts the value in plan and apply CLI output as (sensitive value). Still stored in state. |
ephemeral | No | false | Terraform 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_on | No | none | Explicit dependency list when the value expression does not already reference the object that must finish first. |
precondition | No | none | Optional 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
| Invocation | What you get |
|---|---|
terraform output | Human-readable dump of every root output. Sensitive names print as <sensitive>. |
terraform output NAME | That 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 NAME | The 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 NAME | Stable 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
valuemust 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
valueis required. An output with onlydescriptionis invalid.module.vpc.vpc_idis a child output.module.vpc.aws_vpc.this.idis not visible to the parent.terraform outputdoes not walk into child modules.-rawrejects objects and lists; use-json.sensitivedoes not remove the value from state.ephemeraldoes — and you cannot putephemeralon a root output.-jsonand-rawprint secrets in clear text.
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?
You mark a root output database_password as sensitive = true. Which statement is true on Terraform 1.12?
Which combination correctly describes terraform output and ephemeral outputs on Terraform 1.12?