TTechclick ⚡ XP 0% All lessons
AWS · IAM · IAM SecurityInteractive · L1 / L2 / L3

AWS IAM Security: — Least Privilege, Roles and the 10 Rules That Save Your Account

One IAM policy with "Action":"*" is the single door an attacker needs into your whole AWS account. This lesson rebuilds IAM from the building blocks up — users, groups, roles, policies — then teaches the evaluation order, least privilege you can actually ship, and a 10-rule hardening checklist you can apply on Monday.

📅 2026-06-11 · ⏱ 13 min · 3 live demos · 4 infographics · 🏷 10-Q assessment + AI Tutor inline

⚡ Quick Answer

AWS IAM security for L1/L2 engineers and the SCS-C02 exam: building blocks, policy evaluation (explicit Deny > Allow > implicit Deny), least privilege with Access Analyzer, roles vs keys, and 10 hardening rules.

🎯 By the end you will be able to

Read as:

Pick where you want to start

1

Building blocks

Users, groups, roles, policies — and the request flow.

2

How it's evaluated

Deny beats Allow; reading a policy JSON safely.

3

Least privilege

Start minimal, grow with Analyzer, cap with boundaries.

4

Hardening checklist

MFA, keys, PassRole, CloudTrail — the 10 rules.

🧠 Warm-up — 3 questions, no score

Just notice which ones make you pause. We answer all three inside the lesson.

1. An IAM policy has an Allow for s3:* and a separate Deny for s3:DeleteObject on the same bucket. Can the user delete an object?

Answered in Building blocks.

2. An app on EC2 needs to read from S3 for years. What should it use to authenticate?

Answered in Least privilege.

3. A junior engineer's policy contains "Action":"*","Resource":"*". What does that grant?

Answered in How it's evaluated.

Most engineers think…

Most engineers think IAM security is just "give people the AWS Managed AdministratorAccess policy and move on," or that a leaked access key is no big deal because "we'll rotate it." So they hand out wildcards and bake long-lived keys into apps.

Wrong — and it is the most common way real AWS accounts get popped. IAM's actual job is least privilege: an identity should hold the smallest set of permissions for its task, granted through roles with temporary credentials wherever possible, evaluated by a strict order where any explicit Deny wins. A "*" policy is a single master key; a long-lived AKIA key in a repo is valid until a human notices. The whole discipline is shrinking the door and shortening the time a stolen credential is useful.

① IAM building blocks — users, groups, roles, policies

Meet Sneha, a fresh L1 cloud engineer at Flipkart. On day two she opens the IAM console and sees four words she has to get straight: users, groups, roles, policies. Get these four right and the rest of AWS security clicks; get them muddled and you will hand out a master key by accident. So let us build them up one at a time, like the pass-register at a society gate.

A user is a long-lived identity — one human (Sneha) or one application. It can have a console password and a pair of long-lived access keys (an ID starting AKIA plus a secret). A group is just a labelled bucket of users — attach a policy to the group (say, Developers) and every member inherits it, so you manage permissions once instead of per person.

A role is the one that trips people up. A role has permissions but no permanent password or key. Instead a trusted principal — an EC2 instance, a Lambda function, a user in another account, or a human signing in through SSO — assumes the role and receives temporary credentials from STS that expire (often in an hour). Think hotel: a user with an access key is owning the building and carrying a key that never changes; a role is the front desk handing you a keycard that stops working at checkout.

A policy is the JSON document that says what is allowed. There are two flavours you must never confuse. An identity-based policy is attached to a user/group/role and says what that identity may do. A resource-based policy is attached to the resource (an S3 bucket, a KMS key) and says who may touch this thing. The dead giveaway: a resource-based policy has a "Principal" field; an identity-based one does not.

