You’ve just provisioned infrastructure with Terraform—maybe an EC2 instance, a database, or a load balancer—and now you need to actually use it. What’s the public IP? What’s the database endpoint? That’s where Terraform output values come in.
Outputs are Terraform’s way of exposing resource properties after a run. They act like return values from your infrastructure code, making it easier to integrate with other systems, scripts, or even other Terraform modules.
Start with a Simple Example
Let’s say you create an AWS EC2 instance:
1resource "aws_instance" "web" {
2 ami = "ami-0c55b159cbfafe1f0"
3 instance_type = "t2.micro"
4}
5After applying this, you’ll probably want the public IP. You can expose it like this:
1output "instance_public_ip" {
2 value = aws_instance.web.public_ip
3}
4Run terraform apply, and Terraform prints:
1instance_public_ip = "3.91.45.12"
2That’s the basic idea: reference a resource attribute and expose it via an output block.
How Output Values Work
Each output block has a few key components:
- name: Identifier for the output
- value: The expression to expose
- description (optional): Helpful context
- sensitive (optional): Hides output in CLI
Example with additional metadata:
1output "db_endpoint" {
2 value = aws_db_instance.main.endpoint
3 description = "Primary database endpoint"
4}
5Outputting Multiple Resource Properties
You’re not limited to a single value. Terraform supports complex data structures like maps and lists.
For example:
1output "instance_details" {
2 value = {
3 id = aws_instance.web.id
4 public_ip = aws_instance.web.public_ip
5 az = aws_instance.web.availability_zone
6 }
7}
8This makes downstream consumption much cleaner, especially in automation pipelines.
Looping Over Multiple Resources
If you’re using count or for_each, you can aggregate outputs:
1resource "aws_instance" "web" {
2 count = 2
3 ami = "ami-0c55b159cbfafe1f0"
4 instance_type = "t2.micro"
5}
6
7output "instance_ips" {
8 value = [for instance in aws_instance.web : instance.public_ip]
9}
10This returns a list of IPs, which is far more practical than handling each instance individually.
Working Across Modules
Outputs become especially powerful when working with modules.
Inside a module:
1output "vpc_id" {
2 value = aws_vpc.main.id
3}
4Then in your root module:
1module "network" {
2 source = "./modules/vpc"
3}
4
5output "network_vpc_id" {
6 value = module.network.vpc_id
7}
8This pattern allows clean separation of concerns while still sharing critical resource properties.
Sensitive Outputs (Don’t Leak Secrets)
A common mistake developers make is accidentally exposing sensitive data like passwords or private keys.
Terraform provides a safeguard:
1output "db_password" {
2 value = aws_db_instance.main.password
3 sensitive = true
4}
5When marked as sensitive:
- Terraform hides the value in CLI output
- It still exists in state (important to secure your state file)
Important: Sensitive outputs are not encrypted automatically. Use secure backends like S3 with encryption or Terraform Cloud.
Accessing Outputs Programmatically
Sometimes you want to use outputs outside Terraform—like in CI/CD pipelines or scripts.
Use the CLI:
1terraform output instance_public_ip
2Or in JSON format:
1terraform output -jsonThis is especially useful for automation tools like GitHub Actions or Jenkins.
Conditional Outputs
You can make outputs dynamic based on conditions:
1output "maybe_ip" {
2 value = var.create_instance ? aws_instance.web.public_ip : null
3}
4This prevents Terraform from failing when a resource doesn’t exist.
Common Pitfalls
Here’s where things often go sideways:
- Referencing attributes too early: Outputs only work after apply, not during plan evaluation for missing resources.
- Overusing outputs: Not every value needs to be exposed—keep outputs intentional.
- Leaking sensitive data: Always review outputs for secrets.
- Breaking module contracts: Changing output names can break dependent modules.
When Outputs Really Shine
Outputs are particularly useful in these scenarios:
- Passing infrastructure details to application deployments
- Sharing values between Terraform modules
- Debugging and inspecting provisioned resources
- Integrating Terraform with external systems
A Practical Pattern
One clean pattern is to group related outputs into structured objects:
1output "app_config" {
2 value = {
3 api_url = aws_lb.app.dns_name
4 db_endpoint = aws_db_instance.main.endpoint
5 }
6}
7This reduces clutter and makes it easier for consumers to parse configuration.
Wrapping Up
Terraform output values are simple on the surface but incredibly useful in real-world workflows. Whether you’re exposing a single IP address or passing structured data between modules, outputs help bridge the gap between infrastructure and everything that depends on it.
Use them thoughtfully: keep them minimal, secure sensitive data, and design them as stable interfaces—especially when modules are involved.