IAM Day 2 Lecture
SCS-C03 · Domain 4 · Identity and Access Management · Day 2 of 6

How AWS decides: policy evaluation logic

Yesterday you learned to read one policy. Today you learn how AWS combines all of them into one Allow or Deny, in one account and across accounts. This is the most tested idea in the whole IAM domain.

Skill 4.2.3 least privilege, boundaries, session policiesSkill 4.2.4 analyze authorization failures~90 min read + 30 min lab
01

The evaluation flowchart

Every request goes through the same decision process. AWS collects every policy that applies (RCPs, SCPs, resource-based, identity-based, permissions boundary, session policy) and then asks a fixed series of questions. Three rules decide almost every exam question: a Deny anywhere wins, every guardrail that applies must allow, and at least one grant must allow.

Concept profile

Policy evaluation logic

often confused with network ACL rule order
?What is it?

The fixed process AWS uses to combine all applicable policies into one decision: Allow or Deny.

⚙How does it work?
  • Start from deny by default.
  • Look for any explicit Deny in all policies. Found → Deny.
  • Check the RCPs, then the SCPs: each must allow.
  • Look for an Allow in resource-based or identity-based policies.
  • Check the permissions boundary and session policy if they exist.
  • All passed → Allow.
{ }Structure

Policies considered, by owner: RCPs of the account that owns the resource, SCPs of the account the principal belongs to, the resource-based policy, the principal's identity-based policies, its permissions boundary, and the session policy for temporary credentials.

⇄Policy evaluation logic vs network ACL rule order
IAM evaluationNetwork ACL
Does order matter?No. Statement and policy order are irrelevantYes. Rules run from the lowest number, first match wins
Deny vs AllowDeny always winsWhichever rule matches first wins
DefaultImplicit denyThe final * rule denies
Applies toAPI requestsNetwork packets in a subnet
!Common mistakes the exam tests
  • Thinking the first matching statement wins, like a firewall.
  • Thinking an Allow in one policy beats a Deny in another.
  • Forgetting the RCP and SCP checks happen even when the identity has AdministratorAccess.
  • Treating boundaries and session policies as grants.
✓Benefits and use cases
  • Troubleshoot AccessDenied step by step instead of guessing.
  • Design guardrails knowing exactly where they apply.
  • Explain to auditors why a request was or wasn't allowed.
Request arrives · collect every policy that applies1Is there an explicit Deny in ANY policy?YesDENY2Do the RCPs (resource's account) allow it?NoDENY3Do the SCPs (principal's account) allow it?NoDENY4Does a resource-based policy allow it?YesALLOW*5Does an identity-based policy allow it?NoDENY6Does the permissions boundary allow it?NoDENY7Does the session policy allow it?NoDENYALLOW* Same account only, and only when the resource policy names the IAM user or the role session directly.If it names the role ARN, the boundary and session policy still apply.KMS key policies and role trust policies must always allow (no shortcut).
Simplified from the AWS flowchart. Learn the order: Deny → RCP → SCP → resource → identity → boundary → session.
Think of it like · a loan approval at a bank

A bank checks a loan request in a fixed order, and any “no” on the way ends it.

AWS conceptFamiliar thing
Explicit Deny≈Your name is on the bank's blacklist: rejected immediately
RCP≈Head office rules about this type of loan product
SCP≈Head office rules about which customers each branch may serve
Identity / resource policy≈A loan officer who actually approves you
Permissions boundary≈The officer's own approval limit
Session policy≈The limit written on today's application form
Real AWS example · tracing one request through the steps

Role DevRole (AdministratorAccess) in a member account calls s3:DeleteBucket on audit-logs.

Step 1  Explicit Deny?      SCP "ProtectAuditLogs" denies s3:DeleteBucket on audit-logs  -> DENY (stop)
Steps 2-7 are never reached.

Error the user sees:
  ... is not authorized to perform: s3:DeleteBucket on resource:
  "arn:aws:s3:::audit-logs" with an explicit deny in a service control policy

AdministratorAccess doesn't matter: step 1 ends the evaluation. Only removing or changing the SCP Deny would help.

Exam-style scenario

A user with the AdministratorAccess managed policy cannot read objects in the bucket hr-files in the same account. The error says with an explicit deny in a resource-based policy. What should the engineer check FIRST?

  1. A. The user's permissions boundary.
  2. B. The hr-files bucket policy for a Deny statement that matches this user or request.
  3. C. The SCPs attached to the account.
  4. D. Whether the user has an MFA device.
Show the worked answer

Answer: B

The error names the policy type: a resource-based policy, which for S3 is the bucket policy. An explicit Deny there beats AdministratorAccess. Look for a Deny with a condition the user fails, such as a VPC endpoint, IP range or aws:PrincipalArn check.

Why not the others

  • A: A boundary causes an implicit deny and a different message.
  • C: The message would say “service control policy”.
  • D: Only relevant if the Deny condition checks MFA; the bucket policy is still where to look.
How the exam tests this
  • “Admin user still gets AccessDenied”→ Something has an explicit Deny (SCP, RCP, bucket policy, boundary) or a guardrail doesn't allow it. Read the error's policy type.
  • “Put these evaluation steps in order (ordering question)”→ Deny → RCP → SCP → resource-based → identity-based → boundary → session.
  • “Does the order of statements matter?”→ No. IAM is not first-match.
