Outputs
Outputs expose values from a configuration or module — an instance's IP, a VPC's ID, a database endpoint — for humans to read after apply and for other modules and stacks to consume. They are the public return values of a module: the deliberate surface other code depends on, as opposed to its internal resources.
The mental model that matters is encapsulation. A module's resources are private. The only way anything outside the module can read a value from inside it is an output you declared. That makes the set of outputs a contract — and a contract you should keep small, because every output is something a consumer can come to depend on and you can no longer freely change.
Declaring an Output
An output block names a value and binds it to an expression — usually a resource attribute. After apply, Terraform prints the outputs, and you can query any one with terraform output vpc_id. The block below publishes a VPC's ID and a load balancer's DNS name for downstream use.
output "vpc_id" { value = aws_vpc.main.id description = "ID of the application VPC" } output "alb_dns_name" { value = aws_lb.app.dns_name description = "Public DNS name of the load balancer" }
Outputs as a Module's Interface
When you call a child module, its outputs are the only thing you can read from it — module.network.vpc_id works because vpc_id is a declared output, while module.network.aws_vpc.main.id does not, because the resource is private to the module. This is why a module without outputs is a black box: it does its work, but the parent can't wire anything to it.
module "network" { source = "./modules/network" cidr = "10.0.0.0/16" } resource "aws_instance" "app" { ami = "ami-0abc123" subnet_id = module.network.private_subnet_id }
Sensitive Outputs
Marking an output sensitive = true suppresses its value from the CLI display after apply, so a generated password or token does not print into a terminal or a retained CI log. As with sensitive variables, this controls display only — the value still lives in state, and a consumer that references the output still receives the real value. Forgetting sensitive on a secret output is a direct way to leak it into searchable logs.
output "db_password" { value = aws_db_instance.main.password sensitive = true }
Outputs for Machines
terraform output prints human-formatted text; terraform output -json prints structured JSON that a script can parse reliably. Anything programmatic — a CI step reading an ARN, a wrapper feeding instance IPs into an Ansible inventory — should consume the JSON form. Parsing the human text breaks the first time the display format changes, and it changes between versions.
# reliable: structured JSON for scripts terraform output -json > outputs.json # a single value, raw, for shell use BUCKET=$(terraform output -raw bucket_name)
Cross-Stack Outputs
An output in one configuration often becomes an input to another. The tight-coupling route is terraform_remote_state, which reads another stack's outputs directly and creates a hidden ordering dependency. The looser route is publishing the value to SSM Parameter Store and reading it as a data source, which decouples the two stacks so the producer can move or restructure without breaking the consumer.
- Trying to reference
module.x.aws_instance.y.idfrom the parent — a module's resources are private, and only declared outputs are reachable. - Printing a secret as a non-sensitive output, leaking it into a CI log that is retained and searchable long after the run.
- Exposing far more outputs than any consumer needs, turning module internals into a brittle public contract you can't change later.
- Parsing human-formatted
terraform outputtext in a script, which breaks the moment the display format shifts between versions. - Coupling two stacks with
terraform_remote_statewhen SSM Parameter Store would have kept them independent.
- Treat outputs as a deliberate, minimal public interface — output what consumers need and nothing internal.
- Mark every secret output
sensitive = trueso it stays out of terminal and CI-log display. - Use
terraform output -json(or-rawfor a single value) for any programmatic consumption. - Give each output a
description, since it documents the contract a downstream consumer reads. - Publish cross-stack values through SSM Parameter Store rather than
terraform_remote_stateto keep the two configurations loosely coupled.
Knowledge Check
From the parent, you need a value computed inside a child module. How do you get it?
- Declare an output in the child and read
module.name.output— only outputs are exposed - Reference the child's resource attribute directly as
module.name.aws_x.yfrom the parent - Read the child module's own
terraform.tfstatefile directly from the parent - Pass the computed value back up through a shared input variable
What does sensitive = true on an output change?
- It suppresses the value from CLI display; the value still lives in state and reaches consumers
- It encrypts the value so it never appears in the state file in plaintext
- It blocks other modules and stacks from consuming the output entirely
- It prevents the output from being queried with the
terraform outputcommand at all from then on
Why should a CI script read outputs with terraform output -json rather than the default text?
- The JSON form is stable to parse; the human text format can change between versions and break the script
- The JSON form is the only output format that includes the sensitive values a CI script needs
- The default text form is not available at all in automation, only when running interactively
- JSON output skips the state refresh step entirely and is therefore meaningfully faster to run in a pipeline
What is the trade-off of reading another stack's outputs via terraform_remote_state instead of SSM Parameter Store?
- It couples the two configs tightly, creating a hidden dependency that breaks if the upstream state moves
- It cannot read any upstream output that has been marked as sensitive in the producer stack's configuration
- It requires both the producer and consumer stacks to share a single state file
- It only works when both stacks live within a single AWS region
You got correct