AWS CloudTrail + Detection Foundation Lab (Terraform)
The logging and detection half of the AWS labs. The sibling ir-scenario
lab builds the attack surface (an over-privileged role, a forgotten static key, a
victim EC2). This lab turns on the lights: it records every API call, alerts on
the dangerous ones in near-real-time, and lets you run SQL over the history to
reconstruct exactly what an attacker did.
Last verified2026-06
CloudTrail (multi-region) ├─► S3 bucket durable log archive ──► Athena (SQL / threat hunting) └─► CloudWatch Logs ──► metric filters ──► alarms ──► SNS (real-time alerts) GuardDuty managed threat detection (reads CloudTrail/VPC/DNS for you)
It is one flat Terraform module (not the staged 00/01/02 of ir-scenario) because
nothing here depends on anything else — it's a single detection plane you stand up
once.
Memory hookThe mental modelCloudWatch alarms answer "is something happening right now?"; Athena answers "show me everything that principal did last Tuesday"; GuardDuty answers "does AWS think this looks like a known attack?" You want all three, and they all feed off the one CloudTrail.
What this costs
Last verified2026-06 — verify against current AWS pricing before a long run.
| Thing | Cost |
|---|---|
| CloudTrail management events (one trail) | Free — the first copy of management events per account is free |
| S3 log storage | Pennies (logs are small; a 30-day lifecycle rule deletes them) |
| CloudWatch alarms | ~$0.10 / alarm / month × 5 alarms ≈ $0.50/mo |
| CloudWatch Logs ingestion | Pennies at lab volume (14-day retention) |
| GuardDuty | Free for 30 days per account, then usage-based — disable after |
| Athena | $5 / TB scanned; lab data is tiny and queries prune by region/date → pennies |
| S3 data events (optional) | $0.10 / 100k events — off by default (enable_s3_data_events) |
Comfortably inside a $180 credit. Still, terraform destroy when you're done.
Prerequisites
Same toolchain as ir-scenario (see that lab's README Step 0–1 if you haven't set
up the CLI + direnv). You need:
- A throwaway sandbox AWS account and a non-root
adminCLI profile (ir-lab). - An S3 state bucket. Reuse the one
ir-scenario/00-bootstrapalready created (ir-lab-tfstate-<account-id>). If you skipped that lab, run its00-bootstraponce — it's pennies — or pointbackend.hclat any S3 bucket you own. - To get the most out of it, have the
ir-scenariolab deployed so you have real attacker activity to detect. The detections work on their own, but the exercises in SCENARIO.md replay that lab's attacks and watch them surface here.
Spin up
cd cloud/aws/labs/cloudtrail-detection
cp .envrc.example .envrc && direnv allow # AWS_PROFILE=ir-lab, AWS_REGION=us-east-1
cp backend.hcl.example backend.hcl # paste your state bucket name into it
terraform init -backend-config=backend.hcl
terraform apply # optionally: -var 'alert_email=you@example.com'
terraform outputUseful variables (all optional):
terraform apply -var 'alert_email=you@example.com' # get the alarm emails (confirm the SNS sub)
terraform apply -var 'enable_s3_data_events=true' # also log S3 object reads (the exfil step). Costs a little.
terraform apply -var 'enable_guardduty=false' # if you've already used the 30-day trialCloudTrail back-fills nothingIt only records events that happen after the trail exists. Stand this up first, then run the
ir-scenarioattacks — events take ~5–15 min to land in CloudWatch Logs and a bit longer in S3.
Use it
Full walkthrough — what each detection catches, how to replay the ir-scenario
attacks and watch them light up, the Athena hunt queries, and the interview Q&A —
is in SCENARIO.md. The short version:
# Real-time: tail the audit log as you act in another shell
aws logs tail "$(terraform output -raw log_group)" --follow
# See your alarms
aws cloudwatch describe-alarms --alarm-name-prefix ir-lab- \
--query 'MetricAlarms[].{name:AlarmName,state:StateValue}'
# History: Athena console → workgroup from `terraform output athena_workgroup`
# → run the saved query "00 - create cloudtrail_logs table" ONCE, then the
# "01..04" investigation queries.Tear down
terraform destroyforce_destroy = true on the buckets lets Terraform empty them first. GuardDuty's
detector is deleted with everything else. Then confirm the CloudTrail, CloudWatch
Alarms, and GuardDuty consoles read clean.
If you tore down
ir-scenario's00-bootstrapstate bucket but it still holds THIS lab's state key, destroy this lab first. Order across labs:cloudtrail-detection→ir-scenario 02 → 01 → 00.
Notes & gotchas
This lab uses exactly
one trail with management events. enable_s3_data_events is the only thing here
that meaningfully adds cost.
Attackers pick odd regions hoping you only log
us-east-1. is_multi_region_trail = true captures all of them; global services
(IAM, STS) log into us-east-1 regardless.
(enable_log_file_validation). CloudTrail writes
signed digest files so you can later prove the logs weren't altered — chain of
custody for the S3 archive.
aws:SourceArnto this account's trail — the textbook fix for the S3 "confused deputy" (another account naming your bucket as their log destination).
region and date.That's not cosmetic — it's what
keeps "scan the whole bucket" from becoming a real (if small) bill. Widen the
date filter only as far back as you need.