Must remember
  • Deny anywhere = Deny. Checked before anything else.
  • Every applicable guardrail (RCP, SCP, boundary, session) must allow.
  • At least one grant (identity or resource policy) must allow.
  • Statement order never matters.
02

Same account: one key is enough

When the caller and the resource live in the same account, AWS takes the union of the identity-based and resource-based policies. An Allow in either one is enough, as long as no Deny and no guardrail blocks it. Two resources are exceptions: a KMS key and an IAM role's trust policy must always allow the principal themselves.

Concept profile

Same-account access

often confused with cross-account access
?What is it?

A request where the principal and the resource belong to the same AWS account.

⚙How does it work?
  • Identity policy OR resource policy can grant.
  • Guardrails (SCP, RCP, boundary, session) still apply.
  • KMS key policy and role trust policy must allow: IAM policies alone are not enough unless the key policy delegates to IAM.
{ }Structure

Permissions = (identity policy ∪ resource policy) ∩ every guardrail − any explicit Deny.

⇄Same-account access vs cross-account access
Same accountCross account
Allow needed fromIdentity or resource policyIdentity and resource policy
EvaluationsOne, in that accountTwo, one in each account
GuardrailsThat account's SCPs and RCPsCaller's SCPs + resource owner's RCPs
KMS keyKey policy must allow or delegate to IAMKey policy must allow the other account, and that account's IAM must allow too
!Common mistakes the exam tests
  • Forgetting that a KMS key policy without the “Enable IAM policies” statement ignores IAM policies.
  • Thinking a resource policy that names the account (:root) is enough on its own. It only delegates; an identity policy must still allow.
  • Assuming a resource policy naming the role ARN bypasses the boundary. It doesn't (next sections).
✓Benefits and use cases
  • Let a Lambda function read a bucket using only the bucket policy.
  • Keep access rules next to the data they protect.
  • Lock sensitive keys so only named roles can use them, even if someone has AdministratorAccess.
Same account: OR Identitypolicy Resourcepolicy The whole shaded area is allowed Cross account: AND Account Aidentity policy Account Bresource policy both Only the overlap is allowed
Same account is a union. Cross account (next section) is an intersection.
Think of it like · an office and a bank vault

Inside your own company building, any one valid pass gets you into a normal room.

AWS conceptFamiliar thing
Identity policy≈Your badge says you may enter
Resource policy≈The room's guest list has your name
Same account = OR≈Either one is enough for a normal room
KMS key policy≈The vault: its own list must say yes, even for the CEO
“Enable IAM policies” statement≈The vault list saying “trust whatever the badges say”
Real AWS example · the default KMS key policy statement

This statement is what lets IAM policies control a KMS key. Remove it and only principals named in the key policy can use the key, whatever their IAM policies say.

{
  "Sid": "Enable IAM User Permissions",
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::111122223333:root" },
  "Action": "kms:*",
  "Resource": "*"
}

:root here means the account, so the key policy hands the decision to that account's IAM policies.

Exam-style scenario

A role in account 111122223333 has an identity policy that allows kms:Decrypt on a customer managed key in the same account. Calls fail with AccessDenied. The key policy lists only two key administrator roles and has no other statements. No SCP or boundary applies. What is the cause?

  1. A. Identity policies can never grant KMS permissions.
  2. B. The key policy neither allows the role nor contains the statement that enables IAM policies, so the identity policy is ignored for this key.
  3. C. The role needs kms:CreateGrant first.
  4. D. KMS requires a VPC endpoint for Decrypt.
Show the worked answer

Answer: B

KMS is the exception to the same-account union rule: the key policy must allow, either by naming the role or by delegating to IAM with the account principal statement. Fix: add the role to the key policy, or add the “Enable IAM User Permissions” statement.

Why not the others

  • A: They can, once the key policy delegates to IAM.
  • C: Grants are another way in, not a requirement.
  • D: Endpoints are optional networking, not authorization.
How the exam tests this
  • “Same account, bucket policy allows a user, user has no S3 policy”→ Allowed. One Allow is enough in the same account.
  • “Identity policy allows KMS, still denied”→ Check the key policy.
  • “Role can't be assumed although caller has sts:AssumeRole”→ Check the role's trust policy.
Must remember
  • Same account: identity OR resource policy.
  • Exceptions: KMS key policies and role trust policies must allow.
  • Principal arn:…:root in a resource policy = the account, which then needs IAM policies too.
03

Cross account: two keys, two houses

When a principal in account A uses a resource in account B, AWS runs two evaluations. Account A must allow its principal to make the request, and account B must allow the request on its resource. Both must say Allow. Account A's SCPs apply to the caller; account B's RCPs apply to the resource.

Concept profile

Cross-account access with a resource policy

often confused with cross-account role assumption
?What is it?

A principal in one account uses a resource in another account directly, because the resource's policy names that account or principal.

⚙How does it work?
  • Evaluation 1 (account A): identity policy allows, boundary/session allow, A's SCPs allow.
  • Evaluation 2 (account B): resource policy allows A, B's RCPs allow, and for encrypted data the KMS key policy allows.
  • Both Allow → request succeeds.
{ }Structure

Two ways to share: put a resource policy on the resource that names account A (caller keeps its own identity), or create a role in B that A may assume (caller switches identity).

