Back to path
IntermediateTaskFlow · Project 5 of 13 ~8h· 5 milestones

Define it all in Terraform

Continues from the last build: TaskFlow runs on managed services you assembled by clicking in the console.

In rung 4 you got TaskFlow off a single VM and onto managed AWS services: the React build sits in S3 behind CloudFront with an ACM cert and a Route 53 record, the FastAPI backend runs on App Runner, and Postgres lives in RDS.

Terraform providers, variables, and outputsRemote state with S3 and DynamoDB state lockingCodifying S3 + CloudFront + ACM + Route 53 with OACCodifying App Runner and RDS with least-privilege IAMDetecting and reconciling configuration driftReproducibility via destroy/apply round-trips

What you'll build

All of TaskFlow's AWS infrastructure (S3 + CloudFront + ACM + Route 53 frontend, App Runner backend, RDS Postgres, IAM) is defined in version-controlled Terraform with remote state and a DynamoDB lock. You can run terraform destroy followed by terraform apply and bring the entire stack back, reachable at the same domain, without touching the console. You understand providers, variables, outputs, state, and how to detect and fix drift.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

taskflow/infra/bootstrap/main.tfhcl
terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

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

resource "aws_s3_bucket" "state" {
  bucket = "taskflow-tfstate-eu-west-1"
}

resource "aws_s3_bucket_versioning" "state" {
  bucket = aws_s3_bucket.state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_dynamodb_table" "lock" {
  name         = "taskflow-tflock"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  attribute {
    name = "LockID"
    type = "S"
  }
}

Reading this file

  • bucket = "taskflow-tfstate-eu-west-1"The S3 bucket that will hold your real stack's state file; the name must be globally unique, so change it if taken.
  • status = "Enabled"Turns on S3 versioning so every state write is kept as a recoverable version, which saves you if state is corrupted.
  • hash_key = "LockID"Terraform looks for a DynamoDB item with this exact key name to hold the lock, so it must be spelled LockID.
  • billing_mode = "PAY_PER_REQUEST"Bills per lock operation instead of provisioned capacity, which costs cents because locks are tiny and rare.

Run this ONCE before anything else. It creates the bucket and table that hold remote state for the real stack. It uses local state itself (chicken-and-egg: you cannot store the state backend's state in the backend it is creating).

That's 1 of 9 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Bootstrap remote state with S3 and a DynamoDB lock

    4 guided steps

    State is Terraform's memory of what it created. If two people apply at once against shared state with no lock, they corrupt it and Terraform loses track of real resources, which is far worse than no automation at all. Remote, locked, versioned state is the difference between a toy and something you trust against production.

  2. 2

    Codify the frontend: S3 private origin, CloudFront, ACM, Route 53

    4 guided steps

    The frontend is the most click-heavy part of rung 4: bucket policy, distribution behaviors, cert validation records, and DNS all had to line up by hand. Codifying it means the cert auto-validates, the bucket stays private, and the DNS record always matches the distribution, with no drift between what you intended and what exists.

  3. 3

    Codify the backend and database: App Runner, RDS, and least-privilege IAM

    4 guided steps

    This is the compute and data core of TaskFlow. Codifying it means the DATABASE_URL is always derived from the real RDS endpoint (no copy-paste typos), the DB password comes from a sensitive variable rather than a console field, and the App Runner role can do exactly one thing: pull its image. Least privilege here is the habit that keeps later rungs safe.

  4. 4

    Add outputs and prove reproducibility with destroy then apply

    4 guided steps

    Outputs turn the stack into something you can query instead of hunting through the console. The destroy/apply round-trip is the whole point of this rung: it proves the infrastructure truly lives in code. If apply does not restore a working TaskFlow, you have undocumented manual state, and now is the time to find it, not during an outage.

  5. 5

    Detect and reconcile drift

    4 guided steps

    In any real team, someone will eventually fix something in the console under pressure. Terraform only protects you if you can spot that drift and pull reality back to code. Knowing that plan is your drift detector, and that the codebase is the source of truth, is what separates Terraform-as-theatre from Terraform-as-control.

What's inside when you start

4 starter files, ready to clone
5 guided milestones
5 full reference solutions
9 code blocks explained line-by-line
5 "is it working?" checks
3 interview questions it prepares you for

You'll walk away with

A taskflow/infra/ directory of version-controlled Terraform defining the entire rung-4 architecture (frontend, backend, database, IAM).
Remote state in S3 with a DynamoDB lock table, configured via the bootstrap module and the root backend block.
outputs.tf exposing domain, backend URL, and CloudFront distribution ID, all populated by a real apply.
A documented destroy/apply round-trip that brings TaskFlow fully back at the same domain with zero console clicks.

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building