👉 So far: users and groups are long-lived identities, roles hand out temporary credentials, and policies (identity-based vs resource-based) are the rules. Next: the request flow that ties them together.
Figure 1 — IAM building blocks and the request flow
An over-permissive IAM identity is one door to your whole AWS account — least privilege shrinks that door The IAM building blocks drawn as identities and the doors they open. On the left, users and groups hold long-lived access keys (a permanent key on a keyring), while a role hands out a temporary STS credential that expires. In the middle, an identity-based policy is a key cut for the holder and a resource-based policy is a lock on the door itself. On the right, the request flow shows principal then action then resource being checked against all attached policies before the door opens. Red marks the over-permissive wildcard identity that opens every door; green marks the scoped least-privilege identity that opens only one. IAM building blocks — who can open which door, and for how long Long-lived: user + group 👤 AKIA… key never expires ✗ key leaks once = valid forever ✗ root user = master key, use almost never Temporary: role + STS 🎭 ASIA… expires in ~1h ✓ AssumeRole → short-lived creds ✓ nothing to rotate, nothing to leak Identity-based policya key cut for the holder"what THIS identity may do"attached to user/role Resource-based policya lock on the door itself"who may touch THIS S3/KMS"has a Principal field The request flow 1· principal (who) 2· action (s3:GetObject) 3· resource (the bucket) checked against ALL policiesdefault = deny if nothing allows untrusted / over-permissivetrusted identitypolicy / decisionkey insightallowed
Look at the left column: a long-lived user/key (red) vs a role with expiring STS creds (green). Then follow the right column — every request checks principal → action → resource against all attached policies, and denies by default.

Now the flow. Every API call is a request with three parts: the principal (who is asking), the action (e.g. s3:GetObject), and the resource (which bucket). AWS gathers every policy attached to that identity and that resource and checks them. The default answer is no — if nothing explicitly allows the action, it is implicitly denied. You spend your IAM life carving narrow Allow holes out of that default deny.

Why do roles beat long-lived access keys? Because a leaked AKIA key is valid until a human notices and revokes it — that could be months, and meanwhile it is in someone's clipboard, a Git commit, a Slack message. A role's STS credentials (ASIA…) expire on their own, usually within an hour, so a leak self-heals. That is why AWS now tells you to use roles and federation for humans and instance/task roles for apps, and to treat access keys as a last resort.

And the root user? It is the account owner — unlimited power, and it cannot be reined in by any IAM policy or SCP. So you use it for almost nothing: only the handful of tasks that genuinely require it (changing account settings, restoring a locked-out admin, certain billing/tax actions, S3 MFA-delete). Lock it with hardware MFA, delete its access keys, and do daily work as a far less powerful role.

The four building blocks, one tap each

Tap each card. These four are the vocabulary every AWS interview and the SCS-C02 exam assumes you own cold.

👤
User + group
tap to flip

Long-lived identity; a group is a bucket of users sharing one policy. So: manage permissions on the group, not per person.

🎭
Role + STS
tap to flip

Permissions with no permanent key; a principal assumes it for temporary creds that expire. So: a leak self-heals in ~1 hour.

📄
Identity policy
tap to flip

Attached to a user/role: 'what I may do'. No Principal field. So: this is where you write least privilege.

🔒
Resource policy
tap to flip

Attached to an S3 bucket/KMS key: 'who may touch me'. Has a Principal field. So: this is how cross-account access is granted.

Daily-life analogy — Aadhaar OTP vs a photocopied ID

A long-lived access key is like handing out photocopies of your Aadhaar card — once a copy is loose, it works forever and you cannot un-copy it. A role assumed through STS is like an Aadhaar OTP: a one-time, short-lived token the system issues on demand and that expires by itself. Both prove who you are; only one limits the damage when it leaks. That single difference — does the credential expire on its own? — is why AWS pushes roles over keys.

Quick check · Q1 of 10

Rahul at Infosys finds a teammate has put an IAM user's access key directly in a Lambda function's environment variable so it can read S3. What is the better design?

Correct: a. A Lambda function should have an execution role; AWS supplies temporary, auto-rotating STS credentials at runtime, so there is no key to leak or rotate. The root key is the most dangerous credential in the account and must never be used by an app; emailing or hard-coding a long-lived key spreads a credential that never expires.

Pause & Predict

Predict: you create a brand-new IAM user with NO policies attached and the user signs in. What can they do in the account, and why? Type your guess.

Answer: Almost nothing. IAM is deny-by-default: with no identity-based policy (and no group policy, resource policy or permission boundary granting anything), every action is implicitly denied. The user can authenticate but can't list a bucket, launch an instance, or even see most of the console. Permissions only exist where you explicitly carve out an Allow — which is exactly what makes least privilege the natural state if you start from zero.

② How policies are evaluated — explicit Deny wins

Here is the single most important rule in all of IAM, and it is on the exam in three different costumes: an explicit Deny overrides any Allow. When a request arrives, AWS gathers every policy type that could apply — the SCP, the identity-based policy, any resource-based policy, the permission boundary, and any session policy — and looks for a Deny first.

