Project Specification · Project 03 · Weight 25%
Delete Everything on Purpose
In Project 1 you built your park by hand — clicking through the console. Tonight you write it down as a recipe, hand it to the robot, and then do the scariest thing in this course: delete your park on purpose, and watch the robot grow it back identical. About 30–40 minutes, start to finish.
01The Mission
Project 1 took you maybe thirty clicks: make a bucket, upload the page, flip the public switch, paste a policy. It worked — and the knowledge of how it worked lives nowhere but your memory. Notes 12 called that a snowflake: unique, unrepeatable, and it melts. Tonight your park stops being a snowflake. You write those thirty clicks down as a recipe — one short text file — and the robot does the clicking.
Then the payoff, and it is the entire point: terraform destroy. You will type the scariest command in this course, type yes, and watch every piece of your park get deleted — on purpose, with your own hands. Then terraform apply, and about a minute later the park is back. Same bucket, same page, same URL shape. Nothing was lost, because your park lives in files now, not in your memory. The first time the teardown-and-rebuild runs clean, you'll want to show somebody. Good — that's the screenshot.
You already built everything this recipe creates, once, by hand. This project is just Project 1 again — written down.
02The Five Steps
- Install the robot. Grab Terraform for your OS from terraform.io's downloads page — it's a single file; unzip it somewhere on your PATH and you're done.
- Set up the cargo. New empty folder with two files in it:
main.tf(the recipe below — change the two YOUR PART spots) andindex.htmlnext to it. Use your real Project 1 page if you still have it; any page works. The robot signs in with the same keys as the command line — if it later complains about credentials, runaws configureonce with an access key from your account. - Wake, read, build. In that folder:
terraform init(the robot fetches its AWS plugin), thenterraform plan— read what the robot intends before anything happens; that habit is the course — thenterraform apply. It ends by printingpark_url. Open it. That's your Project 1 site, built by a robot in under a minute. - Delete everything on purpose.
terraform destroy. Read the list of what's about to die, typeyes, feel the fear. Refresh the URL — gone. Nowterraform applyagain and watch the whole park come back, identical. That round trip is the demonstration Notes 12 promised. - Take the one screenshot. It must show the destroy run and the apply run (scroll both into one terminal, or two terminals side by side) and the site live again in a browser. That's your proof the park grows from text.
# main.tf — the whole park, written down. Keep index.html next to this file. provider "aws" { # which landlord, which of their parks region = "us-east-1" } # ---- YOUR PART #1: the bucket name. Globally unique, like Project 1. ---- # "yourname-tiny-park" works. Do NOT keep this example name — it's taken. resource "aws_s3_bucket" "park" { bucket = "yourname-tiny-park" } # tell the bucket it's a website, and which page is the front door resource "aws_s3_bucket_website_configuration" "park" { bucket = aws_s3_bucket.park.id index_document { suffix = "index.html" } } # buckets are born private (Project 1, the hard way). These four # switches are the "Block all public access" page, as text. resource "aws_s3_bucket_public_access_block" "park" { bucket = aws_s3_bucket.park.id block_public_acls = false block_public_policy = false ignore_public_acls = false restrict_public_buckets = false } # the public-read policy you once pasted into the console, as text. # the /* means "every object in the bucket", not the bucket itself. resource "aws_s3_bucket_policy" "park" { bucket = aws_s3_bucket.park.id depends_on = [aws_s3_bucket_public_access_block.park] policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = "*" Action = "s3:GetObject" Resource = "${aws_s3_bucket.park.arn}/*" }] }) } # ---- YOUR PART #2 (optional): point this at your real Project 1 page. ---- # content_type matters: without it the browser downloads instead of showing. resource "aws_s3_object" "page" { bucket = aws_s3_bucket.park.id key = "index.html" source = "index.html" content_type = "text/html" etag = filemd5("index.html") # re-upload when the page changes } # the robot prints your live URL when apply finishes output "park_url" { value = "http://${aws_s3_bucket_website_configuration.park.website_endpoint}" }
Read it out loud before you run it. Six blocks, and each one is a sentence you already know: one bucket, it's a website, the public switches off, the public-read policy, your page goes in, print me the URL. No "first", no "then" — the robot works out the order.
# 1. wake the robot — it downloads its AWS plugin into this folder terraform init # 2. read-only homework: what WOULD the robot do? (should say 5 to add) terraform plan # 3. make it so — then open the park_url it prints terraform apply # 4. the scary one. read the list, type yes, watch the park vanish. terraform destroy # 5. grow it back. same recipe, same park, about a minute. terraform apply
After the first apply, a terraform.tfstate file appears next to your recipe — the robot's notebook from Notes 12. Real teams keep it in a shared bucket with a lock; for this project, local is exactly right. Just don't delete it between destroy and apply, or the robot forgets the park is its.
You picked your provider in Week 1 and you stay with it. On Google Cloud the recipe uses google_storage_bucket (with a website block) plus google_storage_bucket_object for the page and an allUsers → Storage Object Viewer binding for the public switch. On Azure it's azurerm_storage_account with static_website plus azurerm_storage_blob into the $web container. The course repo has the full recipe for each — same six-ish blocks, same five commands, same screenshot.
03Hand In
Due Monday, November 16, 2026, 11:59 PM. Exactly two things:
- Your recipe file — the
main.tfitself, attached (plus the GCP/Azure equivalent if that's your landlord). - One screenshot showing the destroy run and the apply run (one scrolled terminal or two side by side) and the site live again in the browser.
Explicitly not required: no report, no diagram, no architecture write-up, no CI/CD pipeline, no state file. The recipe and the proof — that's the whole submission.
04Grading — Five Checkboxes
100 points, five boxes, 20 each. I grade straight off this list, fast.
- 20
- 20
- 20
- 20
- 20
05FAQ
- Wait — destroy deleted my only copy of index.html?!
- It didn't. Destroy deletes the park — the bucket in the cloud. Your
index.htmlnever left your folder; it sits right next to the recipe. The park is disposable, the recipe is forever. That is the entire lesson, and you just felt it. - Apply failed with "BucketAlreadyExists".
- Bucket names are global across every AWS customer on Earth — Project 1 taught you this the clicking way. Pick a weirder name and apply again.
- Do I leave the park running after I submit?
- Up to you — destroy it right after the screenshot if you like. The recipe is the submission; I can regrow your park for grading in one command. (Leaving an empty-ish bucket up costs about a penny, so that's fine too.)
- I'm on GCP / Azure.
- Use the full equivalent recipe from the course repo (see the blue box above). Same five commands, same destroy-then-apply round trip, same screenshot. The robot speaks every landlord's language — that's why we picked it.