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 to true, hides values in CLI output for confidential information
  • depends_on: Creates explicit dependencies (rarely needed)
  • ephemeral: When true, 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 not subnet_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

  1. Secure your state files, as they contain all output values
  2. 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.