| Tested on | Ubuntu 26.04 LTS (Resolute Raccoon) |
|---|---|
| Package | terraform 1.15.8-1 |
| Applies to | Any host with Terraform installed |
| Lab environment | Single Ubuntu VM with Terraform — Terraform lab environment on Ubuntu |
| Privilege | Normal user; sudo only if Terraform is not installed yet |
| Scope | Terraform output blocks, resource attribute exports, description, sensitive, terraform output command, named output, -raw, -json, jq, complex values, outputs in state, shell automation, update and destroy behavior, and common output errors. Does not cover module output design, remote state consumption, HCP outputs, or secrets management depth. |
| Related guides | Terraform variables Terraform resources Terraform state terraform apply command Terraform Associate certification course |
After terraform apply creates infrastructure, you often need a value back: a hostname, an ID, or a generated string. An output block publishes that value from your configuration:
output "message" {
value = terraform_data.example.output
}The value expression can reference any resource attribute, local value, or input variable. Root-module outputs are what you read with the terraform output CLI command. Child modules use the same block syntax to expose values to their caller; module wiring is a separate topic.
~/terraform-labs/terraform-output/ on the Terraform lab environment on Ubuntu. Run terraform init in each subdirectory before apply. Examples use the built-in terraform_data resource so you do not need cloud credentials.
Terraform output values and block syntax
An output is the outbound half of a module interface. Input variables flow in; outputs flow out. In a root module, outputs make selected values easy to query without opening Terraform state manually.
| Argument | Purpose |
|---|---|
value |
Required expression Terraform evaluates and exports |
type |
Optional type constraint for the output value |
description |
Documents what the output contains and how consumers should use it |
sensitive |
Redacts the value in normal plan/apply output |
ephemeral |
Child modules only; omits the value from state and plan files |
depends_on |
Rare explicit dependency when value cannot express it |
deprecated |
Child modules only; warns module consumers to use another output |
precondition |
Validates a condition before Terraform exposes/stores the output |
ephemeral is available for child-module outputs only. It prevents the value from being written to state or plan files and restricts where that value can be consumed. Root-module outputs cannot be ephemeral.
A minimal block pairs a name with a value and optional description:
output "result" {
description = "Short string for -raw automation demos"
value = "lab-ok"
}Optional type constrains the output value. For full type-system rules, see Terraform data types:
output "result" {
type = string
description = "Result returned to module consumers"
value = "lab-ok"
}Write descriptions for humans and automation maintainers: what the value is, not just that it exists. Avoid description = "The result output."
depends_on inside an output block is uncommon. Terraform usually infers dependencies from the value expression. Reserve explicit depends_on for side-effect ordering that attribute references cannot capture.
precondition blocks validate an output before Terraform stores it, similar to variable validation. Full custom condition patterns live in the Terraform validation and check blocks lesson; outputs only need a short example here:
output "message" {
value = terraform_data.example.output
precondition {
condition = terraform_data.example.output != ""
error_message = "message output must not be empty after apply."
}
}Expose resource attributes with apply
The main lab at ~/terraform-labs/terraform-output/main/ ties several outputs to one terraform_data resource:
terraform {
required_version = ">= 1.12.0"
}
resource "terraform_data" "example" {
input = "hello from terraform_data"
}
output "message" {
description = "Echo of the terraform_data input after apply"
value = terraform_data.example.output
}
output "result" {
description = "Short string for -raw automation demos"
value = "lab-ok"
}
output "tags" {
description = "Map output for JSON demos"
value = {
env = "lab"
role = "demo"
}
}
output "subnet_ids" {
description = "List output for JSON demos"
value = ["subnet-a", "subnet-b"]
}
output "api_token" {
description = "Sensitive credential placeholder"
value = "s3cr3t-token"
sensitive = true
}Initialize the working directory:
cd ~/terraform-labs/terraform-output/main && terraform initApply the configuration so Terraform creates the resource and records outputs in state:
cd ~/terraform-labs/terraform-output/main && terraform apply -auto-approve -input=falseThe apply footer lists every output. message echoes the resource result, api_token is redacted, and complex types print as HCL collections:
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
api_token = <sensitive>
message = "hello from terraform_data"
result = "lab-ok"
subnet_ids = [
"subnet-a",
"subnet-b",
]
tags = {
"env" = "lab"
"role" = "demo"
}message reads terraform_data.example.output, so the output tracks the resource attribute rather than a hardcoded string.
Read outputs with the terraform output command
Once state contains outputs, the terraform output command reads them without another apply.
List every output in HCL format:
cd ~/terraform-labs/terraform-output/main && terraform outputapi_token = <sensitive>
message = "hello from terraform_data"
result = "lab-ok"
subnet_ids = [
"subnet-a",
"subnet-b",
]
tags = {
"env" = "lab"
"role" = "demo"
}Request one output by name. Terraform prints the value in HCL syntax, so strings include quotes:
cd ~/terraform-labs/terraform-output/main && terraform output result"lab-ok"For shell scripts, -raw strips quotes and formatting. It accepts only strings, numbers, and booleans:
cd ~/terraform-labs/terraform-output/main && terraform output -raw resultlab-okThat unquoted value is what you want in VALUE=$(terraform output -raw result).
Machine-readable output uses -json. With no output name, Terraform wraps each value in metadata:
cd ~/terraform-labs/terraform-output/main && terraform output -json | jq .{
"api_token": {
"sensitive": true,
"type": "string",
"value": "s3cr3t-token"
},
"message": {
"sensitive": false,
"type": "string",
"value": "hello from terraform_data"
},
"result": {
"sensitive": false,
"type": "string",
"value": "lab-ok"
},
"subnet_ids": {
"sensitive": false,
"type": [
"tuple",
[
"string",
"string"
]
],
"value": [
"subnet-a",
"subnet-b"
]
},
"tags": {
"sensitive": false,
"type": [
"object",
{
"env": "string",
"role": "string"
}
],
"value": {
"env": "lab",
"role": "demo"
}
}
}Query a single complex output by name. Terraform returns the value JSON directly, so jq paths are shorter:
cd ~/terraform-labs/terraform-output/main && terraform output -json tags | jq -r '.env'labFor the full metadata envelope, parse the named key from terraform output -json without a trailing argument: jq -r '.tags.value.env'.
Sensitive and complex output values
Mark credentials and tokens sensitive = true so plan and apply summaries hide them:
output "api_token" {
description = "Sensitive credential placeholder"
value = "s3cr3t-token"
sensitive = true
}During apply, api_token = <sensitive> appears in the footer. The default terraform output listing also redacts that name.
When you request a sensitive output by name, Terraform 1.15.8 prints the real value:
cd ~/terraform-labs/terraform-output/main && terraform output api_token"s3cr3t-token"The same value appears in JSON and raw form:
cd ~/terraform-labs/terraform-output/main && terraform output -json api_token"s3cr3t-token"-raw strips quotes the same way as for non-sensitive strings:
cd ~/terraform-labs/terraform-output/main && terraform output -raw api_tokens3cr3t-tokensensitive = true hides values from casual plan and apply logs, not from state or from explicit terraform output queries. Anyone with state access or shell access to your workspace can read the value. For broader secret handling, see Terraform sensitive data.
-raw cannot export maps or lists. The raw-complex error lab shows the failure:
cd ~/terraform-labs/terraform-output/errors/raw-complex && terraform output -raw tagsError: Unsupported value for raw output
The -raw option only supports strings, numbers, and boolean values, but
output value "tags" is object.
Use the -json option for machine-readable representations of output values
that have complex types.Use -json plus jq for maps, lists, and objects instead.
Outputs in state, after updates, and after destroy
Terraform writes output values into the state file at apply time. That persistence is what makes terraform output work on later runs without re-creating resources.
When the underlying resource changes, outputs that reference it update on the next apply. The update/ lab starts with input = "version-1":
cd ~/terraform-labs/terraform-output/update && terraform apply -auto-approve -input=falseRead the stored output before you change the resource:
cd ~/terraform-labs/terraform-output/update && terraform output message"version-1"Change the resource input to "version-2" in main.tf, apply again, and re-query:
cd ~/terraform-labs/terraform-output/update && terraform output message"version-2"The output tracked the in-place resource update.
After terraform destroy removes all managed resources, outputs disappear from state. From main/:
cd ~/terraform-labs/terraform-output/main && terraform destroy -auto-approve -input=falseThen query outputs:
cd ~/terraform-labs/terraform-output/main && terraform outputWarning: No outputs found
The state file either has no outputs defined, or all the defined outputs are
empty. Please define an output in your configuration with the `output`
keyword and run `terraform refresh` for it to become available.Re-run apply when you need outputs again.
Shell automation and output vs input
Capture a primitive string for a script without parsing HCL quotes:
VALUE=$(cd ~/terraform-labs/terraform-output/main && terraform output -raw result) && echo "$VALUE"lab-okKeep automation narrow: one -raw call per primitive value, check exit codes, and avoid logging sensitive output names in shared CI logs.
Compare the three value mechanisms at a glance:
| Mechanism | Direction | Syntax | Set from CLI |
|---|---|---|---|
| Variable | Input | var.name |
Yes (-var, tfvars, TF_VAR_*) |
| Local | Internal | local.name |
No |
| Output | Export | terraform output name |
No (read after apply) |
Common Terraform output errors
Reproduce each failure in an isolated subdirectory under ~/terraform-labs/terraform-output/errors/.
Output name not found
After apply, requesting an undefined output name fails:
cd ~/terraform-labs/terraform-output/errors/not-found && terraform output does_not_existError: Output "does_not_exist" not found
The output variable requested could not be found in the state file. If you
recently added this to your configuration, be sure to run `terraform apply`,
since the state won't be updated with new output variables until that command
is run.No state or no applied outputs
Before the first apply, state has nothing to read:
cd ~/terraform-labs/terraform-output/errors/no-state && terraform outputWarning: No outputs foundInvalid resource reference in value
An output value must reference declared objects. validate catches a bad reference before apply:
cd ~/terraform-labs/terraform-output/errors/bad-reference && terraform validateError: Reference to undeclared resource
on main.tf line 4, in output "broken":
4: value = nonexistent_resource.foo.id
A managed resource "nonexistent_resource" "foo" has not been declared in the
root module.Quick troubleshooting reference
| Symptom | Likely cause | Fix |
|---|---|---|
Output "name" not found |
Typo or output added after last apply | Match the block label; run terraform apply |
Warning: No outputs found |
No state yet or post-destroy | Apply configuration first |
Unsupported value for raw output |
-raw used on map/list/object |
Use terraform output -json and jq |
Reference to undeclared resource |
value points at missing resource |
Fix the expression or add the resource |
| Sensitive value in automation logs | Named terraform output reveals secrets |
Restrict state access; avoid echoing sensitive names |
References
- Output Values — Terraform documentation
- Output block reference — Terraform documentation
- terraform output command — Terraform CLI
- Manage sensitive data in Terraform
Summary
You declared output blocks that export resource attributes, static strings, collections, and sensitive values from a root module. After apply, Terraform prints those values in the command footer and stores them in state, which is why terraform output works on later runs without touching cloud APIs again.
The CLI offers three reading modes: plain terraform output for human inspection, -raw for primitive shell capture, and -json with jq for maps and lists. Sensitive outputs are redacted in apply summaries but still retrievable by name on Terraform 1.15.8, and they remain in state until you destroy the stack.
When a plan changes a referenced resource, re-apply updates the stored output. After destroy, outputs vanish from state and terraform output warns that nothing is available. Next, pair outputs with Terraform variables for inputs and Terraform locals for internal derived values, or continue to Terraform data types when output type constraints matter.