The order, straight from the IAM evaluation logic: (1) is the request explicitly Denied by any policy? If yes, stop — denied. (2) If not, is it explicitly Allowed, and do all the guardrails permit it? (3) If nothing allows it, the implicit (default) Deny applies. Allow is the exception; Deny — explicit or default — is the resting state.

How the layers combine matters. Identity-based and resource-based policies in the same account are a union — an Allow in either is enough. But a permission boundary, an SCP and the identity policy are an intersection — the action must be allowed by all three. AWS states it plainly: with a boundary and an SCP present, "the boundary, the SCP, and the identity-based policy must all allow the action." That is why a developer can have AdministratorAccess attached and still be unable to touch a region an SCP blocks.

👉 So far: Deny > Allow > implicit Deny, and boundaries/SCPs intersect while identity+resource policies union. Next: actually reading a policy JSON without fear.
Figure 2 — The decision waterfall for one request
One explicit Deny anywhere wins — Allow only opens the door if no policy type says no and the default deny is beaten The IAM decision waterfall for a request inside one account. The request enters at the top. AWS gathers every policy that applies: the organization service control policy, the identity-based policy, any resource-based policy, the permissions boundary, and any session policy. First check: does ANY policy have an explicit Deny that matches. If yes, the request is denied immediately. If no explicit Deny, AWS checks whether an Allow exists and whether the SCP, boundary and identity policy all permit the action. If all allow, the request is allowed. If nothing allows it, the implicit default Deny applies and the request is denied. Amber marks the decision points, red marks denial, green marks allow. How one request is decided — Deny > Allow > implicit Deny request: principal · action · resource gather ALL policy types that applySCP / RCP · identity-based · resource-based · permission boundary · session policy Q1· does ANY policy explicitly Deny?one Deny anywhere ends it DENIEDexplicit Deny wins yes Q2· do SCP + boundary + identity ALL Allow?guardrails intersect — all three must permit no Deny DENIED (implicit)nothing allowed it → default deny ALLOWEDevery layer permitted the action missing Allow all Allow Key idea: Allow is the exception you carve out; Deny — explicit or default — is the resting state of AWS. denieddecision pointkey insightallowed
Trace top to bottom: any explicit Deny ends it (red). Only if no Deny AND every guardrail allows do you reach ALLOWED (green). Anything else falls into the implicit default deny.

Now read a policy. Every statement has four things: Effect (Allow or Deny), Action (the API calls), Resource (the ARNs it applies to), and an optional Condition (extra tests like MFA or source IP). The danger you must learn to spot in code review is "Action":"*" with "Resource":"*" — that is admin on everything, a single master key. The exam loves to hide a wildcard in a plausible-looking policy.

A least-privilege identity policy (read ONE prefix in ONE bucket) — note the scoped Action + Resource + Condition
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "ReadReportsPrefixOnly",
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::flipkart-reports",
      "arn:aws:s3:::flipkart-reports/finance/*"
    ],
    "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }
  }]
}
Expected output
$ aws s3 ls s3://flipkart-reports/finance/ --profile sneha
2026-06-10 09:14:22   18211  q4-summary.csv
$ aws s3 rm s3://flipkart-reports/finance/q4-summary.csv --profile sneha
delete failed: ... An error occurred (AccessDenied) when calling the
DeleteObject operation: User is not authorized to perform: s3:DeleteObject
Common mistake — "my Allow looks right but I still get AccessDenied"

Symptom: a developer's identity policy clearly has "Effect":"Allow" for the action, yet the call returns AccessDenied. The cause is almost always a Deny somewhere higher — an SCP at the org blocking the region or service, a permission boundary that doesn't include the action, or an explicit Deny in another attached policy. Fix: don't keep adding Allows. Run aws iam simulate-principal-policy or open IAM → Policy simulator, and check for an SCP/boundary intersection before you touch the identity policy.

Quick check · Q2 of 10

Priya at ICICI has a user with the managed AdministratorAccess policy (Allow on *). The org also has an SCP that denies all actions in ap-south-2. Can she create an EC2 instance in ap-south-2?

Correct: c. An explicit Deny in an SCP overrides any Allow, including AdministratorAccess. The identity policy and the SCP intersect, and a Deny wins outright — so the region is blocked regardless of how broad her identity permissions are. Assuming a role or waiting wouldn't change the SCP guardrail.

