Skip to content

Phase 3 · CLOUD

AWS account hygiene — IAM, MFA, budgets, cost tagging

Day 47 of 90 ~55 min 0/20 in phase Builds on Day 46

By the end of today

  • Lock down the AWS root user and turn on MFA
  • Create a least-privilege IAM user and separate users, roles and policies
  • Set a budget alert and tag resources for cost tracking

AWS account hygiene: root, IAM, and the bill you never want

Section 1 of 5 · ~3 min

Six months into an AWS account, the difference between a calm team and a frightened one is almost never the code — it is the account hygiene set up on day one. Two things go wrong on neglected accounts: someone’s over-powered credentials leak, or a forgotten resource quietly runs up a bill nobody notices until the invoice lands. Today you close both doors, and all of it is free.

The root user is the master key — lock it away. When you create an AWS account you get a root user, tied to the sign-up email, that can do literally anything: close the account, change billing, delete every resource. You cannot scope it down. So you do three things and then stop touching it — set a strong unique password, turn on MFA (multi-factor authentication) so a stolen password alone is useless, and from then on sign in as a far weaker identity for daily work. AWS’s own guidance is blunt: use root only for the handful of tasks that truly require it.

IAM is how everyone else signs in. IAM (Identity and Access Management) has three pieces worth separating in your head:

  • A user is a long-lived identity for a person or a script, with its own password and/or access keys.
  • A role is an identity with permissions but no permanent credentials — it is assumed temporarily (by a user, an EC2 instance, or an AWS service), which hands back short-lived tokens that expire. Roles are how you avoid pasting long-lived keys onto servers.
  • A policy is a JSON document listing which actions (s3:GetObject) on which resources are Allowed or Denyed. You attach policies to users and roles; a policy alone does nothing.

The rule that ties them together is least privilege: grant the smallest set of permissions a task needs, nothing more.

The account-hygiene walk: lock the root user with MFA, create an IAM user, attach a least-privilege policy, then set a budget alert as the bill guardrail. Lock root MFA + strong password Create IAM user your daily identity Attach policy least privilege Set budget email alert
The four free moves you run in today's lab — the hygiene every AWS account needs on day one.

Real world: Think of a hotel. The root user is the owner’s master key that opens every door, the safe and the front desk — you keep it in a vault and use it almost never. IAM users are staff keycards, each cut for just the floors that person works. A role is the temporary keycard a contractor is handed at reception, coded to expire at 5 p.m. Policies are the little permission list encoded on each card.

The cost of getting this wrong is real. In the 2019 Capital One breach, an over-permissioned role let an attacker who reached one server read data from S3 buckets it never needed — a textbook least-privilege failure that exposed over 100 million records. Scope every identity down and a single compromised credential leaks far less.

The other door is money. AWS bills by the second and never asks “are you sure?” — a forgotten instance runs all month. Two free habits stop the surprise: an AWS Budget that emails you the moment forecast or actual spend crosses a threshold you set, and cost allocation tags — key/value labels like team=platform or env=dev on every resource — so the bill breaks down by team or project instead of one opaque number. Budgets are your smoke alarm; tags are the itemised receipt.

Today you do all four: lock the root, create an IAM user, prove least privilege, and set a $5 budget alert.

Hands-On Lab

Section 2 of 5 · ~4 min

What this costs: ₹0. IAM users, roles, policies, MFA and AWS Budgets are all free — create as many as you like without a charge. This lab launches no EC2, no S3, nothing billable, so there is no resource to terminate. The one $5 budget you set is itself free; it exists precisely to email you before a real charge ever surprises you — so keep it. We still delete the throwaway practice IAM user at the end, purely to keep the account tidy.

Budget about 25 minutes. First, two steps in the AWS console (they cannot be done from the CLI). Sign in as your root user, open IAM → My security credentials, and under Multi-factor authentication (MFA) assign an authenticator app — then sign out and stop using root. Still in IAM → Users, create your everyday admin user (admin-you), attach the AWS-managed AdministratorAccess policy, give it its own MFA, and generate an access key for CLI use. Now open your terminal with the AWS CLI v2 installed and run the rest as that IAM user. Account IDs, ARNs and user IDs below are examples — yours will differ.

