The first thing this track does on AWS creates no compute, no network and no storage worth mentioning — it creates the guard rails every later cloud item assumes. MFA goes on the root login and the root login goes in a drawer; an IAM Identity Center user becomes the thing you sign in as; a zero-spend budget wires the first cent of real spend to an SNS topic and an e-mail; and a CloudTrail management trail starts writing down who did what. You will be looking at four console pages (IAM, IAM Identity Center, Budgets, CloudTrail) and one terminal. Everything on this page reads ~$0.00/mo — and the scene below is about why each box is free, because the next cloud items are not.
One box per resource — the AWS name on the first line, the Terraform type on the second (none of it is
Terraform yet; that arrives in cloud-03, and this budget is made by hand on purpose, so it exists
before the first terraform apply). Solid boxes exist; dashed ones do not yet. Pick a state of
the account and the readout recomputes an approximate monthly line — prices and free-tier rules are
illustrative (verify on the pricing page today); what to remember is which box would cost money
if it were bigger, and which box protects you.
Every later cloud guide opens with "cloud-02's zero-spend budget alarm exists and has e-mailed you once". This is cloud-02, so that line is what you are about to make true, not something to check. What you need instead:
0. Pick a default region and write it down. This track uses ap-south-1 (Mumbai) as
the example. Every resource in cloud-03 onwards goes there; the console's region selector (top right) and
the CLI profile in step 3 both say so. Two things ignore that choice, and knowing which is the first
interview-grade fact on this page: Billing and Budgets are global and their API lives in
us-east-1, and IAM (users, roles, policies) is global too. A budget made "in Mumbai" is the
same budget, and it counts spend from every region.
1. MFA on root, and no root access keys. Sign in to the console as the root user. Top-right menu →
Security credentials. Under Multi-factor authentication (MFA) press Assign MFA
device, pick Passkey or security key (or Authenticator app), name it
root-mfa and finish the enrolment. On the same page, under Access keys: if any key
is listed, Deactivate it, then Delete it — the root user should own zero access keys,
ever. You will confirm both facts from the CLI in Verify.
2. IAM Identity Center: one user, one permission set, one assignment. Still as root, open
IAM Identity Center, set the region selector to ap-south-1 (Identity Center is
enabled in exactly one region — its "home" — and this is it), press Enable; if it asks, let it
create an AWS Organization (free; it is what Identity Center hangs off). Then, in this order:
abhishek), your e-mail, first and last name;
leave "Send an email to this user with password setup instructions" selected. You will get an invitation
e-mail; accept it in a private window, set a password and enrol MFA for this user.AdministratorAccess. Set the session duration to 4 hours. This is
temporary: it is the admin you need to set up the state backend in cloud-03 (which lists the
backend's least-privilege S3 policy for reference); narrow it to a Terraform-only set once the track's
resources are known, after cloud-07's teardown. Note the date; a permission set that says "temporary" for six
months was never temporary.https://d-xxxxxxxxxx.awsapps.com/start) — step 3 needs it. Now sign out of root.3. AWS CLI v2 and aws configure sso. On the Mac:
# install and check -- it must say aws-cli/2.x; v1 does not know about SSO sessions brew install awscli aws --version # one profile, backed by an SSO session -- no long-lived key ever lands in ~/.aws/credentials aws configure sso # SSO session name (Recommended): lbv # SSO start URL [None]: https://d-xxxxxxxxxx.awsapps.com/start <- the access portal URL from step 2 # SSO region [None]: ap-south-1 <- Identity Center's home region # SSO registration scopes [sso:account:access]: <- Enter (if it shows [None], type sso:account:access) # (a browser tab opens: sign in as the Identity Center user, approve the request) # (one account, one permission set -- both picked for you) # Default client Region [None]: ap-south-1 <- the region you wrote down in step 0 # CLI default output format [None]: json # Profile name [AdministratorAccess-123456789012]: lbv-admin <- the default's form varies by CLI version; type lbv-admin export AWS_PROFILE=lbv-admin # put this in ~/.zshrc; every later cloud item assumes it aws sts get-caller-identity
The proof line is the Arn — it must name an assumed role, not :root:
{
"UserId": "AROAEXAMPLEEXAMPLE:abhishek",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_AdministratorAccess_9f2c1e7d4b6a8c30/abhishek"
}
When the 4-hour session expires, aws sso login opens the browser again.
That is the whole point: the credentials on disk are short-lived tokens, and there is nothing in
~/.aws/credentials to leak.
4. The SNS topic and the e-mail subscription — made first, because the budget in step 5 points
at the topic's ARN. It lives in us-east-1, the same place the Budgets API lives, so nothing
crosses regions:
aws sns create-topic --name billing-alarms --region us-east-1 # -> { "TopicArn": "arn:aws:sns:us-east-1:123456789012:billing-alarms" } -- your account id, not this one # let the Budgets service publish to it; the policy REPLACES the default one, so the owner statement stays in aws sns set-topic-attributes --region us-east-1 \ --topic-arn arn:aws:sns:us-east-1:123456789012:billing-alarms \ --attribute-name Policy --attribute-value file://sns-policy.json # the e-mail subscription -- replace the placeholder with the inbox you read aws sns subscribe --region us-east-1 \ --topic-arn arn:aws:sns:us-east-1:123456789012:billing-alarms \ --protocol email --notification-endpoint <your-e-mail> # -> { "SubscriptionArn": "pending confirmation" } # open the "AWS Notification - Subscription Confirmation" e-mail and press Confirm subscription. # THAT e-mail is the "has e-mailed you once" every later cloud page leads with.
# sns-policy.json -- ":root" in an ARN principal means "the account", not the root login you just locked away;
# the two conditions are the ones the Budgets docs give, so only THIS account's budgets can publish here
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "OwnerFullAccess",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "SNS:*",
"Resource": "arn:aws:sns:us-east-1:123456789012:billing-alarms"
},
{
"Sid": "AllowBudgetsToPublish",
"Effect": "Allow",
"Principal": { "Service": "budgets.amazonaws.com" },
"Action": "SNS:Publish",
"Resource": "arn:aws:sns:us-east-1:123456789012:billing-alarms",
"Condition": {
"StringEquals": { "aws:SourceAccount": "123456789012" },
"ArnLike": { "aws:SourceArn": "arn:aws:budgets::123456789012:*" }
}
}
]
}
5. The zero-spend budget — in the console. Open Billing and Cost Management → Budgets →
Create budget. It is one page. At the top, Budget setup offers two radio buttons: leave
Use a template (simplified) selected. Under Templates pick the first card, Zero spend
budget — its caption says it notifies you after your spending exceeds the AWS Free Tier limits. The
Budget name field is prefilled; change it to zero-spend. The Email recipients
box takes up to ten addresses; type the one you confirmed in step 4. The card explains what the template
fixes for you — at the time of writing, a monthly cost budget of $1.00 with an alert at $0.01 of
actual spend; read the card, it is the definition. Press
Create budget; the list page now shows zero-spend with "Current: $0.00" and
"Budgeted: $1.00". That is the whole console act.
Why a $1 limit with a $0.01 alarm is the zero-spend pattern: a budget must have a non-zero limit, and an absolute notification threshold of one cent on that limit fires the moment any real spend is recorded — so the limit is a formality and the threshold is the alarm.
The same budget from the CLI, for the record — and so that it also publishes to the SNS topic from step 4 (the console template only sends e-mail directly). Run it, or read it as the definition of what the console page made:
aws budgets create-budget --region us-east-1 --account-id 123456789012 \
--budget file://budget.json \
--notifications-with-subscribers file://subs.json
# if the console budget already exists under the same name this returns DuplicateRecordException --
# either delete the console one first, or name this one zero-spend-sns; budgets that only notify are free.
# budget.json
{
"BudgetName": "zero-spend",
"BudgetType": "COST",
"TimeUnit": "MONTHLY",
"BudgetLimit": { "Amount": "1", "Unit": "USD" },
"CostTypes": { "IncludeCredit": false, "IncludeRefund": false }
}
# subs.json -- ACTUAL spend, absolute one-cent threshold, delivered to the SNS topic (and its e-mail subscriber)
[
{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 0.01,
"ThresholdType": "ABSOLUTE_VALUE"
},
"Subscribers": [
{ "SubscriptionType": "SNS", "Address": "arn:aws:sns:us-east-1:123456789012:billing-alarms" }
]
}
]
IncludeCredit: false matters if the account holds promotional credits:
with credits counted, spend can sit at $0.00 for months while the credits drain — and the alarm never
fires. Counting gross spend is what "zero-spend" should mean. This is not hypothetical: an account opened on
AWS's current free plan starts with promotional credits (up to $200, for six months, after which the
account closes on its own unless upgraded to the paid plan — approx., verify on the Free Tier page today), so
with IncludeCredit: true the console template's alarm would stay silent for the whole track.
# For later (cloud-03 onwards) -- the same resource as Terraform would write it. Comments only, on purpose:
# this budget is created by hand so that it exists BEFORE the first terraform apply, not as part of one.
#
# resource "aws_budgets_budget" "zero_spend" {
# name = "zero-spend"
# budget_type = "COST"
# limit_amount = "1"
# limit_unit = "USD"
# time_unit = "MONTHLY"
# notification {
# notification_type = "ACTUAL"
# comparison_operator = "GREATER_THAN"
# threshold = 0.01
# threshold_type = "ABSOLUTE_VALUE"
# subscriber_sns_topic_arns = ["arn:aws:sns:us-east-1:123456789012:billing-alarms"]
# }
# }
6. CloudTrail: one multi-region management-events trail, into a bucket only CloudTrail can write.
The bucket name must be globally unique — change yourname-x7q2 to something of yours, in the
shell variable and in the policy file:
B=lbv-cloudtrail-yourname-x7q2 # the bucket, in the default region, closed to the public four ways aws s3api create-bucket --bucket "$B" --region ap-south-1 \ --create-bucket-configuration LocationConstraint=ap-south-1 aws s3api put-public-access-block --bucket "$B" \ --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true aws s3api put-bucket-policy --bucket "$B" --policy file://trail-bucket-policy.json # the trail: management events from every region, first copy free; then switch it on aws cloudtrail create-trail --name lbv-management --s3-bucket-name "$B" \ --is-multi-region-trail --region ap-south-1 aws cloudtrail start-logging --name lbv-management --region ap-south-1
# trail-bucket-policy.json -- exactly the two statements CloudTrail needs; the SourceArn condition pins it to THIS trail
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": { "Service": "cloudtrail.amazonaws.com" },
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::lbv-cloudtrail-yourname-x7q2",
"Condition": { "StringEquals": {
"aws:SourceArn": "arn:aws:cloudtrail:ap-south-1:123456789012:trail/lbv-management" } }
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": { "Service": "cloudtrail.amazonaws.com" },
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::lbv-cloudtrail-yourname-x7q2/AWSLogs/123456789012/*",
"Condition": { "StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"aws:SourceArn": "arn:aws:cloudtrail:ap-south-1:123456789012:trail/lbv-management" } }
}
]
}
What it costs, approx., verify on the pricing page today: the first copy of management events in a trail is free; the objects it writes are a few MB a month of S3 storage (fractions of a cent after any free tier). Data events (every S3 GetObject, every Lambda invoke) are the paid kind — this trail records none, and the ECR push in cloud-05 will still be visible as a management event.
aws sns list-subscriptions-by-topic
--region us-east-1 --topic-arn arn:aws:sns:us-east-1:123456789012:billing-alarms prints a real
"SubscriptionArn": "arn:aws:sns:us-east-1:…:billing-alarms:…", not
"PendingConfirmation".aws budgets describe-budgets --region us-east-1 --account-id 123456789012 lists
"BudgetName": "zero-spend" with "BudgetLimit": { "Amount": "1", "Unit": "USD" }
— that line is the proof the budget exists; the console's Budgets page shows the same row with
"Current: $0.00".aws cloudtrail get-trail-status --name lbv-management --region ap-south-1 prints
"IsLogging": true. A few minutes later, aws s3 ls s3://$B/AWSLogs/ --recursive
| head shows the first gzipped log objects.aws sts get-caller-identity — the Arn contains
assumed-role/AWSReservedSSO_AdministratorAccess_, not :root.aws iam get-account-summary --query 'SummaryMap.[AccountMFAEnabled,AccountAccessKeysPresent]'
prints [1, 0] — MFA on root, zero root access keys.Optional, one look: Billing → Cost Anomaly Detection — confirm a monitor exists for the account (recent accounts get one by default; approx., verify there today). It is a second, slower backstop; the zero-spend budget is the one that fires first.
None — this stays. Everything on this page is the guard rail every later cloud item assumes:
the budget alarm is the precondition line of cloud-03 onwards, the Identity Center login is what every
later terraform apply runs as, and the trail is what tells you afterwards what an apply did.
Nothing here has a red ring, and nothing here is torn down after the session.
What remains and what it costs, approx., verify on the pricing page today: the budget (a budget that only notifies is free; only budgets with actions are charged after the first two), the SNS topic and one e-mail subscription (the first thousand e-mail notifications a month are free), IAM Identity Center (free), and one management-events trail (first copy free) writing a few MB a month into an S3 bucket. Rounded: ~$0.00/mo. If you ever do close the account, do it from the root login — the one thing it is still for.
There is no teardown box on this page, because there is no teardown. Press the button once the five Verify lines above are true.
This is self-attestation — the site cannot see your AWS account, so pressing the button is you telling The Path the alarm exists and has e-mailed you once.
us-east-1 whatever your default region is, because Billing
is global. The one cost trap it hides: credits. A budget that counts credits stays at $0.00 while they
drain, and tells you nothing until they are gone.