Pause & Predict

Predict: a bucket's resource-based policy Allows arn:aws:iam::123456789012:role/Analyst to read it, but that role's own identity policy says nothing about S3. In the SAME account, can the role read the bucket? Type your guess.

Answer: Yes. Within a single account, identity-based and resource-based policies are evaluated as a UNION — an Allow in either one is sufficient (as long as no Deny applies). The bucket policy alone allowing the role is enough, even though the role's identity policy is silent on S3. (Cross-account is stricter: there BOTH the resource policy AND the identity policy must allow it.)

③ Least privilege in practice — start minimal and grow

"Least privilege" sounds like a poster on the wall until you have to ship it. The workable method is: start from nothing and grow. Give the identity the smallest policy you think it needs, let the workload run, then use real usage data to add only what was actually used and remove what was not. AWS gives you two tools for exactly this: last-accessed information and IAM Access Analyzer.

Access Analyzer does three jobs you'll lean on. It finds external/cross-account exposure — a bucket or role shared outside your account or organization (provable security, not guesswork). It surfaces unused access — roles with no activity, unused access keys and passwords, and service- or action-level permissions a role was granted but never used. And it can generate a policy from your CloudTrail history, so you get a least-privilege starting point instead of a blank page. In 2025 AWS extended these unused-access and custom-policy-check findings to more regions (including GovCloud), a sign of how central this has become.

Two structures stop least privilege from quietly eroding as your account grows. A permission boundary caps what an identity can ever do — vital when you let a team lead create roles, because the boundary guarantees they can't mint a role more powerful than themselves (the effective permission is the intersection of the role's policy and the boundary). An SCP does the same one level up, at the AWS Organizations level, as a guardrail across whole accounts — e.g. "no one, not even an account admin, may disable CloudTrail or leave ap-south-1."

👉 So far: grow from minimal using Access Analyzer + last-accessed; cap with boundaries and SCPs. Next: conditions, and the cross-account role + trust policy model.

Conditions are how you make a broad-looking Allow safe. Three you'll use constantly: require MFA with aws:MultiFactorAuthPresent; restrict to office/VPN ranges with aws:SourceIp; and gate on tags so a policy only touches resources tagged for that team (attribute-based access control). Conditions turn "can read any prod bucket" into "can read prod buckets tagged team=payments, from the office IP, with MFA on."

Figure 3 — Access key vs role — the blast-radius comparison
A leaked long-lived access key is valid until someone notices; a leaked role credential expires on its own in an hour A two-column comparison for the same task: an application on EC2 reading from S3. Left column, the old way: an IAM user with a long-lived access key (AKIA) baked into the app. If the key leaks it stays valid forever, must be rotated by hand, and shows up in code, env files and Git. Right column, the better way: an IAM role attached to the EC2 instance profile, which the instance assumes through STS to get a short-lived credential (ASIA) that AWS rotates automatically and that expires within about an hour, so a leak self-heals. Red marks the long-lived risk, green marks the temporary-credential win, amber marks the trust policy decision. Access key vs role — same job, very different blast radius IAM user + long-lived access key AKIAIOSFODNN7EXAMPLE (no expiry) ✗ baked into app / .env / Git history ✗ leaks once → valid till YOU revoke it ✗ you must remember to rotate (90 days) ✗ same key copied to 5 servers Blast radius if leakedfull access, indefinitely, until detected IAM role assumed via STS trust policy: "EC2 may assume me"sts:AssumeRole · principal = ec2.amazonaws.com ✓ instance profile → no key in the app ✓ STS hands back ASIA… short-lived creds ✓ AWS auto-rotates; nothing to remember ✓ leaked creds expire in ~1 hour Blast radius if leakedscoped + self-healing — expires on its own Rule of thumb: humans get federated roles, apps get instance/role credentials — keys are the last resort. long-lived risktrust decisionkey insighttemporary / safe
Read both columns for the same EC2-reads-S3 job. Left (red) = a long-lived AKIA key that's valid till revoked. Right (green) = a role assumed via STS whose ASIA creds expire on their own — a leak self-heals.

Now the model that powers almost everything cross-account: the role + trust policy. A role has two policies. Its permission policy says what the role can do once assumed. Its trust policy says who is allowed to assume it — that's the resource-based policy with the Principal field. To let an auditor in the security account read your prod account, you create a role in prod whose trust policy names the security account's principal, and grant that principal sts:AssumeRole on their side. Both sides must agree.