# 1. Configure the AWS CLI v2 with the access key from your NEW IAM admin user
#    (not the root user). It writes ~/.aws/credentials and ~/.aws/config.
aws configure
# AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name [None]: ap-south-1
# Default output format [None]: json
# 2. Confirm who the CLI thinks you are — the ARN should be your IAM user, not root.
aws sts get-caller-identity
# Output (yours will differ — account ID, user ID and ARN are your own):
# {
#     "UserId": "AIDAEXAMPLEUSERID",
#     "Account": "123456789012",
#     "Arn": "arn:aws:iam::123456789012:user/admin-you"
# }
# 3. Create a throwaway practice user to see users, policies and least privilege in action.
aws iam create-user --user-name deploy-bot
# Output (the ARN and date are yours):
# {
#     "User": {
#         "Path": "/",
#         "UserName": "deploy-bot",
#         "UserId": "AIDAEXAMPLEDEPLOYBOT",
#         "Arn": "arn:aws:iam::123456789012:user/deploy-bot",
#         "CreateDate": "2026-07-11T09:14:22+00:00"
#     }
# }
# 4. List every IAM user in the account — your admin user and the new deploy-bot.
aws iam list-users --query 'Users[].UserName' --output table
# Output:
# ------------------
# |   ListUsers    |
# +----------------+
# |  admin-you     |
# |  deploy-bot    |
# +----------------+
# 5. Least privilege in action: grant deploy-bot ONLY read-only S3, nothing more.
aws iam attach-user-policy \
  --user-name deploy-bot \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# Output: (no output, exit code 0 — the policy is attached)
# 6. Verify exactly what deploy-bot can do — one AWS-managed read-only policy.
aws iam list-attached-user-policies --user-name deploy-bot
# Output:
# {
#     "AttachedPolicies": [
#         {
#             "PolicyName": "AmazonS3ReadOnlyAccess",
#             "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
#         }
#     ]
# }
# 7. Tag the identity — the same key/value habit you apply to billable resources so
#    Cost Explorer can break the bill down by team and environment.
aws iam tag-user --user-name deploy-bot \
  --tags Key=team,Value=platform Key=env,Value=dev
# Output: (no output on success — confirm with: aws iam list-user-tags --user-name deploy-bot)
# 8. Set a $5/month cost budget that emails you at 80% of ACTUAL spend. Use YOUR account ID.
aws budgets create-budget \
  --account-id 123456789012 \
  --budget '{"BudgetName":"monthly-5-usd","BudgetLimit":{"Amount":"5","Unit":"USD"},"TimeUnit":"MONTHLY","BudgetType":"COST"}' \
  --notifications-with-subscribers '[{"Notification":{"NotificationType":"ACTUAL","ComparisonOperator":"GREATER_THAN","Threshold":80},"Subscribers":[{"SubscriptionType":"EMAIL","Address":"you@example.com"}]}]'
# Output: (no output on success)
# 9. Confirm the budget exists and is armed.
aws budgets describe-budgets --account-id 123456789012 \
  --query 'Budgets[].BudgetName' --output text
# Output:
# monthly-5-usd
# 10. Clean up the throwaway user. You MUST detach its policy before AWS lets you delete it.
aws iam detach-user-policy --user-name deploy-bot \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
aws iam delete-user --user-name deploy-bot
# Output: (no output on success — deploy-bot is gone)
# 11. Confirm only your admin user remains. KEEP the $5 budget — it's your bill guardrail.
aws iam list-users --query 'Users[].UserName' --output table
# Output:
# ------------------
# |   ListUsers    |
# +----------------+
# |  admin-you     |
# +----------------+

Read it back: root is locked behind MFA and set aside, you sign in as a scoped IAM user, deploy-bot proved that a policy grants exactly the permissions you attach — no more — and a free budget now watches your spend. Lock, scope, watch: the three moves every AWS account needs on day one.

Common Errors & Fixes

Section 3 of 5 · ~2 min

These trip up almost everyone in their first hour on a fresh AWS account. Read the error text slowly — parsing it is the actual skill.

Common error: Running any aws command before the CLI has credentials — the very first call prints:

Unable to locate credentials. You can configure credentials by running "aws configure".

Why: The CLI looks for keys in ~/.aws/credentials, environment variables, or an instance role — and finds none. Installing the CLI does not log you in; you still have to hand it an access key from your IAM user.

Fix: Run aws configure and paste the Access Key ID and Secret Access Key you generated for your IAM admin user (step 1), then a default region like ap-south-1. Re-run aws sts get-caller-identity to confirm.

How you’d spot it in prod: A CI job or script failing on its first AWS call with “Unable to locate credentials” almost always means the runner has no key configured or the IAM role wasn’t attached — check the environment, not the command.

Common error: Signing in as a scoped user, then trying an IAM action that user isn’t allowed to perform:

An error occurred (AccessDenied) when calling the CreateUser operation: User: arn:aws:iam::123456789012:user/deploy-bot is not authorized to perform: iam:CreateUser on resource: arn:aws:iam::123456789012:user/new-user because no identity-based policy allows the iam:CreateUser action

Why: This is least privilege working exactly as designed. deploy-bot only has AmazonS3ReadOnlyAccess, so any IAM write action is denied by default — AWS denies everything that isn’t explicitly allowed.

