You have built a complete infrastructure, but after an apply you have a practical question: What is my server's IP to open it in the browser? That's what outputs are for. In this subchapter, we'll see how to extract useful information and dive deeper into references, the mechanism that makes everything fit together.

The Problem Outputs Solve

Imagine you apply your configuration and Terraform creates 8 resources. How do you find out the server's public IP? You could go to the AWS console to look for it... but that's exactly what we want to avoid. Outputs (remember subchapter 10.1) make Terraform show you the important data at the end.

Defining Outputs

We create an outputs.tf file (or put it in any .tf) with the information we're interested in:

output "ip_publica" {
  description = "Public IP of the web server"
  value       = aws_eip.web.public_ip
}

output "url_web" {
  description = "URL to open the website in the browser"
  value       = "http://${aws_eip.web.public_ip}"
}

output "id_instancia" {
  description = "ID of the EC2 instance"
  value       = aws_instance.web.id
}

output "id_vpc" {
  description = "ID of the created VPC"
  value       = aws_vpc.principal.id
}

After a terraform apply, you'll see something like this at the end:

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

Outputs:

id_instancia = "i-0a1b2c3d4e5f67890"
id_vpc       = "vpc-0abc123def456"
ip_publica   = "52.48.123.45"
url_web      = "http://52.48.123.45"

Now you have the URL ready to copy and paste into the browser! You can also check them at any time with terraform output (subchapter 11.4).

Notice url_web: we use interpolation ("http://${...}", subchapter 10.3) to build a complete URL by combining text with the IP value. Outputs don't have to be "raw" values: you can compose whatever you need.

References: The Glue of Terraform

Throughout the chapter, we've constantly used expressions like aws_vpc.principal.id. Let's understand what they are, because they are the central concept of Terraform.

A reference looks like this:

aws_eip.web.public_ip
│        │    │
│        │    └── attribute: what data I want (the public IP)
│        └─────── name I GAVE to the resource
└──────────────── resource type

When you write aws_eip.web.public_ip, you're telling Terraform: "give me the public IP of the Elastic IP resource I called web."

Why References Are So Important

References do two things at once:

1. Pass data from one resource to another. The instance needs the subnet ID, the Elastic IP needs the instance ID, etc. References connect that information.

2. Automatically create the dependency graph. As we saw in subchapter 9.4, Terraform deduces the creation order from the references. If A references B, then B is created before A.

Let's see it in our infrastructure:

aws_vpc.principal
   ▲
   │ (referenced by)
   ├── aws_subnet.publica ────────┐
   ├── aws_internet_gateway.igw    │
   ├── aws_route_table.publica     │
   └── aws_security_group.web      │
                                   ▼
                          aws_instance.web
                                   ▲
                                   │
                              aws_eip.web

Terraform reads this graph and creates things in order: first the VPC, then what depends on it, then the instance, and finally the Elastic IP. You don't specify the order: it deduces it from the references. And where possible, it parallelizes to go faster.

This is what makes Terraform magical. You don't write "do this, then this, then that" (that would be imperative, subchapter 9.2). You just describe the resources and how they relate, and Terraform figures out the correct order. That's why it's declarative.

Explicit References with depends_on

Sometimes two resources depend on each other but without a direct data reference. In those rare cases, you can force the order with depends_on:

resource "aws_instance" "web" {
  # ...
  depends_on = [aws_internet_gateway.igw]
}

This tells it "don't create the instance until the Internet Gateway exists," even if you don't use any of its data. It's a last-resort tool: in 95% of cases, normal references are enough and are preferable. Use it only when Terraform can't deduce a dependency by itself.

What You Should Remember

  • Outputs show useful information at the end (apply) and can be checked with terraform output; perfect for data like the server's IP or URL.
  • You can compose outputs with interpolation (e.g., build a complete URL with "http://${...}").
  • A reference (type.name.attribute) does two things: passes data between resources and creates the dependency (the creation order).
  • Terraform builds a dependency graph from the references and deduces the order itself: you don't specify it. That's what makes it declarative.
  • depends_on forces an order when there's no data reference; use it as a last resort, not by default.

In the last subchapter of the chapter, we'll make the leap to teamwork: how infrastructure changes are reviewed through Pull Requests and plan review.

Cloud, AWS & Terraform — From Zero to Expert

Chapter 1 · What is cloud computing

Chapter 2 · The cloud market and major providers

Chapter 3 · Regions, availability zones and edge

Chapter 4 · Compute: EC2

Chapter 5 · Storage: S3

Chapter 6 · Networking: VPC

Chapter 7 · Identity and access: IAM

Chapter 8 · Managed databases

Chapter 9 · Why Infrastructure as Code

Chapter 10 · HCL: the Terraform language

Chapter 11 · Providers and state

Chapter 12 · Your first real infrastructure in Terraform

Chapter 13 · Load balancing and auto scaling

Chapter 14 · Serverless with Lambda

Chapter 15 · Messaging and events

Chapter 16 · Content delivery and DNS

Chapter 17 · Containers on AWS

Chapter 18 · Modules: reuse and composition

Chapter 19 · Workspaces and environment management

Chapter 20 · Remote backends and locking

Chapter 21 · Infrastructure testing

Chapter 22 · Terraform in CI/CD

Chapter 23 · Defense in depth

Chapter 24 · Observability: logs, metrics and traces

Chapter 25 · Cost optimization

Chapter 26 · High availability and disaster recovery

Chapter 27 · AWS Well-Architected Framework

Chapter 28 · Serverless architectures at scale

Chapter 29 · Data platforms on AWS

Chapter 30 · Multi-account and landing zones

Chapter 31 · Platform Engineering and Internal Developer Platform

Chapter 32 · Relevant AWS certifications

Chapter 33 · Projects to consolidate what you've learned

Chapter 34 · Resources and community

© Copyright 2024. All rights reserved