▶ Watch a cross-account AssumeRole, step by step

Karthik, an auditor in the security account (111111111111), needs read-only access to the production account (123456789012). Follow the temporary-credential handshake. Press Play for the healthy path, then Break it to see the failure.

① Requestauditor@111111111111 calls sts:AssumeRole on arn:aws:iam::123456789012:role/AuditReadOnly
② Trust checkprod role's trust policy says Principal = 111111111111 → who-may-assume check passes
③ STS issuesSTS returns short-lived ASIA… creds + session token, scoped to AuditReadOnly's permissions
④ Use + expireauditor reads prod with temp creds; they expire in ~1h; every call logged in CloudTrail
Press Play to step through the healthy path. Then press Break it.
AWS CLI — generate a least-privilege starting point from real activity (Access Analyzer)
aws accessanalyzer start-policy-generation \
  --policy-generation-details '{"principalArn":"arn:aws:iam::123456789012:role/etl-job"}' \
  --cloud-trail-details file://trail-window.json

# then, once it finishes:
aws accessanalyzer get-generated-policy \
  --job-id 4d2f9b1a-7c33-4f2e-9a01-6b5e8c0a1122
Expected output
{
  "jobDetails": { "status": "SUCCEEDED" },
  "generatedPolicyResult": {
    "generatedPolicies": [{ "policy": "{\"Version\":\"2012-10-17\",
      \"Statement\":[{\"Effect\":\"Allow\",
      \"Action\":[\"s3:GetObject\",\"dynamodb:PutItem\"],
      \"Resource\":[\"arn:aws:s3:::etl-src/*\", ...]}]}" }]
  }
}

Aditya at TCS faces this

Aditya, an L2 engineer, is told a contractor role can read 'any S3 bucket' and the auditors flagged it. He must cut it to least privilege without breaking the running ETL job.

Likely cause

The role was created with s3:* on Resource * months ago 'to get unblocked'. Most of that is unused — but nobody knows exactly which buckets/actions the job really touches, so no one dares trim it.

Diagnosis

Instead of guessing, he reads the role's last-accessed data and runs Access Analyzer policy generation over the last 90 days of CloudTrail to see the actions/resources actually used.

AWS Console > IAM > Roles > contractor-etl > Access Advisor (last accessed) + IAM > Access Analyzer > Generate policy
Fix

He replaces s3:* / Resource * with the generated policy: s3:GetObject + s3:ListBucket scoped to the two buckets the job actually used, with a tag and MFA condition. He keeps a permission boundary on the role so it can never re-expand.

Verify

The ETL job still runs green; a test call to an unrelated bucket now returns AccessDenied; Access Analyzer shows zero unused-permission findings for the role.

Quick check · Q3 of 10

Meera at HCL wants to safely let team leads create IAM roles for their projects, but guarantee no lead can create a role with admin powers. Which IAM feature does that directly?

Correct: b. A permission boundary sets the maximum permissions an identity (or the roles it creates, when required by policy) can ever have — the effective permission is the intersection of the policy and the boundary, so a lead literally cannot mint a role more powerful than the boundary allows. Resource policies don't cap delegation; disabling CloudTrail removes visibility; root is the opposite of least privilege.

Pause & Predict

Predict: you scope a role down to least privilege using Access Analyzer's generated policy. Six months later a new feature needs one extra action. What's the safe way to add it, and what should you NOT do? Type your guess.

Answer: Add exactly that one action (and the minimal Resource it needs), ideally with a condition, then re-check with the policy simulator and Access Analyzer. Do NOT 'unblock' by widening to a wildcard (s3:* or Action *) — that's how least privilege quietly rots back into admin. Least privilege isn't a one-time cleanup; it's a habit of adding the smallest grant each time and letting last-accessed data tell you what to remove later.

④ The hardening checklist — 10 rules + the PassRole trap

Time to turn everything into a checklist you can run on a real account on Monday. None of this is exotic — it's the boring discipline that keeps your account out of a breach write-up. We'll walk the high-value rules, hit the one privilege-escalation trap that catches even seniors, recreate the Create role screen, then leave you a cheat-sheet.

