Understanding Terraform output values
Master Terraform output values: see what they are, when to use them, and copy-ready examples to expose, reference, and share infrastructure data.
Terraform output values provide a powerful way to extract, expose, and share information about your infrastructure. They function like return values in programming languages, enabling you to make resource attributes available to users, automation tools, and other Terraform configurations.
Outputs shine when you need visible infrastructure properties after deployment, want to pass information between modules, or intend to share data across Terraform workspaces. Whether you're creating simple configurations or complex multi-module infrastructure, knowing how to effectively use output values is essential.
Defining output values: syntax and structure
Output blocks in Terraform follow a straightforward structure, with several arguments that control how values are exposed and handled:
output "name" {
value = expression
description = "Description of the output"
sensitive = false
depends_on = [resource.name]
ephemeral = false # Available in Terraform v1.10+
}
The only required argument is value
, which defines what data will be exposed. The expression can reference resource attributes, variables, locals, or even complex calculations.
Optional arguments:
description
: Documents the purpose of the output (highly recommended)sensitive
: When set totrue
, hides values in CLI output for confidential informationdepends_on
: Creates explicit dependencies (rarely needed)ephemeral
: Whentrue
, prevents the value from being stored in state files (v1.10+)
A simple example exposing an EC2 instance's IP address:
output "instance_ip_addr" {
value = aws_instance.server.private_ip
description = "The private IP address of the main server instance."
}
Output value types and how to use them
Terraform outputs can return various data types, from simple primitives to complex structures.
Basic types
Strings:
output "instance_id" {
value = aws_instance.example.id
}
Numbers:
output "instance_count" {
value = length(aws_instance.cluster)
}
Booleans:
output "is_production" {
value = var.environment == "production"
}
Collection types
Lists are useful for returning multiple similar values:
output "private_ips" {
value = aws_instance.server[*].private_ip
}
Maps provide key-value pairs:
output "instance_ips" {
value = {
for instance in aws_instance.cluster:
instance.id => instance.private_ip
}
}
Objects combine multiple values into a structured output:
output "db_config" {
value = {
endpoint = aws_db_instance.db.endpoint
username = aws_db_instance.db.username
database = aws_db_instance.db.name
}
}
Accessing output values after terraform apply
Terraform provides several methods to access output values after deployment.
Command line access
After running terraform apply
, outputs are automatically displayed:
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
instance_id = "i-1234567890abcdef0"
instance_ip = "10.0.0.1"
You can retrieve outputs later using:
# Get all outputs
terraform output
# Get a specific output
terraform output instance_id
# Get a specific output without quotes (useful for scripts)
terraform output -raw instance_id
# Get outputs in JSON format
terraform output -json
Using outputs in scripts and automation
The -json
and -raw
flags are particularly useful for integrating with scripts:
# Extract a specific value using jq
terraform output -json instance_ips | jq -r '.[0]'
# Use the raw output in a script
DB_ENDPOINT=$(terraform output -raw db_endpoint)
Accessing outputs from remote state
You can access outputs from other Terraform configurations using the terraform_remote_state
data source:
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "terraform-state"
key = "network/terraform.tfstate"
region = "us-west-2"
}
}
resource "aws_instance" "app" {
# Use an output from the remote state
subnet_id = data.terraform_remote_state.network.outputs.subnet_id
}
Working with outputs in modules
Modules use outputs to expose specific resource attributes to parent modules, creating a clean interface.
Child module outputs
Define outputs in a child module to expose internal values:
# modules/vpc/outputs.tf
output "vpc_id" {
value = aws_vpc.this.id
description = "The ID of the VPC"
}
output "public_subnets" {
value = aws_subnet.public[*].id
description = "List of public subnet IDs"
}
Accessing child module outputs
In the root module, access child module outputs using the syntax module.<MODULE_NAME>.<OUTPUT_NAME>
:
# main.tf (root module)
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
}
module "ec2" {
source = "./modules/ec2"
vpc_id = module.vpc.vpc_id
subnet_id = module.vpc.public_subnets[0]
}
output "vpc_id" {
value = module.vpc.vpc_id
}
Module output chain
For multi-level modules, outputs must be "bubbled up" through each level:
# Child module (./modules/database/outputs.tf)
output "connection_string" {
value = "postgres://${aws_db_instance.main.endpoint}"
}
# Middle module (./modules/backend/outputs.tf)
output "database_connection" {
value = module.database.connection_string
}
# Root module (./outputs.tf)
output "db_connection" {
value = module.backend.database_connection
}
Advanced output techniques
Beyond basic usage, Terraform outputs support sophisticated patterns that enhance flexibility and security.
Sensitive outputs
For confidential information, mark outputs as sensitive:
output "db_password" {
value = aws_db_instance.db.password
description = "The password for logging in to the database."
sensitive = true
}
This redacts the value in CLI output but doesn't affect storage in the state file.
Ephemeral outputs (Terraform v1.10+)
For truly sensitive values, use ephemeral outputs in child modules:
output "secret_id" {
value = aws_secretsmanager_secret.secret_id
description = "Temporary secret ID for accessing database."
ephemeral = true
}
These values won't be persisted in state files, providing an extra layer of security.
Conditional outputs
Use conditional expressions for outputs that may or may not exist:
output "load_balancer_dns" {
value = var.create_load_balancer ? aws_lb.main[0].dns_name : null
}
Complex transformations
Format and transform outputs using Terraform's built-in functions:
output "instance_details" {
value = {
id = aws_instance.server.id
name = upper(aws_instance.server.tags.Name)
dns = coalesce(aws_instance.server.public_dns, "no-public-dns")
launched = formatdate("DD MMM YYYY", aws_instance.server.launch_time)
}
}
Using for expressions with outputs
Create structured outputs from collections of resources:
output "server_details" {
value = [
for server in aws_instance.cluster : {
id = server.id
ip = server.private_ip
az = server.availability_zone
security = join(", ", server.security_groups)
}
]
}
Output dependencies
Sometimes you need to ensure specific resources are created before computing an output:
output "api_endpoint" {
value = "https://${aws_instance.api.public_dns}/v1/"
depends_on = [
aws_security_group_rule.api_ingress,
aws_route53_record.api
]
}
Troubleshooting output values
When working with outputs, several common issues can arise. Here's how to address them.
Handling null values
One of the most frequent issues involves null values in outputs:
# This will error if some_value is null
output "example" {
value = var.some_value / 5
}
Solutions:
Use try()
to handle potential errors:
output "pet" { value = try(random_pet.this[0].id, "no pet created")}
Use the coalesce()
function for the first non-null value:
output "example" { value = coalesce(var.some_value, 0) / 5}
Use conditional expressions for fallbacks:
output "example" { value = var.some_value != null ? var.some_value / 5 : 0}
Debugging output issues
Terraform offers several tools for debugging output problems:
Inspect the state file to view stored outputs:
terraform show
# or for remote state
terraform state pull > state.json
Enable Terraform logs for detailed information:
export TF_LOG=DEBUG
export TF_LOG_PATH=./terraform.log
terraform apply
Interactive console for testing expressions:
$ terraform console
> module.web_server.instance_ip
"54.43.114.12"
Output formatting problems
When using outputs in automation, formatting can be tricky:
- Use
-raw
for string outputs in scripts (removes quotes)
Use -json
with jq
for complex outputs:
# Extract a value from a mapterraform output -json config | jq -r '.value.region'
Cloud provider examples
Here are practical examples of output values for major cloud providers.
AWS examples
EC2 and infrastructure outputs:
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.app_server.id
}
output "instance_arn" {
description = "ARN of the EC2 instance"
value = aws_instance.app_server.arn
}
output "s3_bucket_url" {
description = "The S3 bucket URL"
value = "https://${aws_s3_bucket.example.bucket_regional_domain_name}"
}
Network configuration outputs:
output "vpc_id" {
description = "The ID of the VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "List of IDs of public subnets"
value = aws_subnet.public[*].id
}
output "lb_dns_name" {
description = "The DNS name of the load balancer"
value = aws_lb.example.dns_name
}
Security-related outputs:
output "security_group_id" {
description = "ID of the security group"
value = aws_security_group.example.id
}
output "iam_role_arn" {
description = "ARN of the IAM role"
value = aws_iam_role.example.arn
}
Azure examples
VM and resource outputs:
output "vm_ids" {
description = "Virtual machine ids created"
value = azurerm_linux_virtual_machine.vm[*].id
}
output "storage_account_primary_blob_endpoint" {
description = "The primary blob endpoint of the storage account"
value = azurerm_storage_account.example.primary_blob_endpoint
}
Network configuration outputs:
output "vnet_id" {
description = "The id of the newly created vNet"
value = azurerm_virtual_network.vnet.id
}
output "subnet_ids" {
description = "The ids of subnets created inside the vNet"
value = azurerm_subnet.subnet[*].id
}
GCP examples
Compute and infrastructure outputs:
output "instance_id" {
description = "The server-assigned unique identifier"
value = google_compute_instance.vm_instance.instance_id
}
output "bucket_url" {
description = "The base URL of the bucket, in gs:// format"
value = google_storage_bucket.static.url
}
Network configuration outputs:
output "network_id" {
description = "The ID of the VPC being created"
value = google_compute_network.vpc_network.id
}
output "subnets_ips" {
description = "The IPs and CIDRs of the subnets being created"
value = google_compute_subnetwork.subnet[*].ip_cidr_range
}
Best practices for Terraform outputs
To make the most of Terraform outputs, follow these best practices.
When to use outputs vs locals vs variables
- Input variables: For external configuration passed into the module
- Local values: For internal calculations and reducing repetition
- Output values: For exposing information to users or other modules
# Input variable for configuration
variable "instance_type" {
type = string
description = "The type of EC2 instance to launch"
default = "t2.micro"
}
# Local value for internal use
locals {
name_suffix = "${var.project_name}-${var.environment}"
}
# Output for exposing information
output "instance_ip" {
value = aws_instance.server.private_ip
description = "The private IP address of the server"
}
Naming and organization
- Use snake_case for output names (e.g.,
instance_ip_addr
) - Place outputs in a dedicated
outputs.tf
file - Group related outputs with comments
- Use plural names for list outputs (e.g.,
subnet_ids
notsubnet_id
)
Documentation best practices
Always include descriptions for all outputs:
output "load_balancer_dns" {
value = aws_lb.main.dns_name
description = "The DNS name of the load balancer for connecting to the application."
}
For modules, document outputs in the README.md and consider using tools like terraform-docs
.
Output validation
Use precondition blocks to validate output data (Terraform 1.2.0+):
output "api_base_url" {
value = "https://${aws_instance.example.private_dns}:8433/"
precondition {
condition = data.aws_ebs_volume.example.encrypted
error_message = "The server's root volume is not encrypted."
}
}
Performance considerations
- Avoid outputting large data structures that bloat the state file
- Consider splitting large outputs into focused, smaller outputs
- Break complex configurations into modules with clear output boundaries
- Only share the outputs necessary between workspaces
Security best practices
- Secure your state files, as they contain all output values
- Consider external secret management systems for truly sensitive data
For highly sensitive data in child modules, use ephemeral outputs:
output "temporary_token" {
value = aws_iam_access_key.temporary.secret
description = "Temporary access token for deployment"
ephemeral = true
}
Mark sensitive outputs appropriately:
output "database_password" {
value = aws_db_instance.main.password
description = "The password for the database."
sensitive = true
}
Conclusion
Terraform output values are a foundational mechanism for extracting and sharing information about your infrastructure. By following best practices for naming, documentation, security, and organization, you can create outputs that enhance the usability and maintainability of your Terraform configurations.
Whether you're working with simple outputs or complex data structures, using outputs effectively allows you to build modular, reusable infrastructure code that's easy to understand and integrate with external systems. The examples and patterns in this guide provide a solid foundation for implementing output values across AWS, Azure, GCP, and other providers in your Terraform projects.