Terraform Output Values

By Vishvesh Patel · DevOps EngineerUpdated July 12, 2026

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

ArgumentPurpose
valueThe expression Terraform should expose
descriptionHuman-readable explanation of the output
sensitiveHides 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:

For an EC2 module, useful outputs might include:

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:

  1. runs terraform apply
  2. reads the application URL
  3. 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

Exercise

Question 1: Output Purpose

What is the main purpose of a Terraform output value?

Exercise

Question 2: Sensitive Outputs

What does sensitive = true do in a Terraform output block?

Exercise

Question 3: Module Outputs

How does a root module usually reference an output named vpc_id from a child module called network?


Frequently Asked Questions

What are Terraform outputs used for?

They expose values from a configuration, an IP address, a DNS name, a database endpoint, a generated ARN, so that they can be read after apply, consumed by a calling module, or picked up by a CI script. Within a module, outputs are the only way to return anything to the caller: a parent module cannot reach inside a child to read a resource attribute directly. Outputs are also how humans find the URL of what they just deployed.

How do I read an output value?

terraform output prints them all, terraform output name prints one, and terraform output -json produces machine-readable output for scripts to parse with jq. The -raw flag prints a single string value without quotes, which is what you want when interpolating into a shell command. Outputs are read from state, so they are available any time after apply without re-running anything.

What does marking an output sensitive actually do?

It redacts the value in terraform plan and terraform apply console output, showing (sensitive value) instead. That is all. The value is still stored in plain text in the state file, still returned by terraform output -json, and still visible to anyone who can read state. It protects against a secret appearing in a CI log or a screenshot. It is not encryption, and it is not an access control.

Why does my output show "known after apply" in the plan?

Because it depends on an attribute that does not exist until the resource is created: an auto-generated ID, an assigned IP address, an ARN. Terraform cannot know it at plan time, so it shows a placeholder. This is normal and requires no action. If an output you expected to be static shows this, it is referencing a resource attribute rather than a variable or literal.

How do I pass outputs between modules?

A child module declares an output, and the parent references it as module.<name>.<output>. That value can then be passed as an input to another module, which is how you wire a network module's subnet IDs into a compute module. For passing values between separate root configurations with separate state, use a terraform_remote_state data source, though many teams prefer writing the value to SSM Parameter Store and reading it back, which avoids coupling one configuration's state to another's.

Can I use outputs in CI/CD pipelines?

Yes, and it is the standard way to hand values to later steps. Run terraform output -json > outputs.json and parse it, or use -raw to capture a single value into a shell variable. Typical uses are passing a load balancer DNS name to a smoke test, an ECR repository URL to a build step, or a cluster name to a deployment job. Remember sensitive outputs appear unredacted in -json, so avoid printing the whole document into a log.

Continue Learning

Explore Related Topics

Try the Tool

Practise this in the browser

Terraform Playground

Run init, plan, apply and destroy against simulated infrastructure.

When it breaks

Related Resources