Identities & credentials. Lock the root user with hardware MFA and delete its access keys — use it only for the genuine root-only tasks (account settings, restoring a locked-out admin, certain billing/tax actions, S3 MFA-delete). Turn on MFA for every human. Rotate or eliminate access keys — better still, replace them with roles/SSO so there's nothing long-lived to rotate. And no inline admin: don't paste AdministratorAccess or "*" inline onto users; grant through groups/roles with scoped policies.

Visibility & guardrails. Keep IAM Access Analyzer on to catch public or cross-account exposure and unused access. Keep CloudTrail on — an organization trail records every AssumeRole and API call, which is both your forensic record and the feed for alarms (e.g. alert on root sign-in). Add SCPs for org-wide non-negotiables and permission boundaries wherever you delegate role creation.

Now the trap. iam:PassRole is how you attach a role to a service (give an EC2 instance or a Lambda its role). The escalation: a low-privileged user who has iam:PassRole plus a launch permission like ec2:RunInstances or lambda:CreateFunction can pass an admin role to a new instance/function and then read its credentials — becoming admin without ever editing their own policy. Security researchers list this among the most common AWS privilege-escalation paths.

Constrain iam:PassRole so a user can pass ONLY one specific role to ONE service
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PassOnlyAppRoleToEc2",
    "Effect": "Allow",
    "Action": "iam:PassRole",
    "Resource": "arn:aws:iam::123456789012:role/app-runtime-role",
    "Condition": {
      "StringEquals": { "iam:PassedToService": "ec2.amazonaws.com" }
    }
  }]
}
Expected output
# user tries to pass a DIFFERENT, more powerful role:
$ aws ec2 run-instances --iam-instance-profile Name=admin-profile ...
An error occurred (AccessDenied) when calling the RunInstances operation:
User: arn:aws:iam::123456789012:user/dev is not authorized to perform:
iam:PassRole on resource: arn:aws:iam::123456789012:role/AdminRole
🖥️ The real screen you'll use to make a role — AWS Console → IAM → Roles → Create role → Custom trust policy. (Recreated for clarity — your console matches this.)
console.aws.amazon.com · IAM · Roles · Create role
1
Trusted entity type
Custom trust policy
2
Principal (who may assume)
arn:aws:iam::111111111111:root
3
Action
sts:AssumeRole
Condition (recommended)
aws:MultiFactorAuthPresent = true
4
Permissions policy
ReadOnlyAccess (scoped, not *)
Permissions boundary
boundary-no-iam-admin
Role name
AuditReadOnly
Create role
Prove a policy is least privilege before you ship it

Don't eyeball it — test it. Use aws iam simulate-principal-policy (or IAM → Policy simulator) to confirm the actions you intend ARE allowed and the ones you don't intend are denied. Then run IAM Access Analyzer policy validation to catch overly broad statements and "Resource":"*" warnings, and check the role's Access Advisor tab after a week to remove services it never touched. Allowed-what-you-meant + denied-everything-else + zero unused findings = shippable.

Figure 4 — AWS IAM hardening — the cheat-sheet
The whole lesson on one card — the policy-evaluation order and the 10 IAM rules that keep an account out of the news A cheat-sheet card. The top band gives the evaluation order: explicit Deny beats everything, then SCP and permission boundary and identity policy must all allow, otherwise the implicit default Deny applies. Below are ten numbered IAM rules: lock the root user with MFA and use it almost never, give every human MFA, prefer roles and STS over access keys, write least privilege and grow it, never use Action star Resource star, scope with conditions like MFA and source IP, cap delegation with permission boundaries and SCPs, guard iam:PassRole, turn on IAM Access Analyzer, and keep CloudTrail on. The last tile flags the iam:PassRole plus a service that assumes it privilege-escalation trap. AWS IAM — your one-glance card Evaluation order (burn this in)explicit Deny > (SCP ∩ boundary ∩ identity all Allow) > implicit Deny (default) 1· Lock the root userhardware MFA on; no access keys;use only for the handful of root tasks 2· MFA on every humanand condition sensitive actions onaws:MultiFactorAuthPresent 3· Roles > access keysSTS short-lived creds for apps &humans; eliminate long-lived keys 4· Start minimal, growdeny by default; add one action at atime using last-accessed data 5· Never "*":"*""Action":"*","Resource":"*"= admin on everything = one door 6· Scope with conditionsMFA present, aws:SourceIp,resource tags, aws:PrincipalOrgID 7· Cap delegationpermission boundaries on the peoplewho make roles; SCPs at the org 8· Guard iam:PassRolepin which role can be passed & towhich service (iam:PassedToService) 9· Access Analyzer ONfind public/cross-account exposure+ unused roles, keys & permissions 10· CloudTrail always onorg-wide trail; every AssumeRole & APIcall recorded — your forensics + alarms ⚠ The escalation trapiam:PassRole + ec2:RunInstances /lambda:CreateFunction = low user → admin role
Your one-card revision sheet: the evaluation order on top, then the 10 rules, with the iam:PassRole escalation trap flagged in red. Keep it open in your first week and before any AWS security interview.