⇄Cross-account access with a resource policy vs cross-account role assumption
Resource policyAssume a role in B
Caller's identityStays the account A principalBecomes a role session in B
Caller keeps its own permissionsYesNo, only the role's
Works forServices with resource policies (S3, KMS, SQS, SNS, Lambda, Secrets Manager…)Any service
Whose SCPs apply to the actionAccount A'sAccount B's (the role lives in B)
Good forCopying between accounts in one callDoing many things inside B
!Common mistakes the exam tests
  • Forgetting the caller-side identity policy.
  • Forgetting the KMS key policy when the bucket uses a customer managed key.
  • Thinking account B's SCPs limit account A's principal. They limit principals in B.
  • Not setting S3 Object Ownership to Bucket owner enforced, so objects uploaded by account A stay owned by A (with ACLs).
✓Benefits and use cases
  • Central logging bucket shared with all accounts.
  • Partner or vendor access to one bucket or queue.
  • Cross-account pipelines (CodePipeline, replication).
Account A (trusted · caller) Identity policy must ALLOW Boundary / session policy (if any) SCPs of account A Evaluation 1 must return Allow Account B (trusting · resource) Resource policy must ALLOW A KMS key policy (if encrypted) RCPs of account B Evaluation 2 must return Allow request
Two evaluations, two Allows. A missing Allow on either side gives AccessDenied.
Think of it like · visiting another company

Going to a meeting at a partner company needs permission from both companies.

AWS conceptFamiliar thing
Account A identity policy≈Your manager approves the trip
Account B resource policy≈The partner's reception has you on the visitor list
A's SCP≈Your company's travel rules
B's RCP≈The partner's building rules for all visitors
Assuming a role in B≈Swapping your badge for the partner's staff badge: you can only do what their badge allows
Real AWS example · the two policies cross-account access needs

Account A (111122223333) role Uploader writes into account B's bucket central-data.

