Terraform Cheatsheet
Outputs
Use this Terraform reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Declaring Outputs
# outputs.tf output "instance_public_ip" { description = "Public IP of the web server" value = aws_instance.web.public_ip } output "db_endpoint" { value = aws_db_instance.main.endpoint sensitive = true }
| Argument | Required | Purpose | Since |
|---|---|---|---|
value | yes | the expression to expose | 0.x |
type | no | type constraint on the value | 1.15 |
description | no | documents the output, surfaced by the registry | 0.x |
sensitive | no | redact from CLI output | 0.14 |
depends_on | no | explicit ordering, rare | 0.x |
precondition | no | assert before the value is finalised | 1.2 |
ephemeral | no | never written to state, module-only | 1.10 |
deprecated | no | warn when a caller references it, child modules only | 1.15 |
Outputs serve two distinct jobs. In the root module they are the CLI and scripting surface, and they are recorded in state. In a child module they are the only values a parent can read, since a parent cannot reach inside a module to a resource attribute.
Reading Outputs
terraform output # all outputs, HCL-ish format terraform output instance_public_ip # one output, quoted terraform output -raw instance_public_ip # bare string, no quotes terraform output -json # every output with type and sensitivity terraform output -json | jq -r '.db_endpoint.value'
-raw only works on a string, number or bool. For anything structured use -json and pipe through jq.
# Feed a value into another tool DB_HOST=$(terraform output -raw db_endpoint) kubectl create secret generic db --from-literal=host="$DB_HOST" # Export every scalar output as an environment variable eval "$(terraform output -json \ | jq -r 'to_entries[] | select(.value.value | type == "string") | "export TF_OUT_\(.key | ascii_upcase)=\(.value.value | @sh)")"
terraform output reads state, not config, so it returns the last applied values and needs no provider credentials. It errors if you have never applied.
Outputs From count and for_each Resources
resource "aws_instance" "worker" { count = 3 ami = var.ami_id } output "worker_ips" { value = aws_instance.worker[*].private_ip # list, splat } output "first_worker_ip" { value = aws_instance.worker[0].private_ip # errors if count = 0 } output "maybe_worker_ip" { value = one(aws_instance.worker[*].private_ip) # null when count = 0 }
resource "aws_instance" "app" { for_each = toset(["web", "api", "worker"]) ami = var.ami_id } output "instance_ids" { value = { for k, v in aws_instance.app : k => v.id } } # => { api = "i-...", web = "i-...", worker = "i-..." }
Prefer a map output over a list whenever the underlying resource uses for_each. A list output reorders when a key is added, which churns every downstream consumer for no reason.
Sensitive Outputs
output "db_password" { value = random_password.db.result sensitive = true }
- Printed as
(sensitive value)byterraform outputand in apply summaries. - Still readable with
terraform output -jsonor-raw, deliberately, because scripts need it. - Still stored in state in plaintext.
sensitiveis a display control, not encryption. - Sensitivity propagates. An output whose value derives from a sensitive input is sensitive whether you said so or not, and Terraform errors if you expose one without the marker. Use
nonsensitive(x)only when you have checked that the specific value is safe.
# Ephemeral output (1.10+): a child module may return a secret that # never reaches state. Root modules cannot declare one. output "session_token" { value = ephemeral.vault_kv_secret_v2.db.data["token"] ephemeral = true sensitive = true }
Preconditions on Outputs
output "api_url" { value = "https://${aws_lb.api.dns_name}" precondition { condition = aws_lb.api.load_balancer_type == "application" error_message = "api_url assumes an ALB, but this is not one." } }
The precondition is checked while the output is finalised, so it fails the apply rather than handing a caller a wrong value. Unlike a resource postcondition, there is no self here, so reference the resource by address.
Consuming Outputs Across Modules
# modules/vpc/outputs.tf output "vpc_id" { value = aws_vpc.main.id } output "public_subnet_ids" { value = aws_subnet.public[*].id }
# root main.tf module "vpc" { source = "./modules/vpc" } resource "aws_instance" "web" { subnet_id = module.vpc.public_subnet_ids[0] # module.<name>.<output> } output "vpc_id" { value = module.vpc.vpc_id # re-export upward }
With for_each on the module call, outputs are keyed the same way:
module "bucket" { for_each = toset(["logs", "backups"]) source = "./modules/s3-bucket" name = each.key } output "bucket_arns" { value = { for k, m in module.bucket : k => m.bucket_arn } }
Only declared outputs cross the boundary. If a parent needs an attribute, the module must export it, which is why a well-designed module exports IDs, ARNs and names rather than whole resource objects.
Reading Another Root Module's Outputs
Two ways to consume outputs from a separate state file.
# 1. terraform_remote_state: reads the state file directly. # Needs read access to the state backend, and sees every output. data "terraform_remote_state" "network" { backend = "s3" config = { bucket = "my-tf-state" key = "shared/network/terraform.tfstate" region = "us-east-1" } } resource "aws_instance" "app" { subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0] }
# 2. tfe_outputs (HCP Terraform): reads only the published outputs, # with normal workspace permissions and no state access. data "tfe_outputs" "network" { organization = "my-org" workspace = "network-prod" }
terraform_remote_state | published values (tfe_outputs, SSM, etc.) | |
|---|---|---|
| Grants | full state read, including other teams' secrets | just the outputs |
| Coupling | tight, breaks on a backend move | loose |
| Works anywhere | yes | needs HCP Terraform or a parameter store |
The looser pattern is to publish outputs deliberately, for example writing them to SSM Parameter Store, and read them back with a normal data source. That keeps one team's state out of another team's blast radius.