Output Values
Output values are how a Terraform configuration returns information about the infrastructure it built. After an apply, you almost always need to know something concrete — the public IP of an instance, the ARN of a queue, the DNS name of a load balancer — and outputs are the structured, supported way to surface those values. They also form the public interface of a module: a child module exposes outputs, and a parent module consumes them. Outputs in OpenTofu behave identically, so everything below applies to both tools.
Declaring an output block
An output is declared with an output block. Each block has a name and a value argument containing the expression to export. The expression can reference resource attributes, variables, locals, or data sources.
resource "aws_instance" "web" {
ami = "ami-0c7217cdde317cfec"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
output "instance_public_ip" {
description = "Public IP address of the web server"
value = aws_instance.web.public_ip
}
output "instance_id" {
description = "EC2 instance identifier"
value = aws_instance.web.id
}
After terraform apply, the values print at the end of the run:
Output:
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
instance_id = "i-0a1b2c3d4e5f6a7b8"
instance_public_ip = "203.0.113.42"
The description argument is optional but strongly recommended — it documents the output for consumers and shows up in generated docs.
Reading outputs after apply
Terraform stores output values in state, so you can retrieve them at any time without re-running apply. Use terraform output to list every output, or pass a name for a single value.
terraform output
terraform output instance_public_ip
For scripting, the -raw flag prints a single string value with no quotes or formatting, and -json emits machine-readable JSON for any output type.
terraform output -raw instance_public_ip
terraform output -json
Output:
$ terraform output -raw instance_public_ip
203.0.113.42
$ terraform output -json
{
"instance_id": {
"sensitive": false,
"type": "string",
"value": "i-0a1b2c3d4e5f6a7b8"
},
"instance_public_ip": {
"sensitive": false,
"type": "string",
"value": "203.0.113.42"
}
}
Use
-rawwhen feeding an output into another command, e.g.ssh ec2-user@$(terraform output -raw instance_public_ip). It only works for primitive string, number, and bool values — for lists or maps, use-jsonand parse with a tool likejq.
Output dependencies
Because an output references a resource attribute, Terraform automatically infers a dependency on that resource and resolves the value only after the resource exists. When a value does not naturally reference the resource you must wait for, declare the relationship explicitly with depends_on.
output "endpoint" {
description = "API endpoint, available only after the gateway deploys"
value = aws_api_gateway_deployment.api.invoke_url
depends_on = [aws_api_gateway_stage.prod]
}
This is rarely needed — implicit dependencies through the expression cover almost every case — but it is the correct escape hatch when an output must not be returned until a side-effecting resource has settled.
Module outputs
Outputs are the only way a child module exposes data to its caller. Resources inside a module are otherwise completely encapsulated. Declare outputs in the module, then reference them from the parent as module.<name>.<output>.
# modules/network/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr_block
}
output "vpc_id" {
description = "ID of the created VPC"
value = aws_vpc.this.id
}
# root main.tf
module "network" {
source = "./modules/network"
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "app" {
vpc_id = module.network.vpc_id
cidr_block = "10.0.1.0/24"
}
Only outputs declared at the root module appear in terraform output and in the CLI summary. Outputs of nested modules are internal plumbing and are not surfaced unless the root re-exports them.
Sensitive and ephemeral outputs
Mark secret values with sensitive = true so Terraform redacts them in plan, apply, and terraform output (without an explicit name). The value is still written to state in plaintext, so protect your backend accordingly. Terraform 1.10+ adds ephemeral = true for values that should pass between configurations without ever being persisted to state.
output "db_password" {
description = "Generated database password"
value = random_password.db.result
sensitive = true
}
| Argument | Type | Purpose |
|---|---|---|
value | any | The expression to export (required). |
description | string | Human-readable documentation of the output. |
sensitive | bool | Redacts the value from CLI output. |
depends_on | list | Explicit resource dependencies. |
ephemeral | bool | Excludes the value from state (1.10+ / OpenTofu 1.7+). |
Best Practices
- Always add a
descriptionto every output — it doubles as module API documentation. - Export only what callers genuinely need; treat outputs as the deliberate public interface of a module rather than a dump of every attribute.
- Use
terraform output -rawand-jsonfor automation instead of scraping the human-readable summary, which is not a stable format. - Mark any credential, token, or key as
sensitive, and remember the value still lives in state. - Prefer
ephemeraloutputs for short-lived secrets that must move between configurations without being stored. - Group related values into a single object output when a consumer needs several attributes together, reducing coupling to individual resource names.