// In account A: identity policy on role Uploader
{ "Effect": "Allow",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::central-data/incoming/*" }

// In account B: bucket policy on central-data
{ "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::111122223333:role/Uploader" },
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::central-data/incoming/*" }

Set the bucket's Object Ownership to Bucket owner enforced (the default for new buckets) so account B owns every uploaded object and ACLs are ignored.

Exam-style scenario

A role in account A uploads files to a bucket in account B. Uploads succeed, but analysts in account B get AccessDenied when reading the new objects. The bucket is old and has Object Ownership set to Object writer. What is the MOST effective fix?

  1. A. Ask account A to add s3:GetObject to its role.
  2. B. Change the bucket's Object Ownership to Bucket owner enforced so account B owns all objects and ACLs are disabled.
  3. C. Add an SCP in account B allowing s3:GetObject.
  4. D. Enable S3 Block Public Access.
Show the worked answer

Answer: B

With Object writer, objects uploaded by account A are owned by account A, and account B's bucket policy doesn't automatically give B access to them. Bucket owner enforced makes the bucket owner own every object and turns ACLs off, so B's bucket and identity policies work as expected.

Why not the others

  • A: The uploader's permissions don't control what B's analysts can read.
  • C: SCPs never grant access.
  • D: Public access isn't the issue.
How the exam tests this
  • “Cross-account, resource policy allows, still denied”→ The caller's identity policy is missing, or the KMS key policy doesn't allow the other account.
  • “Copy data from bucket A to bucket B in one call”→ Resource policy approach: the caller keeps permissions on both sides.
  • “Which account's SCP limits a role assumed in account B?”→ Account B's: the role session is a principal in B.
Must remember
  • Cross account: identity AND resource policy.
  • Caller's account: SCPs. Resource owner's account: RCPs.
  • Encrypted with a customer managed key? The key policy must allow the other account too.
  • Use Bucket owner enforced for shared buckets.
04

Permissions boundaries in depth

A permissions boundary turns “what the identity policy grants” into “what the identity policy grants and the boundary allows”. Day 1 showed why it exists (safe delegation). Today the detail the exam loves: what happens when a resource-based policy is involved. The answer depends on which ARN the resource policy names.

Concept profile

Permissions boundary

often confused with identity-based policy
?What is it?

A managed policy attached to a user or role as a ceiling. It grants nothing; it limits what identity-based policies can grant.

⚙How does it work?
  • Effective = identity policy ∩ boundary.
  • An explicit Deny in the boundary applies like any Deny.
  • Same-account resource policy that names the IAM user ARN or the role session ARN: not limited by the boundary's implicit deny.
  • Resource policy that names the role ARN: still limited by the boundary.
{ }Structure

Set with --permissions-boundary on create-role / create-user, or put-role-permissions-boundary. One boundary per user or role.

⇄Permissions boundary vs identity-based policy
Permissions boundaryIdentity-based policy
Grants permissionsNoYes
How manyOne per user/roleMany (up to the quota)
PurposeCeiling for delegationDay-to-day permissions
Combined with the otherIntersection (AND)Intersection (AND)
Can be on a groupNoYes
!Common mistakes the exam tests
  • Expecting the boundary to grant.
  • Mixing up which resource-policy principal bypasses the boundary (user and session yes, role ARN no).
  • Letting the delegated admin edit or remove the boundary.
  • Using a boundary where an SCP is needed (whole account).
✓Benefits and use cases
  • Self-service role creation for developers.
  • Safe automation that creates IAM roles.
  • Capping a contractor's user even if someone attaches more policies later.
Identity policy s3:* ec2:* dynamodb:* Boundary s3:* logs:* sqs:* Effective s3:* ec2, dynamodb: granted but outside boundary → denied logs, sqs: inside boundary but never granted → denied
Only the overlap survives. Everything else is an implicit deny.
Same-account resource policy grants to…Capped by boundary?Capped by session policy?
IAM user ARN …:user/aliceNon/a
IAM role ARN …:role/AnalystYesYes
Role session ARN …:assumed-role/Analyst/s1NoNo
Think of it like · a company card with a category limit

Your manager decides what you may buy, but your company card only works in some shop categories.

AWS conceptFamiliar thing
Identity policy≈Your manager's shopping list for you
Boundary≈The categories your card works in
Effective permissions≈Items that are on the list AND in an allowed category
Resource policy naming you personally≈A shop that invoices your name directly, so the card limit isn't involved
Real AWS example · creating a role with a boundary

The delegated admin can attach anything to the role, but the boundary keeps it inside S3 and CloudWatch Logs.

$ aws iam create-role --role-name app/OrdersFn \
    --assume-role-policy-document file://trust-lambda.json \
    --permissions-boundary arn:aws:iam::111122223333:policy/DevBoundary

// DevBoundary
{ "Version": "2012-10-17",
  "Statement": [{ "Effect": "Allow",
                  "Action": ["s3:*", "logs:*"],
                  "Resource": "*" }] }

If someone later attaches AdministratorAccess to this role, it still can't call DynamoDB, EC2 or IAM.

Exam-style scenario

A developer created a Lambda execution role with the DevBoundary boundary above, then attached AmazonDynamoDBFullAccess. The function gets AccessDenied on dynamodb:PutItem. The table has no resource policy. What explains this?

  1. A. DynamoDB requires a resource policy.
  2. B. The boundary doesn't allow DynamoDB, so the identity policy's Allow is capped by an implicit deny.
  3. C. AWS managed policies can't be attached to roles with boundaries.
  4. D. Lambda needs iam:PassRole at runtime.
Show the worked answer

Answer: B

Effective permissions are the intersection: DynamoDB is granted by the identity policy but missing from the boundary, so it is denied. The fix is to add DynamoDB to the boundary, if the security team approves.

Why not the others

  • A: Same-account identity policies are enough for DynamoDB.
  • C: They can be attached; they're just capped.
  • D: PassRole is needed when creating the function, not when it runs.
How the exam tests this
  • “Delegate role creation safely”→ Permissions boundary + iam:PermissionsBoundary condition.
  • “Role has admin policy but can't use service X”→ Boundary doesn't include X.
  • “Resource policy names the role ARN, boundary lacks the action”→ Denied. Role ARN grants are still capped.
Must remember
  • Boundary = ceiling, never a grant.
  • Resource policy naming user or role session ARN: not capped by boundary.
  • Naming the role ARN: capped.
  • Groups can't have boundaries.
05

Session policies

A session policy is passed at the moment you create temporary credentials. It narrows that one session: the session can do only what both the role (or user) permissions and the session policy allow. It's how one role can safely serve many users or tenants with different scopes.

Concept profile

Session policy

often confused with permissions boundary
?What is it?

An inline policy (Policy) and/or up to 10 managed policies (PolicyArns) passed when creating a temporary session. It limits that session and grants nothing.

⚙How does it work?
  • Accepted by AssumeRole, AssumeRoleWithSAML, AssumeRoleWithWebIdentity and GetFederationToken.
  • Session permissions = identity-based permissions ∩ session policy.
  • A resource policy that names the session ARN is not limited by the session policy; one that names the role ARN is.
  • With GetFederationToken you must pass a session policy.
{ }Structure
aws sts assume-role \
  --role-arn arn:aws:iam::111122223333:role/TenantAccess \
  --role-session-name tenant-42 \
  --policy file://tenant-42-scope.json
⇄Session policy vs permissions boundary
Session policyPermissions boundary
Attached toOne session, at creation timeA user or role, permanently
Set byWhoever creates the sessionAn IAM admin
LifetimeUntil the session expiresUntil removed
Grants permissionsNoNo
Typical usePer-user or per-tenant scopingDelegation guardrail
!Common mistakes the exam tests
  • Expecting a session policy to add permissions the role lacks.
  • Confusing it with the role's identity policy.
  • Forgetting the session-ARN exception for resource policies.
✓Benefits and use cases
  • Multi-tenant SaaS: one role, a session policy per tenant prefix.
  • Credential vending services that hand out narrow, short-lived access.
  • Reduce blast radius for a single script or job.
Role permissions s3:* on bucket/* dynamodb:* sqs:SendMessage Session policy s3:GetObject on bucket/tenant-42/* This session can only s3:GetObject on bucket/tenant-42/* The session policy can only remove permissions. dynamodb:* in the session policy would still not add anything the role lacks.
A session policy is a filter on top of the role, never a top-up.
Think of it like · a hotel keycard

The hotel's card printer can open every door, but reception programs your card for one room and one stay.

AWS conceptFamiliar thing
Role permissions≈The master list of doors the card system can open
Session policy≈What reception programs onto tonight's card
Session expiry≈Checkout time
Adding a door in the session policy that the role lacks≈Programming a door the hotel doesn't have: it still won't open
Real AWS example · a per-tenant session policy

A broker service assumes TenantAccess for each request and passes a policy limited to that tenant's prefix.

{ "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:::saas-data/tenant-42/*"
  }] }

Even if the broker has a bug, the credentials it hands out for tenant 42 can't touch tenant 43's data.

Exam-style scenario

A SaaS company stores each customer's files under s3://saas-data/<tenant-id>/. A single backend role can access the whole bucket. The company must make sure that a request handled for one tenant can never read another tenant's files, without creating a role per tenant. What should the engineer do?

  1. A. Create an IAM user per tenant.
  2. B. When the backend handles a request, call sts:AssumeRole on the shared role and pass a session policy that allows only saas-data/<tenant-id>/*.
  3. C. Add a permissions boundary to the backend role.
  4. D. Use one bucket per tenant with S3 Block Public Access.
Show the worked answer

Answer: B

Session policies scope one set of temporary credentials at creation time. One role serves all tenants, but every session is fenced to a single prefix.

Why not the others

  • A: Thousands of long-term users is the wrong direction.
  • C: A boundary is fixed for the role, the same for every tenant.
  • D: Possible but heavy, and Block Public Access doesn't isolate tenants.
How the exam tests this
  • “Same role, different scope per user/tenant at runtime”→ Session policy.
  • “Session policy allows X, role doesn't”→ Denied. Session policies never grant.
  • “How many managed session policies?”→ Up to 10 via PolicyArns, plus one inline.
Must remember
  • Session policy = per-session filter, set when credentials are created.
  • Session = role ∩ session policy.
  • Resource policy naming the session ARN is not capped by it.
06

SCPs and RCPs in evaluation

Organizations guardrails follow the tree. For an action to be possible in an account, an Allow must exist at every level from the root, through each OU, down to the account. A Deny at any level blocks everything below it. This gives two design styles: deny-list (keep FullAWSAccess, add Denies) and allow-list (replace FullAWSAccess with a list of allowed services).

Concept profile

Deny-list strategy

often confused with allow-list strategy
?What is it?

The default way to use SCPs: keep FullAWSAccess attached everywhere and add SCPs with Deny statements for what you want to block.

⚙How does it work?
  • FullAWSAccess provides the Allow at every level.
  • Your Deny SCPs remove specific actions.
  • New AWS services work automatically unless you deny them.
{ }Structure
{ "Effect": "Deny",
  "Action": ["organizations:LeaveOrganization",
             "cloudtrail:StopLogging", "cloudtrail:DeleteTrail"],
  "Resource": "*" }
⇄Deny-list strategy vs allow-list strategy
Deny-listAllow-list
Starting pointFullAWSAccess stays attachedFullAWSAccess detached at that level
You writeDeny statementsAllow statements listing services
New AWS servicesAllowed automaticallyBlocked until added
MaintenanceLowHigher
Good forMost organizationsHighly regulated or sandbox OUs
!Common mistakes the exam tests
  • Detaching FullAWSAccess at one level without adding an allow-list: everything below is blocked.
  • Forgetting that the account itself must also have an Allow attached.
  • Expecting an Allow SCP at a lower level to undo a Deny higher up.
  • Forgetting RCPs can only use Deny; RCPFullAWSAccess can't be detached.
✓Benefits and use cases
  • Protect security tooling org-wide.
  • Lock a sandbox OU to a few services (allow-list).
  • Region restrictions.
RootFullAWSAccess ✓ OU: SandboxAllow ec2:*, s3:* only(FullAWSAccess detached) OU: ProdFullAWSAccess ✓Deny cloudtrail:Stop* Account: sbx-01FullAWSAccess attachedMax = ec2 + s3 only Account: prod-01FullAWSAccess attachedCan never stop CloudTrail Allow: needed atEVERY levelDeny: ANY level
Sandbox accounts are limited to EC2 and S3 because that OU only allows those. Prod accounts can never stop CloudTrail because of the Deny on the Prod OU.
Think of it like · nested security gates

To reach a room deep in a building, you pass a gate on every floor.

AWS conceptFamiliar thing
Allow at every level≈Your name must be on the list at every gate on the way
Deny at any level≈One “banned” notice at any gate stops you
FullAWSAccess≈A gate list that says “everyone”
Allow-list SCP≈A gate list naming only some visitors
Real AWS example · an allow-list SCP for a sandbox OU

Detach FullAWSAccess from the Sandbox OU and attach this instead. Accounts under it can only use these services.

{ "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["ec2:*", "s3:*", "cloudwatch:*", "logs:*"],
    "Resource": "*"
  }] }

Remember that in an SCP, Allow statements can only use "Resource": "*".

Exam-style scenario

The organization root and every account have FullAWSAccess attached. The Sandbox OU has FullAWSAccess detached and an SCP that allows only ec2:* and s3:*. A user with AdministratorAccess in a Sandbox account calls dynamodb:CreateTable. What happens?

  1. A. Allowed, because the account has FullAWSAccess attached directly.
  2. B. Denied, because no SCP at the Sandbox OU level allows DynamoDB.
  3. C. Allowed, because AdministratorAccess overrides SCPs.
  4. D. Denied, because the root needs an explicit DynamoDB Allow.
Show the worked answer

Answer: B

An Allow must exist at every level. The Sandbox OU level only allows EC2 and S3, so DynamoDB has no Allow there and is denied, even though the root and the account allow everything.

Why not the others

  • A: One level's Allow isn't enough.
  • C: Identity policies can't exceed SCPs.
  • D: The root's FullAWSAccess already allows it.
How the exam tests this
  • “Restrict a group of accounts to approved services only”→ Allow-list SCP on that OU (detach FullAWSAccess there).
  • “Block one dangerous action everywhere”→ Deny-list SCP at the root.
  • “Why is a service blocked although the account has FullAWSAccess?”→ A higher OU lacks an Allow.
Must remember
  • Allow must exist at every level; Deny at any level wins.
  • SCP Allow statements: Resource must be *.
  • RCPs: Deny only, RCPFullAWSAccess can't be detached.
Official sources: SCP evaluation SCP strategies
07

Least privilege in practice

AWS recommends starting with broad permissions only while you learn what a workload needs, then reducing them to what it actually uses. AWS gives you tools for each step: CloudTrail records activity, IAM Access Analyzer policy generation turns that activity into a policy, and last accessed information plus unused access findings show what can be removed.

Concept profile

IAM Access Analyzer policy generation

often confused with IAM last accessed information
?What is it?

A feature that reads your CloudTrail logs for a user or role over a period (up to 90 days) and generates a policy containing the actions it actually used.

⚙How does it work?
  • Pick the role, the trail and the date range.
  • Access Analyzer reads the events and lists services and, for many services, the exact actions.
  • You review it, add resource ARNs, save it as a customer managed policy and attach it.
{ }Structure
  • Needs an enabled CloudTrail trail and a service role that can read it.
  • Doesn't see data events (such as S3 object-level calls) and doesn't include iam:PassRole, so add those yourself.
  • Related tools: last accessed information (when each service or action was last used) and the unused access analyzer (continuous findings for unused roles, keys, passwords and permissions).
⇄Policy generation vs IAM last accessed information
Policy generationLast accessed information
AnswersWhat policy should this role have?What hasn't been used lately?
Data sourceCloudTrail trailIAM's own access tracking
OutputA draft policy to attachA list of services/actions with last-used dates
Use whenBuilding a policy for an existing workloadRemoving permissions that are no longer needed
!Common mistakes the exam tests
  • Choosing CloudTrail Insights or Config to generate a policy.
  • Expecting the generated policy to include S3 data-event actions or PassRole.
  • Forgetting a trail must exist.
  • Treating least privilege as a one-time job.
✓Benefits and use cases
  • Replace a broad AWS managed policy on an old role.
  • Right-size CI/CD roles.
  • Continuous clean-up with unused access findings across the organization.
1. StartAWS managed or broad policy2. ObserveCloudTrail activity3. GenerateAccess Analyzer policy4. Trimlast accessed + unused accessLeast privilegeis a loop, not a one-time task
Start, observe, generate, trim, repeat.
Think of it like · tidying a wardrobe

You don't know which clothes you need until you watch what you actually wear.

AWS conceptFamiliar thing
Broad starting policy≈A wardrobe full of everything
CloudTrail≈A diary of what you wore each day
Policy generation≈A packing list made from the diary
Last accessed / unused access≈Clothes you haven't touched in months: donate them
Real AWS example · checking what a role really uses

Ask IAM which services a role has used, then read the report.

$ aws iam generate-service-last-accessed-details \
    --arn arn:aws:iam::111122223333:role/LegacyBatch
{ "JobId": "examplef-1305-c245-eba4-71fe298bcda7" }

$ aws iam get-service-last-accessed-details \
    --job-id examplef-1305-c245-eba4-71fe298bcda7
... "ServiceNamespace": "s3",       "LastAuthenticated": "2026-10-01T..."
... "ServiceNamespace": "dynamodb", (never used)

DynamoDB was never used, so it can be removed from the role's policies.

Exam-style scenario

A role used by a batch job has the PowerUserAccess managed policy. The job has run in production for three months and CloudTrail is enabled. The security team wants a least-privilege policy based on what the job actually does, with the LEAST manual effort. What should the engineer do?

  1. A. Read CloudTrail logs manually and write a policy.
  2. B. Use IAM Access Analyzer policy generation for the role over the last 90 days, review the draft, add resource ARNs, and replace PowerUserAccess.
  3. C. Use AWS Config to record IAM changes.
  4. D. Enable GuardDuty and wait for findings.
Show the worked answer

Answer: B

Policy generation turns CloudTrail activity into a draft policy automatically. Check data-event actions and iam:PassRole yourself because the generator doesn't include them.

Why not the others

  • A: Works but is the manual effort the question rules out.
  • C: Config records configuration, not the job's API usage.
  • D: GuardDuty finds threats, not least-privilege policies.
How the exam tests this
  • “Generate a policy from actual activity”→ Access Analyzer policy generation (CloudTrail, up to 90 days).
  • “Find permissions/roles/keys not used across the org, continuously”→ Unused access analyzer.
  • “Which services has this role used and when?”→ Last accessed information.
Must remember
  • Policy generation needs a CloudTrail trail; max 90 days.
  • It skips data events and iam:PassRole.
  • Least privilege is a loop: generate, review, trim, repeat.
08

Cheatsheet extras: S3 policy conflicts (IAM, bucket policy, ACL)

Your cheat sheet calls this an “exam essential topic”: what happens when an IAM policy, an S3 bucket policy and an S3 ACL disagree. You already know the general flowchart from this lecture. Here it is applied to S3 the way older exam questions phrase it, with the two twists they love: the anonymous public link and ACLs that no longer count.

Concept profile

S3 policy conflicts

often confused with a single identity policy check
?What is it?

For every S3 request, AWS combines all the policies that apply: the caller’s IAM policies, the bucket policy, and (only if ACLs are enabled) bucket and object ACLs. The request is allowed only if no policy explicitly denies and at least one allows.

⚙How does it work?
  • Start at Deny. Any explicit Deny in any of them wins.
  • Same account: one Allow from the IAM policy or the bucket policy (or an ACL) is enough.
  • Cross-account: the bucket owner must allow it and the caller’s account must allow it in an IAM policy.
  • Anonymous (unsigned) requests have no user context, so only a public bucket policy or ACL can allow them. An IAM Deny on a person doesn’t follow them to an unsigned browser request.
  • New buckets use Object Ownership = Bucket owner enforced (default since April 2023): ACLs are disabled and ignored. S3 Block Public Access overrides public grants.
{ }Structure
User contextidentity policies of the caller(+ SCP, boundary, session)skipped for anonymous requests Bucket contextbucket policy (up to 20 KB)bucket ACL (only if ACLs on)Block Public Access, RCPs Object contextobject ACL (legacy)ignored with Object Ownership= Bucket owner enforced Decision1 default Deny → 2 any explicit Deny? → Deny3 any Allow (same account)? → Allow · cross-account needs both sides
S3 looks at three contexts, then applies the usual Deny / Allow rules.
⇄S3 policy conflicts vs a single identity policy check
S3 requestMost other services
Policies that can grantIAM policy, bucket policy, ACL (if enabled), access point policyIAM policy (and a resource policy if the service has one)
Anonymous access possibleYes, through a public bucket policy (unless Block Public Access)Rarely
Object-level controlObject ACLs (legacy) or prefixes in the bucket policyResource ARN
Biggest policyBucket policy 20 KBIdentity policy 6,144 chars (managed)
Exam hint“IAM denies, bucket allows public read”“identity policy denies”
!Common mistakes the exam tests
  • Thinking an IAM user’s Deny blocks them from opening a public object URL without signing: the unsigned request is anonymous, so the Deny never applies.
  • Fixing a cross-account problem only in the bucket policy: the caller’s account must allow too.
  • Using object ACLs on a new bucket: with Bucket owner enforced they are ignored, and PutObject with an ACL other than bucket-owner-full-control fails.
  • Expecting a bucket policy Allow to beat an IAM explicit Deny for a signed request: Deny always wins.
✓Benefits and use cases
  • One Deny statement in a bucket policy protects a sensitive bucket (for example HR performance reviews) for every user, instead of editing hundreds of IAM policies.
  • Cross-account access without roles: name the other account in the bucket policy.
  • Large rules that don’t fit identity policies (bucket policy up to 20 KB).
Older material says “use S3 ACLs for object-level permissions or when the bucket policy reaches 20 KB”. Today AWS recommends keeping ACLs disabled (Bucket owner enforced) and using bucket policies, access points or prefixes instead. On the exam, an answer that relies on ACLs is usually only right when the question says ACLs are enabled or mentions a legacy setup such as an old cross-account log delivery.
Think of it like · a building with three keyholders

To open a door in the S3 building, the guard checks three lists.

AWS conceptFamiliar thing
IAM policy≈Your company badge: which floors your employer lets you visit
Bucket policy≈The floor manager’s list on the door of one floor
Object ACL≈A sticky note on a single filing cabinet (old habit, now usually ignored)
Explicit Deny≈A “banned” notice: any one of the lists can ban you, and banned beats invited
Anonymous request≈Walking in through the public entrance without showing a badge, so your company’s ban isn’t checked
Block Public Access≈The building owner locking the public entrance for every floor
Real AWS example · the conflict example from your cheat sheet, explained

The bucket policy on the logs bucket allows user brian everything. Brian’s IAM policy allows his own bucket but denies anything in buckets ending with “log”.

// Bucket policy on app-log bucket
{ "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::111122223333:user/brian"},
  "Action": "s3:*", "Resource": ["arn:aws:s3:::app-log", "arn:aws:s3:::app-log/*"] }

// IAM policy attached to brian
{ "Statement": [
  { "Sid": "OwnBucket", "Effect": "Allow", "Action": "s3:*", "Resource": "arn:aws:s3:::brian-data/*" },
  { "Sid": "NoLogs",   "Effect": "Deny",  "Action": "s3:*", "Resource": "arn:aws:s3:::*log/*" } ] }

$ aws s3 cp s3://app-log/2026/10/20.gz .     # signed as brian
fatal error: An error occurred (403) ... explicit deny in an identity-based policy

The bucket policy allows, but the explicit Deny in Brian’s identity policy wins for any signed request. (The cheat sheet’s ARN arn:aws:s3::::*log/* has a typo: S3 ARNs have three colons, arn:aws:s3:::.)

Exam-style scenario

An HR bucket has an object that someone made public with a bucket policy (Principal *, s3:GetObject). The account has Block Public Access turned off. User Dana has an IAM policy with an explicit Deny on s3:* for that bucket. Dana pastes the object’s URL into a browser without signing in and downloads the file. Then she runs aws s3 cp with her credentials and gets AccessDenied. What is the best explanation and fix?

  1. A. IAM policies are cached; wait 5 minutes and the browser download will also fail.
  2. B. The browser request was anonymous, so only the public bucket policy was evaluated. Remove the public grant and turn on S3 Block Public Access.
  3. C. The bucket policy overrides IAM policies for GetObject. Add the Deny to the bucket policy instead.
  4. D. Object ACLs override IAM. Set the object ACL to private.
Show the worked answer

Answer: B

Unsigned requests carry no user identity, so Dana’s IAM Deny is never evaluated. The signed CLI call is evaluated with her identity policy, and the Deny wins. The fix is to stop public access.

Why not the others

  • A: There is no caching effect here.
  • C: A bucket policy never overrides an explicit Deny for a signed request.
  • D: The access came from the bucket policy, not an ACL; on new buckets ACLs are ignored anyway.
How the exam tests this
  • “IAM allows, bucket policy denies”→ Denied (explicit Deny wins).
  • “IAM denies, object is public, user opens the URL anonymously”→ Allowed: the IAM Deny doesn’t apply to anonymous requests.
  • “No policy mentions the request”→ Denied by default.
  • “Deny one sensitive bucket for everyone, least effort”→ A Deny in the bucket policy.
  • “Object-level permissions on a new bucket”→ Bucket policy with prefixes (ACLs are disabled).
Must remember
  • S3 decision = union of IAM + bucket policy + ACL; Deny wins.
  • Same account: one Allow is enough. Cross-account: both sides.
  • Anonymous requests skip the user context.
  • New buckets: Bucket owner enforced, ACLs ignored.
  • Bucket policy max 20 KB.
09

Lecture summary map

Day 2 on one screen: the mind map for structure, the six gates for the evaluation order, and the keyword chart for fast answers.

1. Mind map

① FlowchartDeny → RCP → SCP→ resource → identity→ boundary → session② Same accountIdentity OR resourceKMS key policy must allowTrust policy must allow③ Cross accountIdentity AND resourceCaller SCP · owner RCPBucket owner enforced④ BoundaryIdentity ∩ boundaryUser/session ARN: not cappedRole ARN: capped⑤ Session policySet when creds createdRole ∩ session policyUp to 10 managed ARNs⑥ SCP / RCP treeAllow at EVERY levelDeny at ANY levelDeny-list vs allow-list⑦ Least privilegePolicy generation (90 d)Last accessed infoUnused access findingsIAM Day 2how AWS decides
Tap a branch to jump back to that part of the lecture. Try redrawing this map from memory on paper.

2. Six gates

Deny?wallRCPfenceSCPfenceGrantkeyBoundaryfenceSessionfence✓stop if Yesmust allowidentity OR resourcemust allow (if present)
Stop at the first gate that says no. The grant gate needs only one key in the same account, but two keys across accounts.

3. “If the question says… pick…”

Admin still gets AccessDeniedLook for an explicit Deny or a guardrail without Allow
Same account, bucket policy allows, identity policy emptyAllowed
Cross account, only one side allowsDenied: need both
Identity policy allows KMS but deniedKey policy doesn't allow / delegate
Objects uploaded by another account unreadableBucket owner enforced
Resource policy names role ARN, boundary lacks actionDenied (role ARN is capped)
Resource policy names IAM user ARN, boundary lacks actionAllowed (user ARN not capped)
Same role, different scope per tenantSession policy
Service blocked though account has FullAWSAccessAn OU above lacks an Allow
Only approved services in an OUAllow-list SCP
Least-privilege policy from real activityAccess Analyzer policy generation
Find unused roles/keys/permissions org-wideUnused access analyzer
Day 2 in one sentence

A Deny stops everything, every fence must say yes, one key opens a door at home, and a door next door needs two keys.

10

Must-remember cheat sheet

1. Order: Deny → RCP → SCP → resource → identity → boundary → session.
2. Statement order never matters. IAM isn't first-match.
3. Same account: identity OR resource policy.
4. Cross account: identity AND resource policy, two evaluations.
5. KMS key policy and trust policy must always allow.
6. Caller's SCP + resource owner's RCP apply cross-account.
7. Assumed role in B → B's SCPs apply to the session.
8. Boundary = identity ∩ boundary, never a grant.
9. Resource policy → user or session ARN: not capped by boundary.
10. Resource policy → role ARN: capped by boundary and session policy.
11. Session policy: AssumeRole*, GetFederationToken; up to 10 managed + 1 inline.
12. SCP Allow needed at every level; Deny at any level.
13. SCP Allow statements: Resource must be *.
14. RCP: Deny only; RCPFullAWSAccess can't be detached.
15. Policy generation: CloudTrail, ≤ 90 days, no data events, no PassRole.
16. Bucket owner enforced for shared buckets.
17. S3 conflicts: IAM + bucket policy + ACL; Deny wins; anonymous requests skip IAM.
18. ACLs are off by default (Bucket owner enforced); prefer bucket policies.
11

Core quiz

Check the basics first. Pick an answer, then press Check.

12

Hard quiz

These are harder than most real exam questions on purpose. Several combine two or three rules from today. If you score 9 or more out of 12, you're ready for Day 3.

13

Practice test questions (from your mock exams)

These questions come from your own mock exams (the SCS-003 mock 1–9 files in your study folder), matched to this lecture. Wording was tidied and checked against current AWS behavior; where a mock answer was outdated or wrong, the explanation says so. The source is shown at the end of each explanation, like “(Mock 5 · Q12)”. Pick an answer, then press Check.

14

Hands-on lab (30 minutes)

  1. In a sandbox account, create role lab-boundary with AdministratorAccess and a boundary allowing only s3:*. Assume it and run aws ec2 describe-instances. You should get AccessDenied.
  2. Assume the same role again, passing --policy with a session policy that allows only s3:ListAllMyBuckets. Try aws s3 ls s3://some-bucket: denied. Try aws s3 ls: allowed.
  3. Open the IAM Policy Simulator and test ec2:DescribeInstances for the role. Note how it reports the boundary as the reason.
  4. Open IAM Access Analyzer and start a policy generation for any role with recent activity.