Fix: Run IAM-management commands as an identity that actually has the permission (your admin-you user with AdministratorAccess), or attach a narrower IAM policy to the user that genuinely needs it. Never reflexively grant AdministratorAccess to make the error go away.

How you’d spot it in prod: An AccessDenied naming a specific action and “no identity-based policy allows” it is a permissions gap, not an outage. The message tells you the exact action to add to a policy — grant only that, not blanket admin.

Common error: Trying to delete an IAM user that still has a policy attached:

An error occurred (DeleteConflict) when calling the DeleteUser operation: Cannot delete entity, must detach all policies first.

Why: IAM refuses to delete a user while it still has attachments — policies, access keys, group memberships — so you can’t accidentally orphan permissions or leave dangling references. The user must be fully stripped first.

Fix: Detach every managed policy (aws iam detach-user-policy …), delete any access keys and login profile, then aws iam delete-user. Step 10 does exactly this: detach AmazonS3ReadOnlyAccess, then delete.

How you’d spot it in prod: A cleanup or Terraform destroy failing with DeleteConflict means a dependency is still attached — the fix is always to remove the children (policies, keys) before the parent, not to force the delete.

AWS Account Hygiene Interview Questions

Section 4 of 5 · ~1 min

Cover the answers below and say your own version out loud first — name what root, an IAM user, a role and a policy each are before you reveal each answer. Recalling before revealing is what makes these stick when an interviewer asks them cold. The four questions and answers render right after this note.

Go Deeper

Section 5 of 5 · ~1 min

Optional extras if you have ~30 more minutes today:

  • 5 min — Read AWS’s root user best practices page and note the short list of tasks that still require root — everything else belongs to IAM.
  • 10 min — Create an IAM role instead of a user: make a role that an EC2 instance can assume, attach AmazonS3ReadOnlyAccess, and read how the trust policy differs from a user’s attached policy. This is the identity you’ll actually wire to a server tomorrow.
  • 15 min — In the Billing console, open Cost allocation tags, activate team and env as user-defined tags, then skim Cost Explorer to see how spend would group by tag — and open the IAM policy simulator to test whether deploy-bot’s policy allows s3:GetObject before you ever deploy anything.
What is the difference between an IAM user, a role and a policy? Both

They are three separate things people mix up. A user is a long-lived identity for a person or a script, with its own password and access keys. A role is an identity with permissions but no permanent credentials — it is assumed temporarily by a user, an EC2 instance or an AWS service, which hands back short-lived tokens that expire. Roles are how you avoid pasting long-lived keys onto servers. A policy is the JSON document that lists which actions on which resources are allowed or denied. Policies attach to users and roles; a policy on its own does nothing. Users and roles are who; a policy is what they may do.

Why should you stop using the AWS root user for daily work? Both

The root user is tied to the account's sign-up email and can do literally anything — close the account, change billing, delete every resource — and you cannot scope it down. If those credentials leak, the whole account is gone. So the hygiene move is: give root a strong unique password, turn on MFA, and then stop using it. For everything day to day you sign in as an IAM user or an assumed role that only has the permissions that task needs. AWS's own guidance reserves root for the handful of tasks that genuinely require it, like changing the account's support plan. Everything else goes through IAM.

How do you apply least privilege in AWS? Service

Least privilege means granting the smallest set of permissions a task actually needs, then widening only when something legitimately fails. In practice I start from a deny-by-default position — an IAM user or role with nothing — and attach a narrow policy, ideally an AWS-managed one like AmazonS3ReadOnlyAccess, or a custom policy scoped to specific actions and resource ARNs. I never hand out AdministratorAccess to a service or a script. For workloads I prefer roles over long-lived user keys, so credentials are short-lived and rotate automatically. The Capital One breach is the cautionary tale: an over-permissioned role let an attacker read data it never needed. Scope tight and a leaked credential leaks less.

How would you stop a surprise AWS bill? Product

Two free habits catch it early. First, AWS Budgets: I create a budget with a dollar limit and an email alert that fires the moment actual or forecast spend crosses a threshold — say 80 percent of $5 — so I hear about a runaway resource in hours, not on the invoice. Second, cost allocation tags: key/value labels like team=platform or env=dev on every billable resource, activated in the Billing console, so Cost Explorer breaks the bill down by team or project instead of one opaque number. Budgets are the smoke alarm; tags are the itemised receipt. Together they turn a scary end-of-month surprise into something you saw coming.

Mark Day 47 complete

Tomorrow you launch your first EC2 instance — connect over SSH and open just the right ports with a security group.

Stuck on today’s lab? Ask in Mission 90 Q&A