It's time to write real Terraform. The language is called HCL (HashiCorp Configuration Language) and is organized into blocks. In this subsection, you'll learn about the four most important blocks you'll use constantly: resource, variable, output, and locals. With these, you can already describe real infrastructure.

What HCL looks like

Terraform code is written in files with the .tf extension. Everything in HCL is structured in blocks, which have this general form:

block_type "label1" "label2" {
  argument = value
  another_argument = another_value
}

Don't worry about the exact syntax now; you'll see it with examples. The important thing: HCL is readable, similar to filling out a form with data. Let's go over the four key blocks.

  1. resource: what you want to create

The resource block is the most important of all. It describes an infrastructure resource you want to exist: a server, a network, a bucket...

resource "aws_instance" "my_server" {
  ami           = "ami-0c1234567890abcde"
  instance_type = "t3.micro"
}

Let's break it down:

  • resource → the block type.
  • "aws_instance" → the resource type (an AWS EC2 instance). The aws_ prefix indicates it's from AWS.
  • "my_server" → the name YOU give it to refer to it within your code (it's not the name in AWS, it's an internal label).
  • Inside { } → the arguments that configure the resource (which AMI to use, what instance type... remember Chapter 4?).

Analogy: A resource block is like filling out the order form for a resource: "I want an instance, of type t3.micro, with this image." Terraform reads the form and creates it for you.

Every AWS resource you saw in Part II (EC2, S3, VPC...) has its own resource type in Terraform: aws_instance, aws_s3_bucket, aws_vpc, etc.

  1. variable: values that come from outside

The variable block defines configurable values that you can change without touching the rest of the code. They're like the "parameters" of your configuration.

variable "instance_type" {
  description = "The EC2 instance type to use"
  type        = string
  default     = "t3.micro"
}

Then you use that variable in your resources with the var.name syntax:

resource "aws_instance" "my_server" {
  ami           = "ami-0c1234567890abcde"
  instance_type = var.instance_type   # ← uses the variable
}

Why they're useful: Variables make your code reusable and flexible. For example, you can use t3.micro in testing and m5.large in production by changing only the variable's value, without touching the server definition. We'll see more in Chapter 19 (environments).

Analogy: Variables are like the configurable settings of an appliance. The washing machine is the same, but you adjust the temperature and program according to the laundry. Your code is the same, but you adjust the variables according to the environment.

  1. output: information you want to see

The output block displays useful information after applying your configuration. It's used to "output" data you need to know, like a server's IP or a website's URL.

output "public_ip" {
  description = "The server's public IP"
  value       = aws_instance.my_server.public_ip
}

When you run terraform apply, at the end you'll see:

Outputs:
public_ip = "54.123.45.67"

Why they're useful: After creating infrastructure, you need to know things like "what's my server's IP?" or "what's my database address?" Outputs show them to you automatically, without having to look them up in the AWS console. They're also used to pass information between modules (Chapter 18).

Analogy: Outputs are like the receipt or summary you get at the end: "here are the important details of what you just created."

  1. locals: calculated and reusable values

The locals block defines internal reusable values within your code. Unlike variables (which come from outside), locals are values you define and calculate inside your configuration.

locals {
  project_name = "online-store"
  environment  = "production"
  full_name    = "${local.project_name}-${local.environment}"
}

And you use them with local.name:

resource "aws_instance" "my_server" {
  ami           = "ami-0c1234567890abcde"
  instance_type = "t3.micro"
  tags = {
    Name = local.full_name   # → "online-store-production"
  }
}

When to use them: to avoid repeating values or calculations. If you use the same name or combination of values in many places, define it once in locals and reuse it. If it changes, you change it in just one place.

Variable vs Local — the key difference:

  • variable: comes from outside (set by whoever uses the code). "Input parameter."
  • local: is defined and calculated inside the code. "Reusable internal value."

Summary of the four blocks

Block What it's for How to reference it
resource Create infrastructure (the most important) aws_instance.my_server
variable Configurable values from outside var.name
output Show information after applying (seen at the end of apply)
locals Reusable internal values local.name

An example that puts it all together

variable "environment" {
  type    = string
  default = "dev"
}

locals {
  name = "myapp-${var.environment}"
}

resource "aws_instance" "web" {
  ami           = "ami-0c1234567890abcde"
  instance_type = "t3.micro"
  tags = {
    Name = local.name
  }
}

output "public_ip" {
  value = aws_instance.web.public_ip
}

This code: takes an environment as a variable, calculates a name with a local, creates a server tagged with that name, and shows its IP as an output. You're already reading Terraform!

What you should remember

  • HCL is organized in blocks, written in .tf files.
  • resource: the most important block; describes the infrastructure you want to create (servers, networks, buckets...).
  • variable: configurable values that come from outside; used with var.name. Make the code reusable.
  • output: shows useful information after apply (IPs, URLs...); used with the resource reference.
  • locals: reusable internal values you define inside the code; used with local.name.
  • Key difference: variable = comes from outside; local = calculated inside.

In the next subsection, we'll look at HCL's data types (string, number, list, map, object...) to shape your variables and values.

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