For the cert: this lesson maps onto Domain 4 — Identity and Access Management (16% of the SCS-C02 exam), and IAM concepts bleed into the Infrastructure Security and Data Protection domains too. The exam tests exactly what you just learned — policy evaluation order, roles vs long-lived credentials, permission boundaries vs SCPs, and reading a policy JSON to spot the over-permissive line. Get these solid and a big slice of the exam (and your first cloud-security job) is in reach.

Next: AWS Security Groups vs NACLs — stateful vs stateless
Quick check · Q4 of 10

An interviewer at PhonePe asks Neha: "A developer only has iam:PassRole and ec2:RunInstances — no admin policy. Why is that a privilege-escalation risk, and how do you fix it?"

Correct: d. iam:PassRole plus a launch permission lets the user attach an admin role to a new instance and harvest its credentials — admin without editing their own policy. The fix is to constrain iam:PassRole to specific role ARNs and add an iam:PassedToService condition. Granting admin makes it worse; disabling CloudTrail just hides the attack instead of preventing it; and 'no admin policy' is exactly why this path is sneaky.

🤖 Ask the AI Tutor

Tap any question — instant, scoped to this lesson. No login, no waiting.

Pre-curated from AWS docs + community Q&A, scoped to this lesson. For a live prod issue, paste your export into chat.techclick.in.

📝 Wrap-up assessment — six more

You've answered 4 inline. Six left. 70% (7 of 10) marks the lesson complete on your profile. Tap Submit all answers at the end.

Q5 · Remember

In AWS IAM policy evaluation, what happens when one policy explicitly Allows an action but another policy (e.g. an SCP) explicitly Denies it?

Correct: a. The foundational rule: an explicit Deny in any policy type overrides any Allow. Specificity doesn't matter; root isn't consulted per request; and there's no 'cancel out' — a single Deny ends it.
Q6 · Apply

An application running on EC2 at Zomato needs to read from an S3 bucket for the foreseeable future. Which is the most secure way to give it access?

Correct: c. An instance role gives the app auto-rotating, short-lived STS credentials with nothing to leak or rotate. Long-lived user keys (in config or source) never expire and spread; the root key must never be used by an app.
Q7 · Apply

You need to give a contractor read-only access to ONE prefix in ONE bucket, only with MFA. Which policy shape is correct?

Correct: b. Least privilege means scoping Action and Resource to exactly what's needed and adding a Condition (MFA). Wildcards on action or resource grant far more than read-one-prefix; trying to Deny 'every other bucket' is unmanageable and still leaves the broad Allow.
Q8 · Analyze

A developer's identity policy clearly Allows ec2:RunInstances, but the call returns AccessDenied. Control of certificates and the policy syntax are fine. What is the most likely cause?

Correct: d. When an Allow is present but the action is still denied, look up the stack: SCPs, permission boundaries and the identity policy must ALL allow it (intersection), and any explicit Deny wins. Root isn't required for a normal action, and this isn't a propagation-delay issue.
Q9 · Analyze

A low-privileged user at Airtel has only iam:PassRole and lambda:CreateFunction — no admin policy. Why is this a privilege-escalation path?

Correct: b. iam:PassRole plus a launch permission lets the user attach a more privileged role to new compute and harvest its credentials — admin without editing their own policy. Lambda doesn't auto-grant admin, and PassRole alone (without a service that consumes the role) isn't enough; the danger is the combination.
Q10 · Evaluate

Two ways to lock down access for an app team: (A) hand each developer the AdministratorAccess managed policy so nothing blocks them; (B) give scoped roles with temporary credentials, set a permission boundary, add SCP guardrails, and keep Access Analyzer + CloudTrail on. Which is the stronger security posture and why?

