Lesson 10 of 16
Section 3: Variables, Expressions, Loops & Meta-Arguments30 min read & walkthrough

Outputs, Sensitive Masking & Remote State Data Sources

01 / OUTPUTS
Interface Contract

Expose endpoint URLs, security group IDs, and connection strings to consumers.

02 / REDACTION
Sensitive Masking

Mark outputs as sensitive to prevent terminal log leakage and CI/CD exposure.

03 / DECOUPLING
Remote State

Read attributes across isolated stacks with the 'terraform_remote_state' data source.

The Big Idea

Just as functions return values, Terraform modules return Outputs. Outputs serve two primary purposes: exposing helpful endpoints to human engineers after an apply (such as a load balancer DNS name), and serving as public interface contracts for parent modules and external stacks.

When an output contains sensitive data (like an RDS master password or an IAM secret access key), Terraform enforces strict protection. If an output is derived from any sensitive resource attribute, Terraform will fail the build unless you explicitly declare sensitive = true.

For large organizations, monolithic state files are a major anti-pattern. Instead, you decouple infrastructure into independent tiers (e.g. Core Network, Database Tier, Kubernetes Compute). Using the terraform_remote_state data source, the compute tier can safely read the VPC ID and Subnet IDs from the network tier's state file without shared mutation risks.

💡

Jargon Buster: Plain-English Translations

  • Output Value: A named return value exported by a Terraform configuration or module.
  • Sensitive Attribute: A flag that instructs Terraform to redact values from human-facing CLI logs, displaying '' instead.
  • Decoupled Stacks: Splitting infrastructure into independently deployed state files to minimize blast radius and deployment lock contention.
  • Data Source: A read-only block used to fetch information from external cloud providers or other Terraform state files.

Declaring Outputs & Consuming Cross-Stack Remote State

# In Stack 1 (Networking): outputs.tf
output "vpc_id" {
  value       = aws_vpc.core.id
  description = "The physical ID of the production VPC"
}

output "database_password" {
  value       = random_password.master.result
  description = "Master database password"
  sensitive   = true # Redacts secret from terminal logs
}

# -------------------------------------------------------------
# In Stack 2 (Compute / ECS): main.tf
# Read Stack 1's outputs without sharing state mutation locks
data "terraform_remote_state" "network" {
  backend = "s3"

  config = {
    bucket = "enterprise-production-terraform-state-2026"
    key    = "core/production/vpc.tfstate"
    region = "us-east-1"
  }
}

# Use the read-only output in compute resources
resource "aws_security_group" "web_app" {
  name        = "web-application-firewall"
  vpc_id      = data.terraform_remote_state.network.outputs.vpc_id
  description = "Web firewall inside network-tier VPC"
}
⚠️

Beginner Traps & Gotchas

1. Thinking 'sensitive = true' encrypts the value in the state file

The Gotcha: Believing that sensitive outputs are encrypted in terraform.tfstate.

The Fix: 'sensitive = true' only redacts values from CLI terminal outputs and plan diffs. The secret is still stored in raw plaintext in the state file.

2. Creating tight coupling with circular remote state references

The Gotcha: Stack A reading from Stack B while Stack B reads from Stack A.

The Fix: Design strict hierarchical layers: Foundation (Network) -> Data (Databases) -> Application (Compute). Dependencies must only flow downward.