Terraform & IaC

Expose & Export Values: Output Values

Lesson 6 of 14 · 9 min · Intermediate

Outputs in Terraform are like return values in a programming function. They expose important infrastructure data — public IP addresses, load balancer DNS names, database endpoints, and resource IDs — on your terminal, to automated CI/CD pipelines, or to parent modules.

Why Output Values Matter

When Terraform finishes provisioning, you often need to know what was created so you can connect to it, configure DNS, or feed the values into an Ansible inventory or CI/CD deploy script. Without output blocks, you would have to open the cloud console and hunt for IDs manually.

Use CaseWhat Output Exposes
Operator TerminalPublic IPs, SSH commands, web portal URLs
CI/CD PipelinesJSON metadata for smoke tests and artifact deployment
Modular ArchitectureVPC Subnet IDs passed into EC2 & RDS child modules
Ansible HandoffDynamic inventory generation via terraform output -json

Root Module Outputs

Printed directly to the terminal screen when terraform apply finishes, and queryable anytime with terraform output.

Child Module Outputs

Export internal resource attributes so the calling parent module can access them via module.<NAME>.<OUTPUT>.

Declaring Output Blocks

outputs.tf
output "web_public_ip" {
  description = "Public IPv4 address of the web EC2 instance"
  value       = aws_instance.web.public_ip
}

output "web_ssh_command" {
  description = "Convenient one-liner to SSH into the instance"
  value       = "ssh -i ~/.ssh/id_rsa ubuntu@${aws_instance.web.public_ip}"
}

Output Block Attributes

An output block requires a unique name and a value expression. Adding a clear description is standard practice for production codebases and self-documenting modules.

  • value: The expression exported (resource attribute, string, map, or list).
  • description: Explains the purpose of the output.
  • sensitive: Hides value from standard terminal logs.
  • depends_on: Enforces explicit dependency before reading.

Querying Outputs with the CLI

terminal
# 1. Print all outputs in key-value format
$ terraform output
web_public_ip   = "13.235.45.109"
web_ssh_command = "ssh -i ~/.ssh/id_rsa ubuntu@13.235.45.109"

# 2. Query a single specific output
$ terraform output web_public_ip
"13.235.45.109"

# 3. Raw output (strip surrounding quotes — ideal for shell scripts)
$ terraform output -raw web_public_ip
13.235.45.109

# 4. JSON format (ideal for jq, Python, Ansible automation)
$ terraform output -json

Integrating with CI/CD & Scripts

In automated deployment pipelines, you can feed Terraform outputs directly into downstream testing or configuration tools without manual copying:

deploy.sh
# Extract raw IP and run curl health check
HOST_IP=$(terraform output -raw web_public_ip)
curl -f "http://${HOST_IP}/health" || exit 1
echo "Application is healthy on ${HOST_IP}"

Protecting Secrets: sensitive = true

If an output contains private passwords, database master keys, or private SSH keys, mark it with sensitive = true. Terraform will redact the value in standard CLI logs and CI/CD console output to prevent accidental leaks.

State Still Stores Secrets

Marking an output as sensitive = true prevents terminal display, but the value is still stored in plaintext inside terraform.tfstate. Always secure your remote backend!

outputs.tf
output "db_password" {
  description = "Generated database root password"
  value       = aws_db_instance.postgres.password
  sensitive   = true
}

# Terminal output when running apply:
# db_password = <sensitive>

# How an authorized engineer views it:
$ terraform output -raw db_password

Data Flow Between Child and Parent Modules

  • Child Module ( vpc ) Declares output "subnet_id"
  • →
  • Root Module ( main.tf ) References module.vpc.subnet_id
  • →
  • Resource ( aws_instance.web ) subnet_id = module.vpc.subnet_id
modules/vpc/outputs.tf
output "public_subnet_id" {
  description = "The ID of the newly created public subnet"
  value       = aws_subnet.public.id
}
main.tf
module "network" {
  source = "./modules/vpc"
}

resource "aws_instance" "web" {
  ami           = "ami-0123456789abcdef0"
  instance_type = "t3.micro"
  subnet_id     = module.network.public_subnet_id
}

Production Best Practices

RuleWhy It MattersExample
Always add descriptionsEnables automatic documentation generation (terraform-docs).description = "VPC ID"
Flag credentials as sensitivePrevents token and secret leakage in Jenkins / GitHub Actions logs.sensitive = true
Use -raw for shell scriptingExtracts clean strings without parsing quotes.terraform output -raw ip
Keep outputs in outputs.tfMaintains standard Terraform 3-file structure conventions.Dedicated output file