Correct: a. B applies least privilege (smallest permissions, temporary credentials), caps delegation with boundaries/SCPs, and keeps visibility on via Access Analyzer and CloudTrail — so a compromised credential does limited damage and drift is detected. A optimises for convenience by handing out admin, which is exactly the single-master-key pattern that turns one leak into a full account compromise.
Lesson complete — saved to your profile.
Almost! You need 70% (7 of 10) — re-read the path that tripped you up and tap "Try again".

🧠 In your own words

Type one line: In one line, why does a leaked IAM role credential hurt far less than a leaked IAM user access key? Then compare to the expert version.

Expert version: Because the role credential comes from STS and is short-lived (it expires on its own, typically within an hour), so a leak self-heals — while a long-lived access key (AKIA) stays valid until a human notices and revokes it, which could be months.

🗣 Teach a friend

Best way to lock it in — explain it in one line to a teammate. Tap to generate a paste-ready summary.

📖 Glossary

IAM
Identity and Access Management — the global, free AWS service that decides who (principal) can do what (action) to which resource.
Role
An identity with permissions but no permanent credentials; a trusted principal assumes it via STS for temporary, expiring credentials.
STS
Security Token Service — issues short-lived credentials (keys starting ASIA + a session token) when a principal assumes a role.
Access key
A long-lived credential pair (ID starting AKIA + secret) for an IAM user or app. No expiry — risky if leaked; prefer roles.
Identity-based policy
A JSON policy attached to a user/group/role saying what that identity may do. Has no Principal field.
Resource-based policy
A policy attached to a resource (S3 bucket, KMS key) saying who may touch it. Has a Principal field.
Explicit Deny
A Deny statement that overrides any Allow, in any policy type — the top rule of IAM evaluation.
Permission boundary
An advanced policy setting the maximum permissions an identity can ever have; effective permission = policy ∩ boundary.
SCP
Service Control Policy — an AWS Organizations guardrail that caps the maximum permissions for accounts/identities in scope; grants nothing by itself.
Trust policy
A role's AssumeRolePolicyDocument — the resource-based policy with a Principal field that names who may assume the role.
iam:PassRole
The permission to hand an IAM role to an AWS service (attach a role to an instance/function). A common privilege-escalation primitive when unscoped.
IAM Access Analyzer
Service that finds external/cross-account exposure, flags unused access, validates policies, and generates least-privilege policies.

📚 Sources

  1. AWS IAM User Guide — "Policy evaluation logic" (same-account union of identity + resource policies; intersection of identity policy, permissions boundary and SCP; explicit Deny overrides any Allow; default implicit Deny). docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html
  2. AWS IAM User Guide — "Refine permissions using last accessed information" + "Using IAM Access Analyzer" (last-accessed data, unused-access findings, external-access findings, policy generation from CloudTrail). docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_last-accessed.html · docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html
  3. AWS IAM User Guide — "AWS account root user" + "Root user best practices" (the short list of tasks that require root; lock with MFA, delete root access keys, use almost never). docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html · docs.aws.amazon.com/IAM/latest/UserGuide/root-user-best-practices.html
  4. Hacking The Cloud + Praetorian + Rhino Security Labs — AWS IAM privilege escalation via iam:PassRole with ec2:RunInstances / lambda:CreateFunction / ecs:RunTask / glue:CreateDevEndpoint (community practitioner research on the escalation primitive). hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ · praetorian.com/blog/privilege-escalation-in-aws-with-passrole-attacks/ · github.com/RhinoSecurityLabs/AWS-IAM-Privilege-Escalation
  5. AWS What's New (2025) — "IAM Access Analyzer supports additional analysis findings and checks in AWS GovCloud (US) Regions" (recent expansion of unused-access + custom-policy checks; ongoing push toward least privilege). aws.amazon.com/about-aws/whats-new/2025/07/iam-access-analyzer-findings-checks-govcloud/
  6. AWS Certified Security – Specialty (SCS-C02) Exam Guide — Domain 4 Identity and Access Management = 16% of scored content (plus IAM coverage across Infrastructure Security and Data Protection). d1.awsstatic.com/training-and-certification/docs-security-spec/AWS-Certified-Security-Specialty_Exam-Guide_C02.pdf

What's next?

You've locked down WHO can do WHAT in the account. Next we move to the network edge: two AWS firewalls that confuse everyone — Security Groups and NACLs — and the stateful-vs-stateless difference that decides which one actually blocks your traffic.