Skip to content
Infrastructure as Code iac variables 4 min read

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 -raw when 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 -json and parse with a tool like jq.

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
}
ArgumentTypePurpose
valueanyThe expression to export (required).
descriptionstringHuman-readable documentation of the output.
sensitiveboolRedacts the value from CLI output.
depends_onlistExplicit resource dependencies.
ephemeralboolExcludes the value from state (1.10+ / OpenTofu 1.7+).

Best Practices

  • Always add a description to 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 -raw and -json for 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 ephemeral outputs 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.
Last updated June 14, 2026
Was this helpful?