Terraform Output Values
Learn Terraform output values in depth, including output blocks, sensitive outputs, terraform output commands, module outputs, and CI/CD automation patterns.
What Are Output Values in Terraform?
Terraform output values are named results that Terraform prints after it creates or updates infrastructure. They answer a simple but important question: after Terraform finishes, how do you get back the values you care about?
For example, after creating infrastructure you often want to know:
- the public IP of a server
- the DNS name of a load balancer
- the ARN of an IAM role
- the URL of an application
- the ID of a VPC or subnet
Those values usually come from resources created during terraform apply. Output values make them easy to expose in a clean, reusable way.
A basic example looks like this:
output "instance_public_ip" {
description = "Public IP address of the web server"
value = aws_instance.web.public_ip
}
After terraform apply, Terraform can display the result:
instance_public_ip = "54.12.34.56"
Output values are useful for both humans and automation. A person can quickly see important infrastructure details, while a CI/CD script can read outputs and pass them to later steps.
Why Output Values Matter
It is possible to inspect Terraform state manually, but that is not a good workflow. State is verbose, internal, and often sensitive. Output values give you a small curated interface to the most important results.
They help in several ways:
1. Better usability
Instead of searching through a long plan or cloud console, you can expose exactly what matters.
2. Module communication
Outputs are how child modules publish values back to the root module or to other modules.
3. Automation
Deployment scripts, Ansible playbooks, Kubernetes bootstrap steps, and CI/CD jobs often depend on values generated by Terraform.
4. Documentation through code
A good output block explains which values a module intentionally exposes.
Declaring Output Values with output Blocks
An output is declared with an output block:
output "alb_dns_name" {
description = "DNS name of the application load balancer"
value = aws_lb.app.dns_name
}
Common arguments in an output block
| Argument | Purpose |
|---|---|
value | The expression Terraform should expose |
description | Human-readable explanation of the output |
sensitive | Hides the value from normal terminal output |
value is required. description is strongly recommended. sensitive is optional and useful for secrets.
Simple Output Examples
Resource attribute output
output "bucket_name" {
description = "Name of the S3 bucket used for application logs"
value = aws_s3_bucket.logs.id
}
URL output
output "application_url" {
description = "Base URL for the deployed application"
value = "https://${aws_lb.app.dns_name}"
}
List output
output "public_subnet_ids" {
description = "IDs of public subnets"
value = aws_subnet.public[*].id
}
Map output
output "instance_private_ips" {
description = "Private IPs of application instances keyed by name"
value = {
for instance in aws_instance.app :
instance.tags.Name => instance.private_ip
}
}
Outputs can contain simple values or richer data structures, as long as the expression is valid Terraform.
A Realistic Example
Imagine a small AWS deployment that creates a VPC, subnets, and an application load balancer.
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "Public subnet IDs"
value = aws_subnet.public[*].id
}
output "alb_dns_name" {
description = "Public DNS name of the load balancer"
value = aws_lb.app.dns_name
}
output "application_url" {
description = "URL users should open in a browser"
value = "https://${aws_lb.app.dns_name}"
}
This is much more user-friendly than telling people to inspect state or hunt for resource attributes.
Sensitive Outputs
Some outputs should not be displayed openly in the terminal, especially if they contain secrets.
output "db_password" {
description = "Database password"
value = var.db_password
sensitive = true
}
When Terraform prints outputs after apply, a sensitive output is hidden:
db_password = <sensitive>
Very important warning
Sensitive outputs are hidden in terminal output, but they are not removed from Terraform state. If the value is stored in state by Terraform or by the provider, anyone with state access may still be able to retrieve it.
So sensitive = true improves safety in logs and command output, but it is not a substitute for:
- securing remote state
- restricting access to backend storage
- using secret managers where appropriate
Using the terraform output Command
After you apply a configuration, you can retrieve outputs at any time with the terraform output command.
Show all outputs
terraform output
Example:
alb_dns_name = "app-alb-123456.us-east-1.elb.amazonaws.com"
application_url = "https://app-alb-123456.us-east-1.elb.amazonaws.com"
vpc_id = "vpc-0abc123def456"
This is useful when you want a quick summary of the deployment.
Show a specific output
terraform output alb_dns_name
Result:
app-alb-123456.us-east-1.elb.amazonaws.com
This is convenient in scripts or when you only care about one value.
Use -json for machine-readable output
terraform output -json
Example shape:
{
"alb_dns_name": {
"sensitive": false,
"type": "string",
"value": "app-alb-123456.us-east-1.elb.amazonaws.com"
},
"vpc_id": {
"sensitive": false,
"type": "string",
"value": "vpc-0abc123def456"
}
}
JSON output is the preferred format for automation because scripts can parse it reliably.
Output command examples in practice
Fetch a load balancer URL for testing
terraform output application_url
Save all outputs for another tool
terraform output -json
A script could then parse the JSON and export variables for later pipeline stages.
Read a single output cleanly in shell automation
Many teams use:
terraform output -json application_url
or parse the full JSON with tools available in their environment. The key lesson is that Terraform can provide machine-readable output, which is much better than scraping human-oriented terminal text.
Passing Outputs Between Modules
Outputs are essential in Terraform module design.
A child module exposes values using output blocks, and the parent module reads them with:
module.<module_name>.<output_name>
Child module example
Imagine a module that creates a VPC.
# modules/vpc/outputs.tf
output "vpc_id" {
description = "ID of the created VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "IDs of the public subnets"
value = aws_subnet.public[*].id
}
Root module using those outputs
module "network" {
source = "./modules/vpc"
project_name = var.project_name
environment = var.environment
vpc_cidr = var.vpc_cidr
}
module "app" {
source = "./modules/app"
vpc_id = module.network.vpc_id
subnet_ids = module.network.public_subnet_ids
}
This is how Terraform modules communicate safely and intentionally.
Why outputs matter in module design
A good module does not expose every possible internal detail. It exposes the values that callers actually need.
For a VPC module, useful outputs might include:
- VPC ID
- public subnet IDs
- private subnet IDs
- route table IDs
For an EC2 module, useful outputs might include:
- instance ID
- private IP
- public IP
- security group ID
Outputs define the public contract of the module.
Using Outputs in CI/CD Scripts
Terraform outputs are frequently used in deployment pipelines.
Imagine a pipeline that:
- runs
terraform apply - reads the application URL
- runs smoke tests against the deployed service
Example workflow:
terraform apply -auto-approve
terraform output application_url
A CI/CD job could use the output value to:
- run a health check
- create a deployment annotation
- pass the URL to integration tests
- store the endpoint as a pipeline variable
Example scenario: DNS or app endpoint
Suppose Terraform creates an Application Load Balancer and your test step needs its hostname.
output "application_url" {
description = "Public endpoint for smoke tests"
value = "https://${aws_lb.app.dns_name}"
}
Now your pipeline can read this output and test the environment automatically.
Example scenario: Kubernetes bootstrap
If Terraform provisions an EKS cluster, outputs might provide:
- cluster name
- cluster endpoint
- node security group ID
- OIDC provider ARN
Downstream automation can then configure kubectl, install Helm charts, or update DNS.
Best Practices for Output Values
1. Output what is useful, not everything
Too many outputs make a module noisy. Expose the values callers or operators truly need.
2. Always add descriptions
Descriptions make outputs self-documenting and much easier to understand in code reviews.
3. Mark secrets as sensitive
If an output includes confidential data, set sensitive = true to reduce accidental exposure in logs.
4. Prefer stable, meaningful outputs
Useful outputs are usually IDs, ARNs, names, URLs, or lists of created resources—not every internal attribute.
5. Think of outputs as a module API
Once other code depends on your outputs, changing or removing them can break consumers. Design them intentionally.
Common Beginner Mistakes
Outputting values that should stay internal
Not every resource attribute deserves to be an output. If no one needs it outside the module, leave it internal.
Forgetting to mark secrets as sensitive
If an output contains credentials or secret tokens, always mark it sensitive.
Assuming sensitive outputs are fully secure
Remember: hidden in terminal output does not mean removed from state.
Creating outputs but never using them
Outputs should serve a clear purpose for operators, parent modules, or automation.
A Full Example: Outputs for a Web Stack
output "vpc_id" {
description = "ID of the VPC used by the application"
value = aws_vpc.main.id
}
output "alb_dns_name" {
description = "DNS name of the application load balancer"
value = aws_lb.app.dns_name
}
output "application_url" {
description = "Public HTTPS URL for the application"
value = "https://${aws_lb.app.dns_name}"
}
output "app_security_group_id" {
description = "Security group ID attached to application instances"
value = aws_security_group.app.id
}
output "private_instance_ips" {
description = "Private IP addresses of app servers"
value = aws_instance.app[*].private_ip
}
This gives both humans and automation a concise summary of the deployment.
Final Thoughts
Output values are one of Terraform's simplest features, but they are incredibly useful. They turn raw infrastructure details into an intentional interface. For beginners, they are the easiest way to answer questions like, "What did Terraform just create for me?" For teams, they are the glue that connects modules and automation.
A practical summary is:
- Resources create infrastructure
- Outputs expose important results
- Modules publish outputs to parents
- CI/CD pipelines consume outputs for automation
- Sensitive outputs hide terminal display but not state contents
If you treat outputs like a clean API for your Terraform code, your configurations will be easier to reuse and easier to automate.
Knowledge Check
Question 1: Output Purpose
What is the main purpose of a Terraform output value?
Question 2: Sensitive Outputs
What does sensitive = true do in a Terraform output block?
Question 3: Module Outputs
How does a root module usually reference an output named vpc_id from a child module called network?