Terraform Output Values
Terraform has revolutionized how we manage infrastructure, allowing us to define and provision resources with code. A key, yet sometimes underutilized, feature in this ecosystem is output values. These are more than just a way to print information to your console; they are crucial for connecting configurations, enabling automation, and providing clear visibility into your deployed infrastructure.
In this post, we'll dive deep into Terraform output values—what they are, why they're essential, and how to use them effectively. We'll also touch upon how managing outputs, especially at scale, can be streamlined for better collaboration and security.
Table of Contents
- What Exactly Are Terraform Output Values?
- The "Why": Key Benefits of Using Output Values
- Defining Your Outputs: Syntax and Key Arguments
- Accessing and Utilizing Your Outputs
- Handling Data Types in Outputs
- Outputs, the Terraform Lifecycle, and State Management
- Outputs vs. Inputs vs. Locals: A Quick Comparison
- Best Practices for Effective Output Management
- Common Use Cases and Examples
- Elevating Output Management with Scalr
- Conclusion
What Exactly Are Terraform Output Values?
Think of Terraform output values as the "return values" of your Terraform configuration. After Terraform creates or modifies your infrastructure, outputs allow you to extract specific pieces of information about those resources. This could be an IP address of a server, the ARN of an S3 bucket, a database connection string, or even complex data structures.
Essentially, outputs define an API for your Terraform module or configuration, declaring what information it will make available to the outside world—be it a user, another Terraform configuration, or an automation script.
The "Why": Key Benefits of Using Output Values
Effectively using output values brings several advantages to your IaC practice:
- Data Sharing: Easily share attributes of dynamically created resources. For example, a network module can output VPC IDs and subnet IDs for application modules to consume.
- Automation: Outputs are the lifeblood of automation. CI/CD pipelines can consume outputs like application URLs or database endpoints to deploy applications or run tests.
- Enhanced Visibility & Debugging: Quickly inspect the state and attributes of your provisioned resources, which is invaluable for troubleshooting.
- Modularity: Outputs are fundamental to Terraform's module system, allowing child modules to expose necessary information to parent modules in a controlled way.
- Consistency: Referencing outputs ensures that dependent systems always use the correct, dynamically generated information, reducing errors.
- Self-Documentation: Well-defined outputs act as live documentation for your infrastructure's key endpoints and identifiers.
While Terraform provides the mechanisms, managing these benefits across numerous workspaces, teams, and environments can become complex. Platforms like Scalr can centralize the visibility of these outputs, making it easier for teams to discover and consume them securely, enhancing collaboration without direct state file access.
Defining Your Outputs: Syntax and Key Arguments
Outputs are declared using an output
block, typically in an outputs.tf
file for organization.
The output
Block and value
Argument
The basic structure is:
output "instance_public_ip" {
value = aws_instance.web_server.public_ip
# Other arguments go here
}
The output
keyword is followed by a name (e.g., "instance_public_ip"
), which is how you'll reference it. The value
argument is mandatory and takes any valid Terraform expression. Its result is what gets exposed.
Essential Optional Arguments: description
, sensitive
, depends_on
, ephemeral
depends_on
: Rarely needed. Explicitly defines a dependency if Terraform can't infer it from thevalue
expression. Use sparingly.ephemeral
(Terraform v1.10+): For child modules only. Iftrue
, the output's value isn't persisted in the state file or plan files, useful for transient sensitive data passed between modules.
sensitive
: A boolean (defaults to false
). If true
, Terraform redacts the value from CLI output during plan
and apply
. Crucially, the value is still stored in plaintext in the state file.
output "database_password" {
value = random_password.password.result
description = "The generated password for the database."
sensitive = true
}
description
: A human-readable explanation of the output. Always include this!
output "rds_endpoint" {
value = aws_db_instance.default.endpoint
description = "The connection endpoint for the RDS database instance."
}
Ensuring Data Integrity with precondition
You can add precondition
blocks to validate output values:
output "instance_public_ip" {
value = aws_instance.example.public_ip
description = "The public IP address of the EC2 instance."
precondition {
condition = can(regex("^(\\d{1,3}\\.){3}\\d{1,3}$", aws_instance.example.public_ip))
error_message = "The public IP address must be a valid IPv4 format."
}
}
Accessing and Utilizing Your Outputs
The terraform output
Command
After a terraform apply
, use the terraform output
command:
terraform output
: Shows all non-sensitive outputs. Sensitive outputs are shown as<sensitive>
.terraform output <NAME>
: Shows the value of a specific output. Displays sensitive values in plaintext.terraform output -json
: Outputs all values in JSON format, useful for scripts. Displays sensitive values in plaintext.terraform output -raw <NAME>
: Prints the raw string value of a single output (string, number, or bool only). Displays sensitive values in plaintext.
Outputs in Modules and Remote State
terraform_remote_state
Data Source: Access outputs from another Terraform configuration's state file.
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-terraform-states"
key = "prod/network/terraform.tfstate"
region = "us-east-1"
}
}
resource "aws_instance" "web" {
# ...
subnet_id = data.terraform_remote_state.network.outputs.public_subnet_id
}
While terraform_remote_state
is powerful, it requires broad access to the source state file, which can be a security concern. Platforms like Scalr offer more granular ways to share outputs between environments or workspaces without exposing the entire state, often integrating with concepts like an Environment-Scoped Variable provider.
Child Modules: Access outputs from a child module using module.<MODULE_NAME>.<OUTPUT_NAME>
.
resource "aws_route53_record" "app" {
zone_id = data.aws_route53_zone.primary.zone_id
name = "app.example.com"
type = "A"
ttl = 300
records = [module.app_server.public_ip] // Accessing output from 'app_server' module
}
Handling Data Types in Outputs
Outputs can handle various data types:
Complex Types: list
, map
, object
, set
, tuple
.
output "server_public_ips" {
description = "List of public IP addresses for the web servers."
value = aws_instance.web.*.public_ip // Splat expression for a list
}
output "instance_details" {
description = "Details of the primary instance."
value = {
id = aws_instance.main.id
public_ip = aws_instance.main.public_ip
private_ip = aws_instance.main.private_ip
}
}
Primitive Types: string
, number
, bool
.
output "instance_count" {
value = 3
description = "Number of web server instances."
}
Outputs, the Terraform Lifecycle, and State Management
Output values are computed during terraform apply
after dependent resources are created/updated. The terraform plan
will indicate changes to output definitions or if their values are expected to change ((known after apply)
), but won't show the final computed values.
Computed output values are stored in the Terraform state file. This is how terraform output
retrieves them quickly.
Security Considerations for Outputs in State
This is critical: values marked sensitive = true
are stored in plaintext in the state file. Anyone with access to the state file can read these "sensitive" outputs. This underscores the importance of:
- Secure Remote State Backends: Use backends like AWS S3 (with encryption and strict policies), Azure Blob Storage, or HashiCorp Terraform Cloud/Enterprise.
- Strict Access Control: Limit who and what can access state files.
- Alternative Secret Management: For highly sensitive data, consider having Terraform configure resources to fetch secrets at runtime from systems like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault, rather than passing the secrets through outputs.
Scalr helps here by managing state storage securely and providing Role-Based Access Control (RBAC) not just to the state itself, but potentially to the visibility and consumption of outputs derived from that state, reducing the need for direct state file exposure.
Outputs vs. Inputs vs. Locals: A Quick Comparison
Feature | Input Variables ( | Local Values ( | Output Values ( |
---|---|---|---|
Purpose | Parameterize modules | Name expressions, DRY principle within a module | Expose data from a module/configuration |
Scope | External to module (passed in) | Internal to module | Internal to module, exposed externally |
Data Flow | Into module | Within module | Out of module |
CLI Access | Set via | Not directly accessible |
|
Use Case | Instance types, CIDR blocks, environment tags | Complex resource names, transformed data for internal use | IP addresses, ARNs, connection strings, resource identifiers |
Best Practices for Effective Output Management
- Strategic Exposure: Output what's needed for external consumption or automation. For reusable modules, consider exposing all potentially useful, non-internal attributes.
- Clear Naming: Use
snake_case
and be descriptive (e.g.,instance_public_ip
,database_endpoint_port
). Include units if applicable (e.g.,ram_size_gb
). - Always Describe: Every output needs a meaningful
description
. - File Organization: Keep all outputs in
outputs.tf
. - Security First: Use
sensitive = true
appropriately, but remember state file security is paramount. - Module Contracts: Outputs define a module's API. Changes to them are breaking changes.
- Reference Resource Attributes: Outputs should generally reference attributes of resources managed by the module (e.g.,
value = aws_instance.main.id
) rather than just passing through input variables. This ensures correct dependency tracking.
Common Use Cases and Examples
Connection Strings (Always Sensitive!):
output "db_jdbc_connection_string" {
description = "JDBC connection string for the RDS database."
value = "jdbc:postgresql://${aws_db_instance.default.endpoint}/${aws_db_instance.default.db_name}"
sensitive = true
}
Resource Identifiers:
output "ec2_instance_id" {
description = "ID of the provisioned EC2 instance."
value = aws_instance.app_server.id
}
output "lambda_function_arn" {
description = "ARN of the deployed Lambda function."
value = aws_lambda_function.my_lambda.arn
}
Network Details:
output "vpc_id" {
description = "ID of the provisioned Virtual Private Cloud."
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "List of public subnet IDs."
value = aws_subnet.public.*.id
}
Elevating Output Management with Scalr
While Terraform provides the core functionality for output values, managing them across a large organization with multiple teams, environments, and hundreds of workspaces presents challenges:
- Discoverability: How do teams find the outputs they need from other workspaces without sifting through state files or code?
- Access Control: How do you grant access to consume specific outputs without granting overly broad permissions to state files?
- Consistency: How do you ensure outputs are used consistently and securely across projects?
- Automation Complexity: Integrating outputs into complex CI/CD pipelines or scripts often requires custom scripting to handle
terraform_remote_state
orterraform output -json
.
This is where a Terraform Automation and Collaboration platform like Scalr adds significant value. Scalr can provide:
- Centralized Output Visibility: A dashboard or API to easily view and search for outputs across all managed workspaces and environments.
- Granular RBAC for Outputs: Control who can see or use specific outputs, independent of direct state file access. This enhances security by adhering to the principle of least privilege.
- Environment-Scoped Variables from Outputs: Scalr can automatically promote outputs from one workspace to become input variables for another, simplifying inter-workspace dependencies without manual
terraform_remote_state
configurations. This is particularly powerful for managing foundational service outputs (like network details or shared EKS cluster endpoints) consumed by multiple application workspaces. - Simplified CI/CD Integration: Easier ways to inject output values into your pipelines, reducing boilerplate scripting.
- Auditability: Track who accessed or used which outputs.
By abstracting some of the complexities of raw Terraform state and output management, Scalr empowers teams to collaborate more effectively and securely, making infrastructure insights (via outputs) more accessible and actionable.
Conclusion
Terraform output values are a fundamental building block for creating robust, modular, and automated infrastructure. By understanding their syntax, lifecycle implications, and security considerations, you can significantly improve your IaC practices. From simple IP address exposure to complex inter-module data sharing, outputs are the key to unlocking the full potential of your Terraform configurations.
And as your Terraform usage scales, consider how platforms like Scalr can help you manage these valuable pieces of information more efficiently and securely, turning raw outputs into governed, easily consumable infrastructure intelligence.