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.
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:
- An AWS account you are allowed to create IAM roles, Lambda functions, and Bedrock resources in. This costs real money. Everything here is small, but model calls, OpenSearch Serverless, and Knowledge Bases are not free. Tear down after each session.
- The AWS CLI v2, configured (
aws configure) with a region. The labs default tous-east-1; override withAWS_REGION. - Bedrock model access. In the Bedrock console, open Model access and enable the models you intend to use, in the region you are working in. Nothing works until you do this, and it is the single most common reason a lab fails.
bash,zip, andpython3locally for the labs that package Lambda code.
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):
- Labs 01-10 run. Titan Text Embeddings V2 is on demand in Sydney, and
Nova Lite/Micro invocation works there (the models are listed as
profile-only, but Converse routes the bare id; if a lab ever rejects it,
set
MODEL_ID=apac.amazon.nova-lite-v1:0– theapac.prefix is the geography’s cross-region inference profile, and preflight will tell you when you need it). - Lab 11 runs. S3 Vectors is available in Sydney. If
RetrieveAndGeneraterejects the generation model, passMODEL_ARNpointing at theapac.inference-profile ARN; the deploy script forwards it. - Lab 12 does not. No Bedrock model supports fine-tuning in
ap-southeast-2, so run the customisation job inus-east-1(AWS_REGION=us-east-1 ./scripts/deploy.shand so on). The free curve-reading path has no region at all.
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.