Skip to content

Module 4 — Terraform: Infrastructure as Code

Goal: read, write, plan and apply Terraform; understand state; and finally read the very code that built this training environment. From here on, the student changes the lab by editing HCL, never by clicking.

4.1 Why IaC (10 minutes, but the most important 10 minutes)

Console clicking is a truck roll: unrepeatable, undocumented, error-prone at 2am. IaC gives you:

  • Repeatabilityapply builds the same thing every time, in any account/region.
  • Review — infra changes go through pull requests like code; plan is the MOP.
  • Rollbackgit revert + apply.
  • Documentation that can't lie — the code is the current state of the estate.

Declarative vs imperative: you describe the destination, Terraform computes the route (like typing a target config into a planning tool vs issuing individual set commands).

4.2 The core loop

write .tf  →  terraform init  →  terraform plan  →  terraform apply  →  (destroy)
                     │                  │
              downloads providers   the diff: + create  ~ change  - destroy/replace

State (terraform.tfstate): Terraform's record of what it built and the IDs AWS assigned. Lose it and Terraform forgets it owns anything. Real teams keep it in S3 with locking — never in git, and never edited by hand.

Lab 4.2 — First resource from scratch

On lab-box (terraform is pre-installed):

mkdir -p ~/labs/module4/first && cd ~/labs/module4/first

main.tf:

terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

provider "aws" {
  region = "eu-west-2"
}

resource "aws_s3_bucket" "scratch" {
  bucket = "cloud-course-STUDENT-tf-scratch"   # must be globally unique — edit!
  tags = {
    Project   = "cloud-course"
    ManagedBy = "terraform"
  }
}

output "bucket_name" {
  value = aws_s3_bucket.scratch.bucket
}
terraform init
terraform plan          # read EVERY line of the plan. This is the discipline.
terraform apply
aws s3 ls | grep scratch
cat terraform.tfstate | head -30    # look, but never touch

Now the three lessons that make IaC click:

  1. Drift: delete the bucket in the console. terraform plan → it wants to re-create it. Terraform reconciles reality to code.
  2. Change: add a tag in the code, apply → in-place update (~).
  3. Replacement: change the bucket name → destroy-and-create (-/+). Some changes are surgery, not settings — plan warns you which.
terraform destroy       # leave nothing behind

4.3 Variables, outputs, data sources, count

variables.tf / terraform.tfvars / outputs.tf; data sources look things up instead of creating them:

variable "instance_type" {
  type        = string
  default     = "t3.micro"
  description = "Size of the demo instance"
}

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }
}

resource "aws_instance" "demo" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type
  tags          = { Name = "tf-demo", Project = "cloud-course" }
}

Lab 4.3 — Parameterised instance

Build the above in ~/labs/module4/ec2, apply, SSH in, destroy. Then change instance_type via -var on the CLI and re-apply — watch plan propose a stop/ change/start.

4.4 Read the course's own source code

The payoff. Clone this repo on lab-box and open terraform/:

git clone https://github.com/helhindi/devops-course.git ~/course && cd ~/course/terraform
less main.tf

Guided reading, student explains each block back to the instructor:

  • the VPC/subnet/IGW/route table (module 2 made flesh),
  • the security group (the 8080 rule you added in lab 2.4b — it's here now),
  • both EC2 instances, their user_data boot scripts, and the IAM role from module 3,
  • the optional S3+CloudFront course-site stack (module 3's production pattern).

Checkpoint: student predicts, without applying, what terraform plan will say (should be: no changes). Then they make one real change — e.g. add a tag to monitor-box or open a port they'll need in module 7 (3000/9090 from admin IP) — submit it as a git commit, and apply it with the instructor. Their first infrastructure change through code review.

4.5 Terraform hygiene (know these exist; don't drown in them yet)

  • Remote state: S3 backend + DynamoDB lock (show the instructor's real backend config for the production PWA — actual backend "s3" block).
  • terraform fmt and terraform validate — run in CI (module 6 does exactly this).
  • Modules: reusable folders of resources; registry modules for VPCs etc.
  • Workspaces / separate state per environment (dev/prod).
  • Never commit state or .tfvars with secrets; .gitignore from day one.

Quiz

1. What exactly does terraform plan promise you? A diff between code and (refreshed) state/reality — what would be created, changed in place, or destroyed/replaced — without touching anything.
2. Someone "quickly fixed" a security group in the console. What happens and what should happen? Drift: next plan will propose reverting it. The fix should have been a code change; either codify their change or let Terraform revert it — but decide deliberately.
3. Why is state kept in S3 with locking on real teams? Shared source of truth for everyone/CI, durability, and the lock prevents two concurrent applies corrupting state.
4. Which is riskier in a plan: ~ or -/+, and why? -/+ — the resource is destroyed and recreated (new IDs, possible data loss, downtime); ~ is an in-place update.