GenAI Pro Hands-On Labs

A build-it-yourself lab track for the Generative AI Developer material in The Exam Room. Each lab gives you a working base (CloudFormation plus a couple of scripts) and asks you to build the part that matters. You deploy real AWS resources, make the thing work, prove it, and tear it down.

The idea: scaffolding that fades

The labs are ordered so the scaffolding fades as you go. Early labs are almost finished and you fill one clearly-marked gap. Later labs hand you less. The final lab hands you only data and a requirement, and you build the whole solution.

Scaffold What you’re given What you do
5/5 Everything but one function body Fill the gap, deploy, prove it
3/5 Infra and data, no application logic Wire the pieces together
1/5 Data and a requirement, a bare skeleton Design and build most of it
0/5 Data and a requirement Build it from scratch

Every lab has a solution/ you can deploy at any point (set SRC=solution), so you are never stuck. Peek when you want to; the learning is in trying first.

The arc

Do roughly one a day. Each builds on the mental model of the last, and each has a walk-through post you can read alongside it. Every lab is also downloadable as a zip if you’d rather not clone the repository.

# Lab Scaffold Walk-through Download
01 Invoke a foundation model from Lambda 5/5 Lab: Invoke a Foundation Model From Lambda zip
02 Put a Guardrail in front of a Bedrock model 5/5 Lab: Put a Guardrail in Front of a Bedrock Model zip
03 Get structured JSON out of a model with tool use 4/5 Lab: Get Structured JSON Out With Tool Use zip
04 Give a Bedrock chatbot a memory 4/5 Lab: Give a Bedrock Chatbot a Memory zip
05 RAG from scratch 3/5 Lab: Build RAG From Scratch zip
06 Wire a tool the model can call 3/5 Lab: Wire a Tool the Model Can Call zip
07 A data-quality gate 2/5 Lab: Build a Data Quality Gate zip
08 Answer a metric question with text-to-SQL 2/5 Lab: Answer a Metric Question With Text-to-SQL zip
09 Evaluate the pipeline 1/5 Lab: Evaluate the Pipeline zip
10 Capstone: data and a requirement, nothing else 0/5 Lab: The Capstone zip

The zips are built by scripts/build-lab-zips (also make lab-zips) into zips/labs/ and committed, so they publish with the site. Re-run it whenever a lab changes.

The managed track

The first ten labs build everything by hand against the model API. The managed track does the same jobs with the managed services, and going second is the point: you already know what the service is doing for you.

# Lab Scaffold Walk-through Download
11 Stand up a Bedrock Knowledge Base 3/5 Lab: Stand Up a Bedrock Knowledge Base zip
12 Fine-tune a model and read the loss curves 2/5 Lab: Fine-Tune a Model and Read the Loss Curves zip
13 Generate the week’s creative from the box manifest 2/5 Lab: Generate the Weekly Box Art zip

Before you start

Run ./preflight.sh first (it sits at the top of this directory and ships inside every lab zip). It checks the CLI, credentials, region, Bedrock model access, quotas, and whether an organisation-level SCP is going to block you, so nothing surfaces halfway through a deploy. The model probes send three 1-token requests, costing a fraction of a US cent; SKIP_INVOKE=1 skips them.

You need:

Regions

The labs default to us-east-1, which has the widest model coverage and is the safe choice. They are not hard-wired to it: every script honours AWS_REGION, and preflight.sh probes whatever region you point it at.

Verified against ap-southeast-2 (July 2026):

Architecture

Every lab is the same shape: one CloudFormation stack, one Lambda function, one IAM role scoped to exactly what that lab needs. Amazon Bedrock itself is never deployed – on-demand inference is serverless and per-token, which is why teardown.sh deleting the stack removes everything with a meter on it.

  your terminal                      your AWS account
 ┌─────────────────┐                ┌───────────────────────────────────┐
 │ preflight.sh ───┼─ read-only ──► │                                   │
 │                 │                │  CloudFormation stack             │
 │ deploy.sh ──────┼─ create ─────► │  genai-lab-NN                     │
 │                 │                │  ┌──────────┐    ┌─────────────┐  │
 │ test.sh ────────┼─ invoke ─────► │  │  Lambda  │───►│  IAM role   │  │
 │                 │                │  └────┬─────┘    │ (scoped to  │  │
 │ teardown.sh ────┼─ delete ─────► │       │          │  this lab)  │  │
 └─────────────────┘                │       │          └─────────────┘  │
                                    └───────┼───────────────────────────┘
                                            │ bedrock-runtime (Converse)
                                            ▼
                                    Amazon Bedrock ── the model, serverless,
                                                      billed per token

What each lab’s stack adds to that base:

Lab Beyond Lambda + role
01, 03, 05, 06, 08, 09 Nothing. The whole lab is the function and the call.
02, 10 A Bedrock Guardrail and a published guardrail version.
04 A DynamoDB table (conversation memory).
07 An S3 bucket (the quarantine for rejected documents).
11 A documents bucket, an S3 Vectors vector bucket and index, a Knowledge Base with its S3 data source, and a second IAM role (the service role Bedrock assumes to ingest).
12 A bucket and the customisation job’s service role only. The training job, the custom model, and any serving capacity are created by API, which is why teardown deletes those explicitly rather than through the stack.

How a lab is laid out

lab-NN-name/
  README.md          the scenario, the requirement, your task, how to prove it
  template.yaml      the CloudFormation base setup
  src/               the code with the gap you fill (starts as a stub)
  solution/          the reference answer (deploy with SRC=solution)
  scripts/
    deploy.sh        deploy the stack and upload your code
    test.sh          prove it works
    teardown.sh      delete everything

Common flow:

cd lab-01-invoke-a-model
cat README.md                 # read the requirement
# ...edit src/ to fill the gap...
./scripts/deploy.sh           # deploy your version
./scripts/test.sh             # prove it
./scripts/teardown.sh         # clean up when done

To deploy the reference solution instead of your own edit:

SRC=solution ./scripts/deploy.sh

Cleanup, always

Every lab has scripts/teardown.sh. Run it when you finish a session. A half-deleted OpenSearch Serverless collection or a running endpoint is the kind of thing that turns a learning exercise into a surprise bill. If a stack fails to delete, check the console for a resource CloudFormation could not remove on its own (usually a non-empty bucket or a manually-attached policy).

The reaper: a backstop for the session you forget

Manual teardown is the habit; the lab reaper is the safety net for the time you don’t. Every deploy.sh tags its stack with an expiry (24 hours out, reset on each deploy), and the reaper is a scheduled Lambda you deploy once into your own account that deletes anything past its expiry:

cd lab-reaper
./scripts/deploy.sh          # on and enforcing, hourly, sweeps us-east-1

It only ever acts on resources carrying the lab-reaper:managed=true tag, so nothing else in your account is a candidate. It empties tagged buckets so non-empty ones still delete, and it catches lab 12’s out-of-band Bedrock resources (serving capacity and fine-tuned models) that a stack delete misses. Keep a stack alive longer with lab-reaper/scripts/extend.sh <stack> <hours>, or just re-run that lab’s deploy.sh. Full details, including the permissions trade-off, are in lab-reaper/README.md.

The reaper is opt-in infrastructure, not a substitute for teardown.sh: run teardown when you finish, and let the reaper cover the sessions you walk away from.