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

IAM building blocks and the policy language

By the end of this lecture you should be able to read any IAM policy and say in one sentence what it allows or denies. Every fact here comes from the official AWS documentation linked at the end of each section.

Skill 4.2.1 authorization controlsSkill 4.2.3 least-privilege policies~75 min read + 30 min lab
01

How AWS checks every request

Every call to AWS, whether from the console, the CLI or an SDK, goes through two checks. Authentication proves who is calling. Authorization decides whether that caller may perform this action on this resource. AWS bundles the details of the call into a request context, and your policies are evaluated against it.

Concept profile

Explicit deny

often confused with implicit deny
?What is it?

Two different ways a request ends in “no”. An implicit deny is the default answer when no policy says Allow. An explicit deny is a "Effect": "Deny" statement that matches the request.

⚙How does it work?
  • AWS first looks for any matching explicit Deny in every applicable policy. If found → denied, stop.
  • Then it looks for an Allow (and checks every guardrail also allows).
  • Nothing allowed it → implicit deny.
{ }Structure

Implicit deny has no statement at all: it is the absence of an Allow. Explicit deny is a normal statement:

{ "Effect": "Deny",
  "Action": "cloudtrail:StopLogging",
  "Resource": "*" }
⇄Explicit deny vs implicit deny
Explicit denyImplicit deny
Comes fromA Deny statementNo matching Allow
Can an Allow fix it?No, neverYes, add an Allow in the right policy
Error message hint“…with an explicit deny in a service control policy”“…because no identity-based policy allows the … action”
FixRemove or change the DenyAdd the missing Allow (identity, resource, SCP, boundary…)
!Common mistakes the exam tests
  • Adding AdministratorAccess to “fix” an explicit deny. It never works.
  • Assuming a missing Allow in an SCP or boundary is an explicit deny. It's implicit, so allowing it there fixes it.
  • Forgetting the error text tells you which policy type caused the deny.
✓Benefits and use cases
  • Use explicit Deny for guardrails that must never be overridden: stop disabling CloudTrail or GuardDuty, stop leaving the organization, block unapproved Regions.
  • Rely on implicit deny for least privilege: grant only what is needed and everything else is denied automatically.
Principal user, role, root 1. Authenticate password + MFA, keys, STS token 2. Request context Principalwho Actions3:GetObject Resourcebucket/key EnvironmentIP, time, TLS, MFA used,VPC endpoint, tags… 3. Authorize evaluate all policies that apply (Day 2) Allow Deny Task 4.1 authentication Task 4.2 authorization
Conditions in your policies test values in the request context. That is why the environment box matters so much for this exam.
If no policy explicitly allows the request, the answer is deny. AWS calls this the implicit deny. An explicit deny in any policy always wins over any allow.

Two separate questions are asked, in order. First “Are you really who you say you are?” AWS checks the signature on the request (made with a password session, access keys or temporary STS credentials). If that fails, AWS never even looks at your policies. Only after that does it ask “Are you allowed to do this?”

Because the two checks are separate, their errors look different. Learning to tell them apart is a real exam skill (Skill 4.1.3 and 4.2.4).

Think of it like · checking in at an airport

Boarding a plane uses the same two-step logic as an AWS request.

AWS conceptFamiliar thing
Authentication≈Passport check: proves who you are
Authorization≈Boarding pass check: are you allowed on this flight?
Request context≈Flight number, gate, time, your seat class
Implicit deny≈Your name isn't on the passenger list, so you don't board
Explicit deny≈You're on the no-fly list. A valid ticket doesn't help
Real AWS example · reading the error message

Two errors you will meet in real life. The first is an authentication failure; the second is an authorization failure that even tells you which policy type said no.

$ aws sts get-caller-identity
An error occurred (InvalidClientTokenId) when calling the
GetCallerIdentity operation: The security token included
in the request is invalid.

$ aws s3 cp report.csv s3://finance-data/
An error occurred (AccessDenied) when calling the PutObject
operation: User: arn:aws:sts::111122223333:assumed-role/AppRole/i-0abc
is not authorized to perform: s3:PutObject on resource:
"arn:aws:s3:::finance-data/report.csv"
with an explicit deny in a service control policy

Practical habit: when something fails, run aws sts get-caller-identity first. It answers “who does AWS think I am?” If it fails, you have a credential problem. If it works but shows an unexpected role, you are using the wrong profile.

Exam-style scenario

A CI/CD job assumes the role DeployRole and runs a database migration that takes about 3 hours. After 60 minutes every API call fails with ExpiredToken. The role's identity policy allows all required actions. What should the security engineer do?

  1. A. Add sts:* to the role's identity policy.
  2. B. Increase the role's maximum session duration to 4 hours and request a longer DurationSeconds when assuming it, making sure the job does not use role chaining.
  3. C. Create an IAM user with access keys for the job.
  4. D. Remove the SCP attached to the account.
Show the worked answer

Answer: B

ExpiredToken is an authentication error: the temporary credentials ran out, permissions are fine. A role session lasts 1 hour by default and can be raised to 12 hours with the role's maximum session duration. One catch: if the job got its credentials by role chaining (using one role to assume another), the session is capped at 1 hour regardless.

Why not the others

  • A: Permissions aren't the problem, and sts:* would break least privilege.
  • C: Long-term keys are less secure; temporary credentials are the right design.
  • D: An SCP would cause AccessDenied, not ExpiredToken.
How the exam tests this
  • “InvalidClientTokenId, SignatureDoesNotMatch, ExpiredToken”→ An authentication problem: wrong, rotated or expired credentials, or clock skew. Fixing policies won't help.
  • “AccessDenied / not authorized to perform …”→ An authorization problem: look for a missing Allow or an explicit Deny somewhere (identity, resource, SCP, RCP, boundary, session).
  • “…with an explicit deny in a service control policy”→ Modern AccessDenied messages often name the policy type that denied you. Read the error carefully.
Must remember
  • Default = DENY. Nothing is allowed until some policy says Allow.
  • Explicit Deny beats every Allow, in any policy type, always.
  • Authentication error ≠ authorization error. Check credentials before you touch policies.
02

Who can make requests

A principal is a human or workload that can make a request. Inside one AWS account you will meet the following.

Concept profile

IAM role

often confused with IAM user
?What is it?

An IAM identity with permissions but no password and no long-term access keys. Anyone (or any service) the role trusts can assume it and receives temporary credentials.

⚙How does it work?
  • The role's trust policy lists who may assume it.
  • The caller (person, service or other account) calls sts:AssumeRole; AWS services do this for you.
  • STS returns an access key, secret key and session token valid 15 minutes to 12 hours (1 hour max when chaining roles).
  • Requests made with those credentials use the role's permissions, not the caller's original ones.
{ }Structure
  • Trust policy: resource-based, has Principal and sts:AssumeRole.
  • Permissions policies: identity-based, what the role can do.
  • Optional: permissions boundary, maximum session duration (1–12 h), path, tags.
⇄IAM role vs IAM user
IAM roleIAM user
CredentialsTemporary (session token, expires)Long-term (password, access keys)
Who uses itAnyone the trust policy allowsOne specific person or app
Trust policyYesNo
If credentials leakThey expire on their ownValid until someone deletes them
AWS recommends forWorkloads, cross-account, federated humansOnly when nothing else works
!Common mistakes the exam tests
  • Thinking the permissions policy is enough. Someone must also be trusted in the trust policy, and for cross-account the caller also needs sts:AssumeRole in its own policy.
  • Expecting permissions to add up. When you assume a role you get only the role's permissions.
  • Forgetting iam:PassRole when giving a role to EC2 or Lambda.
  • Expecting a 12-hour session through role chaining. Chained sessions are capped at 1 hour.
✓Benefits and use cases
  • EC2 instance profile, Lambda execution role, ECS task role.
  • Cross-account access (with sts:ExternalId for third parties).
  • Human access through IAM Identity Center or SAML/OIDC federation.
  • Break-glass emergency access with MFA required in the trust policy.
AWS account 111122223333 Root user email + password all permissions lock away, use MFA IAM group: Developers a container, NOT a principal IAM userlong-term creds IAM userlong-term creds password for the console, access keys for the CLI Avoid for humans and workloads when an alternative exists IAM role no long-term credentials assumed through AWS STS gives temporary credentials Preferred WorkloadsEC2, Lambda,on-prem servers HumansIdentity Center,SAML / OIDC IdP assume
Workloads and humans both end up in a role and receive temporary credentials. That is the pattern AWS recommends.

AWS best practice: "Require human users to use federation with an identity provider to access AWS using temporary credentials" and "Require workloads to use temporary credentials with IAM roles to access AWS."

An IAM group cannot be named in the Principal element of a resource policy, and nobody can sign in as a group. Groups only exist to attach policies to many users at once.

Identity vs principal: an identity is something you create in IAM (user, group, role). A principal is something that can actually make a request. Users and roles are both. A group is only a convenient way to attach the same policies to many users, so it is an identity but never a principal.

Long-term vs temporary credentials is the idea the exam returns to most. An IAM user's access key works until someone deletes it, so if it leaks it can be abused for months. A role gives credentials that expire on their own, from 15 minutes up to 12 hours. That is why “use a role” is almost always the most secure answer.

Think of it like · an office building

Picture your AWS account as an office building with a security desk.

AWS conceptFamiliar thing
AWS account≈The building
Root user≈The building owner with the master key
IAM user≈An employee with a permanent photo badge
IAM group≈A department. Departments don't walk through doors, people do
IAM role≈A visitor badge from reception: you hand it back and it expires at the end of the day
AWS STS≈The reception desk that hands out visitor badges
Federation / Identity Center≈Showing your company ID (from Okta, Entra ID, AD) to get a visitor badge
Real AWS example · an EC2 instance role

This is how a workload gets credentials without any access keys. The trust policy says who may wear the role (the EC2 service). The instance profile is the container that attaches the role to an instance.

// Trust policy on role "AppServerRole"
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "ec2.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }]
}

$ aws iam create-instance-profile --instance-profile-name AppServer
$ aws iam add-role-to-instance-profile \
    --instance-profile-name AppServer --role-name AppServerRole
$ aws ec2 associate-iam-instance-profile \
    --instance-id i-0abc123 --iam-instance-profile Name=AppServer

The AWS SDK on the instance finds the credentials automatically through the instance metadata service (use IMDSv2) and refreshes them before they expire. Your code contains no secrets at all.

Exam-style scenario

A security audit finds that an application on 40 EC2 instances reads from a DynamoDB table using an IAM user's access key stored in /etc/app/config.ini. The company wants to remove long-term credentials with the LEAST operational overhead. What should the engineer do?

  1. A. Move the access key to AWS Secrets Manager and turn on automatic rotation.
  2. B. Create an IAM role with DynamoDB permissions, attach it to the instances through an instance profile, update the app to use the default SDK credential chain, then delete the access key.
  3. C. Encrypt the config file with AWS KMS.
  4. D. Create a separate IAM user for each instance.
Show the worked answer

Answer: B

Roles give temporary credentials that rotate automatically and never sit on disk. The SDK's default credential chain picks them up with no code for fetching secrets. After the change, deactivate and then delete the old key, and check CloudTrail for any remaining use of it.

Why not the others

  • A: Still a long-term key, plus you now maintain rotation logic. A common distractor.
  • C: The key is still long-term and can still leak from memory or backups.
  • D: More long-term keys, more overhead: the opposite of the goal.
How the exam tests this
  • “An application on EC2 needs to read S3. MOST secure?”→ An IAM role via instance profile. Never put access keys in code, config files or user data.
  • “Workforce already uses Okta / Entra ID / Active Directory”→ IAM Identity Center connected to that IdP (Day 4).
  • “Servers in the company data centre need AWS access”→ IAM Roles Anywhere with X.509 certificates (Day 3), not IAM user keys.
  • “A developer's access key was leaked on GitHub”→ Deactivate or delete the key, check CloudTrail for what it did, then move the workload to a role.
Must remember
  • Groups can't be nested and can't be a Principal. A user can belong to up to 10 groups.
  • Role = temporary credentials through STS. Prefer roles for both humans and workloads.
  • Long-term access keys are almost always the wrong exam answer when a role would work.
03

The root user

The root user is the identity created with the account. It has full access, and you cannot attach identity-based policies or a permissions boundary to it. The only policies that can limit root are those from AWS Organizations: an SCP or RCP limits the root user of a member account.

Concept profile

Root user

often confused with an IAM admin user
?What is it?

The identity created together with the AWS account. It signs in with the account's email address and password and has complete access to everything in the account.

⚙How does it work?
  • Not controlled by IAM policies or permissions boundaries.
  • In a member account, SCPs and RCPs do limit it.
  • Some tasks can only be done by root (close account, change root email, unlock a deny-all bucket policy, some billing tasks).
  • With centralized root access, member accounts may have no root credentials; the management account runs root tasks with sts:AssumeRoot.
{ }Structure
  • Credentials it can have: password, MFA devices, access keys (should be none), signing certificates.
  • Identified in CloudTrail as userIdentity.type = "Root".
⇄Root user vs an IAM admin user
Root userIAM user with AdministratorAccess
CreatedWith the accountBy an admin
Limited by IAM Deny / boundaryNoYes
Limited by SCP (member account)YesYes
Root-only tasksYesNo
Can be deletedNo (only its credentials)Yes
!Common mistakes the exam tests
  • Using root for daily work or creating root access keys.
  • Thinking an IAM Deny policy can stop root.
  • Thinking SCPs restrict the management account's root. They don't.
  • Missing that a deny-all bucket policy is a root task.
✓Benefits and use cases
  • Protect with MFA, no keys, and an EventBridge alert on root sign-in.
  • Use only for root-only tasks and emergency recovery.
  • In Organizations, remove member root credentials centrally to shrink the attack surface.
Tasks that need root credentials (examples)Why it matters on the exam
Change account settings (email, root password); close a standalone accountProtect root with MFA and keep no root access keys
Restore IAM permissions after the only admin locked themselves outRecovery scenario
Edit or delete an S3 bucket policy or SQS queue policy that denies all principalsClassic "locked-out bucket" question
Configure MFA delete on an S3 bucketData protection link

Centralized root access management

In AWS Organizations, the management account or a delegated administrator can remove root credentials from member accounts (password, access keys, signing certificates, MFA). New member accounts can be created with no root credentials. The few privileged root tasks, such as unlocking a misconfigured bucket policy, are then done centrally with a short-lived root session (STS AssumeRoot).

Management account or delegated admin sts:AssumeRoot short-lived, task-scoped Member account A Member account B Member account C no root password no root keys no root MFA
Exam signal: "prevent use of root credentials across hundreds of member accounts with least overhead" points to centralized root access management.

Root is special because it sits outside IAM's control. You can't attach a policy to it or put a boundary on it. So the only ways to protect it are: strong MFA, no access keys, monitoring, and in an organization, SCPs and RCPs or removing the credentials completely.

Think of it like · the master key

The building owner's master key opens every door, so you keep it in a bank safe, not in your pocket.

AWS conceptFamiliar thing
Root user≈Master key
MFA on root≈The bank safe needs two keys to open
SCP on a member account≈Head office rules that even the branch owner must follow
Centralized root access≈Head office collects every branch's master key and sends a locksmith only when needed
Bucket policy that denies everyone≈A door locked from the inside. Only the master key opens it
Real AWS example · two root-user tools

First, an EventBridge rule pattern that fires when the root user signs in to the console (send it to SNS for an alert). Second, the CLI call the management account uses to unlock a member account's bucket with a short root session.

// EventBridge rule: alert on root console sign-in
{
  "detail-type": ["AWS Console Sign In via CloudTrail"],
  "detail": { "userIdentity": { "type": ["Root"] } }
}

// Centralized root access: unlock a bucket in member account
$ aws sts assume-root \
    --target-principal 444455556666 \
    --task-policy-arn arn=arn:aws:iam::aws:policy/root-task/S3UnlockBucketPolicy \
    --duration-seconds 900
# then, with those credentials:
$ aws s3api delete-bucket-policy --bucket locked-bucket

The root session is limited to the task policy you choose. AWS provides five: IAMAuditRootUserCredentials, IAMCreateRootUserPassword, IAMDeleteRootUserCredentials, S3UnlockBucketPolicy and SQSUnlockQueuePolicy.

Exam-style scenario

An engineer in member account 444455556666 applied a bucket policy with "Effect": "Deny", "Principal": "*", "Action": "s3:*" and a mistyped IP condition. Now nobody, including administrators, can read the bucket or change its policy. The organization uses centralized root access management, so member accounts have no root password. What is the FASTEST way to recover?

  1. A. Sign in with a role that has AdministratorAccess and delete the bucket policy.
  2. B. From the management account (or delegated admin), call sts:AssumeRoot for the member account with the S3UnlockBucketPolicy task policy, then delete the bucket policy.
  3. C. Open a case with AWS Support to remove the policy.
  4. D. Delete the bucket and restore from backup.
Show the worked answer

Answer: B

A bucket policy Deny with Principal: "*" applies to every IAM principal, admins included. Only the root user is exempt for deleting the policy. Because member root credentials were removed, the root task is performed centrally with AssumeRoot and a task-scoped session. If the account still had root credentials, signing in as root would also work.

Why not the others

  • A: The explicit Deny blocks admins too; AdministratorAccess cannot override a Deny.
  • C: Slower and not needed; the account owner can fix it.
  • D: Destructive, and you can't delete objects through the Deny either.
How the exam tests this
  • “A bucket policy now denies all principals, even admins”→ Sign in as root (or use a centralized privileged task in Organizations) to delete the bucket policy.
  • “Alert when anyone signs in as root”→ CloudTrail + EventBridge rule matching userIdentity.type = Root, sending to SNS.
  • “Stop root credential use in hundreds of member accounts, least overhead”→ Centralized root access management to remove member root credentials.
  • “Restrict what root can do in a member account”→ An SCP. IAM policies can't do it.
Must remember
  • IAM policies and boundaries cannot restrict root. SCPs and RCPs can, but only in member accounts.
  • Root best practice: MFA on, no access keys, use it only for root-only tasks.
  • Locked-out S3 or SQS policy is a root task.
04

Policy types: does it grant, or only limit?

The IAM User Guide lists these policy types. The single most important column is whether a policy type can grant permissions or can only set a maximum.

Concept profile

Resource-based policy

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

A JSON policy attached to a resource (S3 bucket, KMS key, SQS queue, SNS topic, Lambda function, Secrets Manager secret, and the role trust policy) that says who may use that resource.

⚙How does it work?
  • Evaluated together with the caller's identity-based policies.
  • Same account: an Allow in either the identity policy or the resource policy is usually enough.
  • Cross-account: both sides must allow: the resource owner's resource policy and the caller's identity policy.
  • Exceptions to remember: a KMS key policy must allow access (directly or by enabling IAM policies), and a role can only be assumed if its trust policy allows it.
{ }Structure
  • Always has a Principal element.
  • Inline only: one policy per resource, stored on the resource.
  • Size limits are per service (an S3 bucket policy can be up to 20 KB).
⇄Resource-based policy vs identity-based policy
Resource-basedIdentity-based
Attached toThe resourceUser, group or role
Principal elementRequiredNot allowed
AnswersWho may use me?What may I do?
Cross-accountGrants to other accounts directlyNeeds the other side to allow too
Managed / reusableNo, inline onlyYes (managed policies)
!Common mistakes the exam tests
  • Forgetting cross-account needs two Allows.
  • Forgetting the KMS key policy when objects are encrypted with a customer managed key.
  • "Principal": "*" without a Condition makes the resource public.
  • Reading arn:aws:iam::111122223333:root as the root user. It means the whole account.
✓Benefits and use cases
  • Share a bucket, queue or key with another account without making them switch roles (they keep their own permissions).
  • Enforce TLS, a VPC endpoint or organization-only access on the resource itself.
  • Let AWS services (CloudTrail, Config, SNS) write to your resources, protected with aws:SourceArn.
Concept profile

Permissions boundary

often confused with SCP
?What is it?

A managed policy you set on one IAM user or role to define the maximum permissions its identity-based policies can give it. It grants nothing by itself.

⚙How does it work?
  • Effective permissions = identity-based policy ∩ boundary.
  • An explicit Deny in the boundary always applies.
  • A resource-based policy that names the user or role directly is not capped by the boundary (Day 2 covers this case).
  • Typical pattern: allow developers iam:CreateRole only when iam:PermissionsBoundary equals your approved boundary.
{ }Structure
  • A normal AWS managed or customer managed policy, used as the PermissionsBoundary setting.
  • One boundary per user or role. Groups can't have one.
⇄Permissions boundary vs SCP
Permissions boundarySCP
Applies toOne IAM user or roleAll users and roles in accounts / OUs
Set byAn IAM admin in the accountThe organization's management account
Limits root userNoYes (member accounts)
Grants permissionsNoNo
Main purposeSafe delegationOrganization-wide guardrails
!Common mistakes the exam tests
  • Thinking a boundary grants access. The identity policy must also allow it.
  • Trying to put a boundary on a group.
  • Not stopping developers from removing or editing the boundary.
  • Choosing an SCP when only some roles in one account need limiting.
✓Benefits and use cases
  • Let developers create roles for their apps without privilege escalation.
  • Let a CI/CD pipeline create roles safely.
  • Delegate IAM administration to a team while capping what it can hand out.
Policy typeAttached toEffectWhat it is for
Identity-based (AWS managed, customer managed, inline)user, group, rolegrantsWhat this identity can do
Resource-based (S3 bucket policy, KMS key policy, role trust policy…)resourcegrantsWho can use this resource, including other accounts
ACLsresourcegrantsLegacy cross-account only; the one type that is not JSON
AWS RAM resource sharesresource sharegrantsShare resources with accounts, OUs, or the organization
Permissions boundaryuser or rolelimitsMaximum that identity-based policies can grant
SCP (Organizations)root, OU, accountlimitsMaximum for principals in member accounts
RCP (Organizations)root, OU, accountlimitsMaximum for resources in member accounts
VPC endpoint policyVPC endpointlimitsExtra boundary for traffic through the endpoint
Session policypassed when assuming a role or federatinglimitsNarrows one session
Granted identity policy resource policy Guardrail max SCP, RCP, boundary, session Effective permissions granted but over the limit: denied within the limit but never granted: denied Explicit Deny in ANY policy removes the action from the overlap, no matter how many Allows exist −
Simplified picture for the same-account case. Day 2 walks through the exact evaluation order, including how a permissions boundary does not cap what a resource-based policy grants.
An SCP that says "Effect": "Allow", "Action": "ec2:*" gives nobody EC2 access. The identity still needs an identity-based or resource-based policy that grants it. AWS says it plainly: "SCPs do not grant permissions."

Managed vs inline

AWS managed policies are written and updated by AWS and are broad. Customer managed policies are reusable, and IAM keeps up to five versions so you can roll back. Inline policies live inside one identity and are deleted with it. AWS recommends customer managed policies for least privilege, and inline only when a policy must stay strictly one-to-one with an identity.

Think of every policy as answering one of two questions. Grant policies answer “what is this person allowed to do?” Limit policies answer “what is the most this person could ever be allowed?” A limit policy on its own gives nothing, and a grant policy is cut back by every limit that applies.

This explains a pattern that shows up in many exam scenarios. If an action is allowed by the identity policy but missing from the boundary or SCP, it is denied. If it is allowed by the SCP but missing from the identity policy, it is also denied. You need the Allow in both places.

Think of it like · badges, guest lists and fences

Grant policies are like permissions you are handed. Limit policies are like fences nobody can climb over.

AWS conceptFamiliar thing
Identity-based policy≈What is printed on your badge
Resource-based policy≈The guest list taped to a meeting room door
Permissions boundary≈Your employment contract: your manager can give you tasks, but never beyond what the contract allows
SCP≈Company-wide rules for every employee in every branch
RCP≈Building code for every room: some doors never open to strangers
Session policy≈Today's day pass, narrower than your usual badge
VPC endpoint policy≈A guard at the private tunnel entrance who checks what you carry through
Real AWS example · delegating role creation with a permissions boundary

Developers may create roles, but only under the /app/ path and only if the role carries the DevBoundary boundary. A second statement stops them from removing or editing the boundary.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CreateRolesOnlyWithBoundary",
      "Effect": "Allow",
      "Action": ["iam:CreateRole", "iam:AttachRolePolicy", "iam:PutRolePolicy"],
      "Resource": "arn:aws:iam::111122223333:role/app/*",
      "Condition": {
        "StringEquals": {
          "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/DevBoundary"
        }
      }
    },
    {
      "Sid": "ProtectTheBoundary",
      "Effect": "Deny",
      "Action": ["iam:DeleteRolePermissionsBoundary",
                 "iam:CreatePolicyVersion", "iam:DeletePolicy"],
      "Resource": "*",
      "Condition": { "ArnLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/Developer*" } }
    }
  ]
}

Even if a developer attaches AdministratorAccess to a new role, that role can only do what DevBoundary allows. The developer can't hand out more power than the security team approved.

Exam-style scenario

Developers want to create their own AWS Lambda execution roles. The security team requires that no role created by developers can ever access anything beyond S3 and DynamoDB, and that developers cannot remove that limit. The security team does not want to review each role manually. What should the engineer do?

  1. A. Attach an SCP to the account that denies everything except S3 and DynamoDB.
  2. B. Create a DevBoundary managed policy (S3 + DynamoDB), allow developers iam:CreateRole only with the condition iam:PermissionsBoundary = DevBoundary, and deny changes to the boundary.
  3. C. Have the security team create every role on request.
  4. D. Use IAM Access Analyzer to detect over-privileged roles weekly.
Show the worked answer

Answer: B

This is the textbook use of a permissions boundary: safe delegation. The boundary caps what the new roles can do, and the condition key forces every new role to carry it.

Why not the others

  • A: An SCP would restrict everyone in the account, including the security team and other workloads.
  • C: Works, but it's the manual overhead the team wants to avoid.
  • D: Detective, after the fact. The requirement is preventive.
How the exam tests this
  • “Let developers create roles for their apps without being able to escalate their own privileges”→ Permissions boundary, plus a condition that every role they create must have that boundary (iam:PermissionsBoundary).
  • “SCP allows the action but the user still gets AccessDenied”→ The identity has no Allow in its own policy. SCPs never grant.
  • “Give a third party read access to one bucket”→ A resource-based policy (bucket policy) or a cross-account role, not an SCP.
Must remember
  • Only identity policies, resource policies, ACLs and RAM shares grant.
  • SCP, RCP, boundary, session policy and VPC endpoint policy only limit.
  • Effective permission = granted ∩ every limit − any explicit Deny.
  • A boundary caps identity policies, not what a resource policy grants (Day 2).
05

SCPs protect your principals, RCPs protect your resources

Both are AWS Organizations guardrails, but they face in opposite directions. An SCP limits what your identities can do, wherever the resource is. An RCP limits what anyone, including outsiders, can do to your resources.

Concept profile

Service control policy (SCP)

often confused with IAM policy
?What is it?

An AWS Organizations policy that sets the maximum permissions for IAM users and roles, including the root user, in member accounts. It is a guardrail and grants nothing.

⚙How does it work?
  • Attach to the organization root, an OU or an account. Accounts inherit from every level above them.
  • For an action to be possible, an SCP Allow must exist at every level from the root down to the account.
  • A Deny at any level blocks the action for everything below it.
  • The default FullAWSAccess SCP allows everything, so you usually add Deny statements (deny-list strategy).
  • The management account and service-linked roles are not affected.
{ }Structure
  • Same JSON language as IAM: Version, Statement, Effect, Action/NotAction, Resource/NotResource, Condition.
  • No Principal or NotPrincipal. To target a role, use a Condition on aws:PrincipalArn.
  • Allow statements may only use "Resource": "*".
  • Other Organizations policy types: authorization policies (SCP, RCP) and management policies (tag, backup, AI services opt-out, declarative).
⇄Service control policy vs IAM policy
SCPIAM policy
LevelOrganization (root, OU, account)One account
Attached toRoot, OU, accountUser, group, role
Grants permissionsNo, sets the maximumYes
Affects root userYes (member accounts)No
Affects management accountNoYes
Typical useGuardrails for many accountsDay-to-day access
!Common mistakes the exam tests
  • Believing an SCP Allow gives access.
  • Detaching FullAWSAccess without an allow-list replacement, which blocks everything.
  • Forgetting that an Allow must exist at every level (allow-list strategy).
  • Running workloads in the management account, where SCPs don't apply.
  • Using an SCP to stop outsiders. SCPs only limit your own principals; that's the RCP's job.
✓Benefits and use cases
  • Restrict Regions (aws:RequestedRegion).
  • Stop accounts leaving the organization (organizations:LeaveOrganization).
  • Protect security services: deny stopping CloudTrail, GuardDuty, Config, Security Hub.
  • Enforce rules such as IMDSv2 or encryption at creation.
  • One set of rules for hundreds of accounts, managed centrally.
Concept profile

Resource control policy (RCP)

often confused with a bucket policy
?What is it?

An AWS Organizations policy that sets the maximum permissions on resources in member accounts, no matter who is calling, including identities outside your organization.

⚙How does it work?
  • Evaluated whenever anyone accesses a supported resource (S3, KMS, STS, SQS, Secrets Manager and 70+ more) in a member account.
  • RCPFullAWSAccess is attached everywhere automatically and cannot be detached; you add your own Deny RCPs on top.
  • Doesn't apply to the management account, service-linked roles or AWS managed KMS keys.
{ }Structure
  • Custom RCPs can only use "Effect": "Deny".
  • "Principal" must be "*"; narrow it with Conditions.
  • NotPrincipal and NotAction are not supported.
⇄Resource control policy vs a bucket policy
RCPBucket policy
ScopeEvery supported resource in many accountsOne bucket
Grants permissionsNo, Deny onlyYes
Who writes itCentral security teamBucket owner (any developer with access)
Can a developer override it?NoYes, by editing the policy
Services70+ supported servicesS3 only
!Common mistakes the exam tests
  • Forgetting to exempt AWS service principals (aws:PrincipalIsAWSService), which breaks CloudTrail log delivery and similar.
  • Assuming RCPs cover every service.
  • Trying to write an Allow RCP.
  • Choosing an RCP to stop your own people sending data out. That's the SCP side.
✓Benefits and use cases
  • Organization-only identity perimeter (aws:PrincipalOrgID).
  • Enforce TLS everywhere (aws:SecureTransport).
  • Org-wide confused-deputy protection (aws:SourceOrgID).
  • A safety net under every bucket policy a developer might get wrong.
Your organization (member accounts) Your role principal SCP applies here Your S3 bucket resource RCP applies here SCP + RCP Outside account not in your org only the RCP can stop this Outside bucket not in your org only the SCP can stop this (data exfiltration)
Together they form a "data perimeter": trusted identities only, accessing trusted resources only.
SCPRCP
LimitsUsers and roles in member accounts, including member-account rootResources in member accounts, whoever calls them
Grants permissions?NoNo
Management accountNot affectedNot affected
Service-linked rolesNot affectedNot affected
Services coveredAll servicesA supported list that keeps growing (S3, STS, KMS, SQS, Secrets Manager, DynamoDB, ECR, CloudWatch Logs, Cognito and many more). AWS managed KMS keys are not covered.
Typical condition keyaws:ResourceOrgID, aws:RequestedRegionaws:PrincipalOrgID, aws:SecureTransport

The easiest way to tell them apart is to ask whose rule is being written. An SCP is written about your people: wherever they go, they carry the SCP with them. An RCP is written about your things: whoever comes to them, the RCP is waiting at the door.

So if the risk is “my staff copying data out to their personal account”, the SCP side is the answer. If the risk is “an outsider getting into my bucket because someone wrote a bad bucket policy”, only the RCP can stop it, because your SCPs never apply to someone else's identities.

Think of it like · rules for staff vs rules for rooms

Your company writes two kinds of security rules.

AWS conceptFamiliar thing
SCP≈Staff rule: “Our employees may not carry files out of the company.” It follows the employee
RCP≈Room rule: “No outsider may enter our archive room, even if someone signed them in.” It stays on the door
aws:ResourceOrgID (SCP side)≈“Only work in rooms that belong to our company”
aws:PrincipalOrgID (RCP side)≈“Only people with our company badge may enter”
Data perimeter≈Both rules together: only trusted people, in trusted rooms, from trusted networks
Real AWS example · the AWS sample RCP for an organization-only perimeter

Unlike SCPs, an RCP statement always uses "Principal": "*", because it applies to whoever calls the resource. The second condition keeps AWS services (such as CloudTrail delivering logs) working.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "EnforceOrgIdentities",
    "Effect": "Deny",
    "Principal": "*",
    "Action": ["s3:*", "sqs:*", "kms:*", "secretsmanager:*", "sts:AssumeRole"],
    "Resource": "*",
    "Condition": {
      "StringNotEqualsIfExists": { "aws:PrincipalOrgID": "o-abc123xyz" },
      "BoolIfExists": { "aws:PrincipalIsAWSService": "false" }
    }
  }]
}

Read it as: “Deny anyone who is not from my organization and is not an AWS service from using these resources.” Both conditions must be true for the Deny to apply (AND).

Exam-style scenario

A company has 80 accounts in AWS Organizations. Auditors require that (1) employees cannot copy data to S3 buckets owned outside the organization, and (2) no identity outside the organization can read the company's buckets, even if a developer writes a bucket policy that grants an outside account. Which combination meets both requirements? (Choose TWO.)

  1. A. An SCP that denies S3 actions when aws:ResourceOrgID is not the organization ID.
  2. B. An RCP that denies S3 actions when aws:PrincipalOrgID is not the organization ID, with an exception for AWS service principals.
  3. C. Turn on S3 Block Public Access in every account.
  4. D. An IAM Access Analyzer analyzer for external access.
  5. E. A permissions boundary on every developer role.
Show the worked answer

Answer: A and B

Requirement 1 is about your principals going out, so it is the SCP side, checking the resource's org. Requirement 2 is about outsiders coming in, so it is the RCP side, checking the principal's org.

Why not the others

  • C: Block Public Access stops public access, but a bucket policy can still grant one specific outside account.
  • D: Finds the problem, doesn't prevent it.
  • E: Only limits your developers, not outsiders, and must be attached to every role.
How the exam tests this
  • “Prevent employees uploading data to S3 buckets outside the organization”→ SCP (or VPC endpoint policy) that denies when aws:ResourceOrgID isn't your org.
  • “Guarantee no identity outside the org can access our S3 and KMS, even if a policy is misconfigured”→ RCP that denies when aws:PrincipalOrgID isn't your org, with an exception for AWS service principals.
  • “Enforce TLS on every bucket in every account”→ RCP that denies when aws:SecureTransport is false (fewer moving parts than editing every bucket policy).
Must remember
  • SCP = your principals, RCP = your resources.
  • Neither affects the management account or service-linked roles. Both need Organizations with all features enabled.
  • RCPs don't apply to AWS managed KMS keys.
  • The default SCP is FullAWSAccess. Remove it only if you mean to use an allow-list strategy.
06

Anatomy of a policy

Every JSON policy has the same parts. Read it as a sentence: Effect the Action on the Resource, for the Principal, when the Condition is true.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "ReadReportsFromOfficeWithMFA",
    "Effect": "Allow",
    "Action": [
      "s3:GetObject",
      "s3:ListBucket"
    ],
    "Resource": [
      "arn:aws:s3:::reports",
      "arn:aws:s3:::reports/*"
    ],
    "Condition": {
      "IpAddress": {
        "aws:SourceIp": "203.0.113.0/24"
      },
      "Bool": {
        "aws:MultiFactorAuthPresent": "true"
      }
    }
  }]
}
Version is always 2012-10-17. The older 2008-10-17 does not support policy variables.
Effect is Allow or Deny.
Action and Resource say what and where. ListBucket works on the bucket ARN, GetObject on object ARNs (/*). You need both.
Condition tests the request context. Here both blocks must be true.
No Principal: this is an identity policy. The principal is whoever it is attached to.
ElementRemember
PrincipalOnly in resource-based policies, including role trust policies. "AWS": "arn:aws:iam::111122223333:root" means the whole account, and that account's admins must still grant access with their own identity policies.
NotPrincipalAWS recommends against using it with Deny. Use a Deny with a condition on aws:PrincipalArn instead.
NotActionWith Allow it means "everything except", which is very broad. With Deny it means "deny everything except", common in SCPs.
NotResourceMatches every resource except those listed. Use with care.

Read every statement as an English sentence with five parts. Effect is the verb of permission (allow or deny). Principal is the subject (who). Action is the verb (what they do). Resource is the object (on what). Condition is the “only if” clause. Once you read policies this way, long policies stop being scary.

A policy can have many statements. AWS evaluates all of them; the order of statements in the file does not matter.

Think of it like · a sentence in grammar

A policy statement is just a sentence with a fixed structure.

AWS conceptFamiliar thing
Effect≈Allow / Deny: “may” or “may not”
Principal≈Subject: who
Action≈Verb: does what
Resource≈Object: to what
Condition≈Clause: only if …
NotAction + Allow≈“You may do anything except …”: a very wide sentence
Real AWS example · a cross-account bucket policy, read as a sentence

Account 111122223333 owns shared-reports and wants a partner account to read it.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PartnerRead",
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::444455556666:root" },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::shared-reports/*"
  }]
}

Sentence: “Allow account 444455556666 to read objects in shared-reports.” Note the word account: :root here means the whole account, not its root user. The partner's own admins still decide which of their users or roles may use this access.

Exam-style scenario

Using the bucket policy above, an analyst in account 444455556666 assumes the role AnalystRole and runs aws s3 cp s3://shared-reports/q3.csv .. The result is AccessDenied. The objects use default SSE-S3 encryption. What is the MOST likely cause?

  1. A. AnalystRole has no identity-based policy in account 444455556666 allowing s3:GetObject on shared-reports/*.
  2. B. The bucket policy must use "Principal": "*".
  3. C. The bucket needs an ACL granting the partner account.
  4. D. The organization's SCP must explicitly allow s3:GetObject.
Show the worked answer

Answer: A

Cross-account access needs two Allows: the resource owner's bucket policy and the caller's own identity policy. The bucket policy trusted the account, so the partner account must still grant AnalystRole. If the objects were encrypted with a customer managed KMS key, the KMS key policy would also need to allow the partner; that is the second most common cause in exam questions.

Why not the others

  • B: That would make it far too open, and the partner would still need an identity Allow.
  • C: ACLs are legacy and not needed when the bucket policy grants access.
  • D: The default FullAWSAccess SCP already allows it; SCPs never need to “grant”.
How the exam tests this
  • “Which statement allows access only to the user's own folder?”→ Look for ${aws:username} in the Resource, with Version 2012-10-17.
  • “Policy allows s3:GetObject on bucket/* but listing fails”→ Missing s3:ListBucket on the bucket ARN (no /*).
  • “Principal "*" in a bucket policy”→ Public to the world unless a Condition narrows it (for example aws:PrincipalOrgID). Access Analyzer would flag it.
Must remember
  • Principal appears only in resource-based policies (and trust policies).
  • Statement order doesn't matter. Deny wins regardless of position.
  • Prefer Deny + aws:PrincipalArn condition over NotPrincipal.
07

Reading an ARN

An Amazon Resource Name identifies one resource. Its fields are separated by colons, and some services leave fields empty.

Concept profile

IAM path

often confused with resource tags
?What is it?

An optional prefix in the name of an IAM user, role or policy, such as /app/ or /security/, used to group identities. It becomes part of the ARN.

⚙How does it work?
  • role/app/OrdersFn has path /app/; the default path is /.
  • Policies match it with wildcards in Resource, or with aws:PrincipalArn in Conditions.
  • A role's path is set when it is created (CLI, API, CloudFormation) and can't be changed afterwards.
{ }Structure
arn:aws:iam::111122223333:role/app/OrdersFn
                                 └─path─┘
⇄IAM path vs resource tags
IAM pathTags
FormHierarchy in the ARNKey = value pairs
Change laterRole path: noYes, if allowed
Used inResource ARNs, aws:PrincipalArnConditions (aws:PrincipalTag, aws:ResourceTag)
RiskLow, fixed at creationSomeone who can edit tags can change access, so protect tagging
Best forFixed groupings (RBAC-style)Flexible, scalable rules (ABAC, Day 5)
!Common mistakes the exam tests
  • Looking for the path in the role name. It's only visible in the ARN.
  • Expecting to change a role's path later.
  • Allowing iam:PassRole on * instead of a path.
✓Benefits and use cases
  • Scope iam:PassRole to role/app/*.
  • Let a team manage only roles under /team-a/.
  • Exempt /security/ roles from an SCP Deny with aws:PrincipalArn.
arn aws iam (empty) 111122223333 role/app/DataReader partition service Region account ID resource (path: /app/) arn aws s3 (empty) (empty) reports/2026/* IAM is global, so it has no Region. S3 bucket names are globally unique, so S3 ARNs have no Region or account.
The /app/ in the role name is an IAM path (skill 4.2.1). A policy can target arn:aws:iam::111122223333:role/app/* to cover every role under that path. Day 5 covers this.
Think of it like · a postal address

An ARN is an address that goes from the widest area to the exact door.

AWS conceptFamiliar thing
Partition (aws, aws-cn, aws-us-gov)≈Country
Service (iam, s3, kms)≈City
Region (eu-west-1)≈District, left blank for global services
Account ID≈House number
Resource (role/app/DataReader)≈The room; the path /app/ is like the floor
Real AWS example · using an IAM path to control iam:PassRole

To launch EC2 with a role or create a Lambda with a role, the caller needs iam:PassRole on that role. Paths let you say “only application roles”.

{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::111122223333:role/app/*",
  "Condition": {
    "StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" }
  }
}

Roles created as role/app/OrdersFn match; role/admin/BreakGlass does not. Paths are set when the role is created (with the CLI or API) and can't be changed later.

Exam-style scenario

Developers can create Lambda functions. A developer recently created a function that uses the company's OrgAdmin role, which gave the function full administrator access. The company wants developers to attach only approved application roles to Lambda functions. What should the engineer do?

  1. A. Create application roles under the path /app/ and allow developers iam:PassRole only on arn:aws:iam::111122223333:role/app/* with iam:PassedToService = lambda.amazonaws.com.
  2. B. Remove lambda:CreateFunction from developers.
  3. C. Add a Deny on sts:AssumeRole to the OrgAdmin trust policy.
  4. D. Tag the OrgAdmin role with Sensitive=true.
Show the worked answer

Answer: A

Attaching a role to a service is controlled by iam:PassRole. Allowing PassRole on * is a well-known privilege-escalation path. Scoping it to a path limits developers to approved roles.

Why not the others

  • B: Stops the work entirely instead of making it safe.
  • C: Lambda assumes the role, not the developer, and it would break the admin role for everyone.
  • D: A tag by itself does nothing unless a policy checks it.
Must remember
  • IAM ARNs have no Region. S3 bucket ARNs have no Region and no account.
  • * in a Resource can match across path levels, so bucket/* means every object.
08

Conditions and condition keys

A condition is "Operator": { "key": "value" }. How several conditions combine is a favourite exam detail.

Concept profile

aws:PrincipalOrgID

often confused with aws:ResourceOrgID
?What is it?

A global condition key that holds the organization ID of the account the caller belongs to. It lets you trust a whole organization in one line.

⚙How does it work?
  • Present in the request when the calling principal belongs to an account in an organization.
  • Used in resource-based policies and RCPs, with "Principal": "*" plus the condition.
  • AWS service principals don't carry it, so add aws:PrincipalIsAWSService where services must get through.
{ }Structure
"Condition": {
  "StringEquals": { "aws:PrincipalOrgID": "o-abc123xyz" }
}
⇄aws:PrincipalOrgID vs aws:ResourceOrgID
aws:PrincipalOrgIDaws:ResourceOrgID
ChecksThe caller's organizationThe resource's organization
Written inResource policies, RCPsIdentity policies, SCPs, VPC endpoint policies
Protects againstOutsiders coming inYour people taking data out
Related keyaws:SourceOrgID: the org of the resource an AWS service is acting for (confused deputy)
!Common mistakes the exam tests
  • Listing hundreds of account IDs instead of using this key.
  • Putting it in an SCP. Your own principals are always in your org, so it checks nothing useful there.
  • Swapping it with aws:ResourceOrgID in a data-perimeter question.
  • Forgetting service principals and breaking log delivery.
✓Benefits and use cases
  • Share a logging bucket or KMS key with every account in the organization.
  • Automatically include new accounts with no policy changes.
  • Build the identity part of a data perimeter.
Different keys or operators aws:SourceIp MFA present AND Every condition must be true for the statement to apply. Several values for one key eu-west-1 eu-central-1 OR Any one value matching makes that condition true.

Operators to recognise

StringEquals, StringLike (with * and ?), StringNotEquals, ArnLike, IpAddress / NotIpAddress, Bool, NumericLessThan, DateGreaterThan, and Null (is the key present?). Add IfExists to apply the test only when the key exists. For keys with several values, such as aws:TagKeys, use ForAllValues: or ForAnyValue:.

Global condition keys the exam uses most

KeyTypical use
aws:PrincipalOrgIDResource policy: allow any principal from my organization without listing accounts
aws:ResourceOrgIDIdentity policy or SCP: my principals may only use resources in my organization
aws:SourceIpLimit to corporate public IP ranges. Does not match requests that come through a VPC endpoint.
aws:SourceVpce, aws:SourceVpcOnly allow access through my VPC endpoint or VPC
aws:SecureTransportDeny when false to enforce TLS
aws:MultiFactorAuthPresentRequire MFA. Not present at all for calls signed with long-term access keys.
aws:RequestedRegionSCP that blocks unapproved Regions
aws:PrincipalTag, aws:ResourceTag, aws:RequestTag, aws:TagKeysAttribute-based access control (Day 5)
aws:SourceArn, aws:SourceAccountConfused deputy protection when an AWS service acts for you (Day 3)
aws:PrincipalArnMatch the calling role, for example to exempt a break-glass role from a deny
aws:PrincipalIsAWSServiceExempt AWS service principals (such as CloudTrail writing logs) from a perimeter deny

Two traps, illustrated

Trap 1: private traffic EC210.0.1.25 VPC endpointvpce-1a2b S3 ✗ aws:SourceIp = public CIDR → no match ✓ aws:SourceVpce = vpce-1a2b → match Trap 2: MFA check Deny if aws:MultiFactorAuthPresent = false Console, no MFAkey = false Access keyskey missing Bool: denied ✓ slips through ✗ BoolIfExists: denied ✓ denied ✓ Missing key + IfExists = condition treated as true

Policy variables

One policy can give each user their own folder: "Resource": "arn:aws:s3:::home/${aws:username}/*". You can also use tags, such as ${aws:PrincipalTag/team}. Variables only work with "Version": "2012-10-17".

Conditions are where most exam questions hide their tricks, because one word (IfExists, ForAllValues, Not) can flip the meaning.

IfExists means: “If the key is in the request, test it. If the key is missing, treat the condition as true.” In an Allow that makes the rule looser. In a Deny that makes the rule stricter, which is exactly what you want for MFA.

ForAllValues means every value in the request must be in your list; it is also true when the request has no values at all, so pair it with a Null check if the key is required. ForAnyValue means at least one value must match.

Think of it like · a security checkpoint

Conditions behave like the rules at a checkpoint.

AWS conceptFamiliar thing
Several keys = AND≈You must show an ID and pass the metal detector
Several values = OR≈Passport or driving licence or national ID card is fine
…IfExists≈“If you carry a bag, it must be scanned.” No bag, you pass this rule
Null condition≈“Do you have a bag at all?”
aws:SourceIp vs aws:SourceVpce≈Checking your street address vs checking which private tunnel you came through
Real AWS example · RequestTag vs ResourceTag vs PrincipalTag

Three tag keys look alike but check different things. This policy lets developers start and stop only instances of their own project, and launch new instances only if they tag them correctly.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ManageOnlyMyProjectInstances",
      "Effect": "Allow",
      "Action": ["ec2:StartInstances", "ec2:StopInstances"],
      "Resource": "arn:aws:ec2:*:111122223333:instance/*",
      "Condition": { "StringEquals": {
        "aws:ResourceTag/Project": "${aws:PrincipalTag/Project}" } }
    },
    {
      "Sid": "LaunchOnlyWithAllowedProjectTag",
      "Effect": "Allow",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:111122223333:instance/*",
      "Condition": { "StringEquals": {
        "aws:RequestTag/Project": ["alpha", "beta"] } }
    }
  ]
}

aws:ResourceTag = the tag already on the thing you touch. aws:RequestTag = the tag you are trying to put on it in this request. aws:PrincipalTag = the tag on you (your role or session). (A real RunInstances policy also needs statements for the AMI, subnet, network interface and so on; they're left out here to keep the focus on tags.)

Exam-style scenario

A company requires that every new EC2 instance be launched with a Project tag whose value is either alpha or beta. Launches without the tag, or with another value, must fail. Which condition should be added to the ec2:RunInstances Allow for the instance resource?

  1. A. "StringEquals": {"aws:RequestTag/Project": ["alpha", "beta"]}
  2. B. "StringEquals": {"aws:ResourceTag/Project": ["alpha", "beta"]}
  3. C. "ForAnyValue:StringEquals": {"aws:TagKeys": ["Project"]}
  4. D. "StringEquals": {"aws:PrincipalTag/Project": ["alpha", "beta"]}
Show the worked answer

Answer: A

At launch the instance doesn't exist yet, so it has no tags to read. The tag is part of the request, so you check aws:RequestTag. If the tag is missing, the key isn't in the request, StringEquals fails, and the Allow doesn't apply, so the launch is denied. The two values in the list are OR.

Why not the others

  • B: ResourceTag reads tags that already exist on a resource; the new instance has none yet.
  • C: Checks the tag name is present but not its value.
  • D: Checks the caller's tag, not the instance's tag.
How the exam tests this
  • “Require MFA, but scripts with access keys still get through”→ Use BoolIfExists in the Deny.
  • “IP restriction breaks after moving to a VPC endpoint”→ Use aws:SourceVpce / aws:SourceVpc.
  • “Trust 300 accounts of the organization”→ aws:PrincipalOrgID, never a list of account IDs.
  • “Users must tag every new resource with CostCenter”→ Deny create when Null aws:RequestTag/CostCenter = true.
Must remember
  • Different keys/operators = AND. Values of one key = OR.
  • aws:SourceIp only sees public IPs; it never matches traffic through a VPC endpoint.
  • aws:MultiFactorAuthPresent is missing (not false) for long-term access keys.
  • Policy variables need Version 2012-10-17.
09

Cheatsheet extras: password policy, key rotation and policy limits

Your cheat sheet (older SCS-C02 material) spends time on IAM user hygiene: the account password policy, how to rotate access keys without breaking an application, what to do when a previous admin used the root user, and the size limits of each policy type. These are small facts, but they turn up as “which TWO steps” and “least disruption” questions. Today the best answer is still “no IAM users at all, use roles and IAM Identity Center”, but when the question has IAM users, these are the rules.

Concept profile

IAM account password policy

often confused with the identity provider’s password policy
?What is it?

One account-wide setting that sets the rules for IAM users’ console passwords: minimum length, required character types, expiry, reuse and whether users may change their own password.

⚙How does it work?
  • Set it in the IAM console (Account settings) or with aws iam update-account-password-policy. One policy per account.
  • Options: minimum length 6–128; require upper case, lower case, numbers, symbols; maximum age 1–1095 days; prevent reuse of the last 1–24 passwords; allow users to change their own password; require administrator reset after expiry (hard expiry).
  • With no custom policy, AWS uses a default: at least 8 characters, three of the four character types, and not the same as the account name or email.
  • It applies only to IAM users. It doesn’t touch access keys, the root user, federated users, IAM Identity Center users or Cognito users.
  • A new policy is checked when a password is next set or changed; an expiry forces a change at the next sign-in.
{ }Structure
IAM users Federated users (AD, Okta…) Cognito user pool users Root user IAM account password policylength 6–128, char types, max age 1–1095 d, reuse 1–24 The identity provider's own policye.g. Active Directory domain password policy The user pool's password policyset in the Cognito user pool (not the identity pool) Not covered by the IAM policyuse a strong unique password + MFA, no access keys
Each kind of user gets its password rules from a different place.
⇄IAM account password policy vs the identity provider’s password policy
IAM password policyIdP / Cognito password policy
Who it coversIAM users onlyUsers stored in that IdP or user pool
Where you set itIAM account settingsAD domain policy, Okta, Identity Center store, Cognito user pool
ScopeOne accountEvery app that uses the IdP
Org-wide?No organization password policy exists; set it per account (StackSets/Control Tower can automate)Already central in the IdP
Exam hint“IAM users must use 14-character passwords”“federated users / Cognito users need complex passwords”
!Common mistakes the exam tests
  • Setting the IAM password policy for federated users: they never have an IAM password.
  • Looking for a password policy in a Cognito identity pool: only user pools store passwords.
  • Expecting an “AWS Organizations password policy”: there is none.
  • Thinking the policy rotates access keys: rotation of keys is a separate process (below).
✓Benefits and use cases
  • CIS AWS Foundations checks (for example length ≥ 14 and reuse prevention 24).
  • A quick, account-wide control when a legacy app still needs IAM users.
  • Combined with an MFA requirement for every console user.

Rotating an access key without downtime

An IAM user can have two access keys at once. That is exactly what makes zero-downtime rotation possible: create the second key, move the application to it, check the old key is no longer used, deactivate it (your roll-back path), and only then delete it. If the key is leaked, skip the waiting: deactivate it at once and accept the short outage.

Real AWS example · rotating a key in five commands

The order matters: create, switch, check, deactivate, delete.

$ aws iam create-access-key --user-name build-bot
{ "AccessKey": { "AccessKeyId": "AKIAEXAMPLENEWKEY111", "Status": "Active", ... } }

# 2. update the application / secret store to use the new key, redeploy

$ aws iam get-access-key-last-used --access-key-id AKIAEXAMPLEOLDKEY000
{ "UserName": "build-bot",
  "AccessKeyLastUsed": { "LastUsedDate": "2026-10-18T09:12:00Z", "ServiceName": "s3", "Region": "eu-west-1" } }

$ aws iam update-access-key --user-name build-bot --access-key-id AKIAEXAMPLEOLDKEY000 --status Inactive
# watch the app for a while; if something breaks, set it back to Active

$ aws iam delete-access-key --user-name build-bot --access-key-id AKIAEXAMPLEOLDKEY000

The last-used date stops moving once nothing uses the old key. Inactive is reversible; delete is not.

The “previous admin only used root” checklist. Change the root password (and make it strong), re-register the root MFA on a device you control, delete any root access keys, review IAM users and their keys and remove ones you don’t recognise, and look for resources you didn’t create. Don’t rotate every IAM user’s password or delete custom policies “just in case”: that breaks working users without fixing the risk. In an organization, also consider centralized root access (Day 1 governance) to remove member-account root credentials completely.

Policy size and count limits

Older material quotes these as “2 KB for users, 5 KB for groups, 10 KB for roles, 20 KB for bucket policies”. They are really character quotas, and they matter when a question says a policy “can’t be saved” or “has grown too large”.

ItemLimit (default)What to do when you hit it
Inline policies, total per user2,048 charactersMove to a customer managed policy or a group
Inline policies, total per group5,120 charactersSplit into managed policies
Inline policies, total per role10,240 charactersSplit into managed policies
One managed policy6,144 characters (whitespace not counted)Split it, use wildcards carefully, or use a resource policy
Managed policies attached to a user, group or role10 (role/user quota can be raised to 20)Combine policies or request a quota increase
Versions of a customer managed policy5Delete an old non-default version first
Role trust policy2,048 characters (can be raised to 4,096)Trust an account or org condition instead of many ARNs
Access keys per IAM user2That is the rotation slot
S3 bucket policy / KMS key policy / SCP20 KB / 32 KB / 5,120 charactersUse aws:PrincipalOrgID, prefixes or more OUs
Think of it like · looking after the keys of an office

IAM users are staff with personal keys; you look after them like an office manager.

AWS conceptFamiliar thing
Password policy≈The office rule “PINs must be 8+ digits and changed every 90 days”, for staff badges only
Federated user’s password≈A visitor’s badge issued by their own company, under their company’s rules
Two access keys per user≈Two key slots on a key ring: add the new key before throwing the old one away
Deactivate before delete≈Lock the old door but keep the key for a week in case someone is still stuck outside
Root checklist≈A new manager changes the master key, the alarm code and the safe combination on day one
Policy size limits≈A notice board that only fits so many pages: put the long rules in a binder (managed policy)
Exam-style scenario

A DevOps role has eight inline policies that grew over time. An engineer tries to add one more statement and gets LimitExceeded for the role’s inline policy size. The team wants to keep least privilege and make the permissions reusable by two other roles. What should the engineer do?

  1. A. Create a second role and let the application switch roles when it needs the new permission.
  2. B. Move the statements into one or more customer managed policies and attach them to all three roles.
  3. C. Replace the statements with "Action": "*" to make the policy shorter.
  4. D. Attach the statements to an IAM group and add the role to the group.
Show the worked answer

Answer: B

Inline policies share one 10,240-character budget per role. Customer managed policies each have their own 6,144-character limit, can be attached to many roles (10 by default, up to 20) and are versioned.

Why not the others

  • A: Works around the limit but adds complexity and still doesn’t make the permissions reusable.
  • C: Breaks least privilege.
  • D: Roles can’t be members of groups; groups only hold IAM users.
How the exam tests this
  • “IAM users must have 14-character passwords that expire”→ IAM account password policy.
  • “Password complexity for AD-federated users”→ The Active Directory policy, not IAM.
  • “Password rules for app users in Cognito”→ The Cognito user pool policy.
  • “Rotate a key, applications must keep working”→ Create second key → switch → deactivate → test → delete.
  • “Former admin knew the root password”→ Change root password, re-do root MFA, delete root keys, review users.
  • “Inline policy too large”→ Use customer managed policies (6,144 chars each).
Must remember
  • Password policy covers IAM users only; one per account.
  • Length 6–128, max age 1–1095 d, reuse 1–24.
  • 2 access keys per user → rotation without downtime.
  • Inactive first, delete later (unless leaked).
  • Inline totals: user 2,048 · group 5,120 · role 10,240; managed 6,144; 5 versions; 10 attached.
10

Hands-on lab (30 minutes)

Use a personal sandbox account, never a work production account.

  1. Turn on MFA for the root user and confirm root has no access keys (IAM console, Security credentials).
  2. Create a bucket and a role named lab-s3-reader with an inline policy that allows only s3:GetObject on arn:aws:s3:::your-bucket/*.
  3. Assume the role (console "Switch role" or aws sts assume-role) and run aws s3 ls s3://your-bucket. You should get AccessDenied.
  4. Add s3:ListBucket on arn:aws:s3:::your-bucket (no /*) and try again. It now works. You have just met exam trap number one.
  5. In the IAM policy editor, notice the warnings and suggestions under the editor. That is IAM Access Analyzer policy validation, which Day 6 covers.
11

Lecture summary map

The whole lecture on one screen. Use the three pictures below to review: the mind map for the structure, the keys-fences-wall picture for how permissions work, and the keyword chart for answering exam questions fast.

1. Mind map

① Request checkAuthN ≠ AuthZDefault = DENYExplicit Deny always wins② PrincipalsRoles beat access keysGroups are not principalsEC2 → instance profile③ Root userMFA on, no access keysOnly SCP/RCP can limit itLocked bucket → AssumeRoot④ Policy typesGRANT: identity · resource· ACL · RAM shareLIMIT: SCP·RCP·boundary…⑤ SCP vs RCPSCP = my PEOPLERCP = my RESOURCESBoth = data perimeter⑥ Policy + conditionsEffect·Principal·ActionResource·ConditionKeys AND · values ORIAM Day 1who · what · limits · words
Tap a branch to jump back to that part of the lecture. Try redrawing this map from memory on paper: if you can fill in all six branches, you know Day 1.

2. Keys, fences and a wall

🔑 KEYSopen doors (grant) Identity policyResource policyACLRAM share 🚧 FENCESset the max (limit) SCPRCPPermissions boundarySession policyVPC endpoint policy 🧱 WALLends the story Explicit Denyin ANY policyno key or fencegets past it
To get through you need at least one key, every fence must have a gap for you, and there must be no wall.

3. “If the question says… pick…”

Exam questions are full of signal words. When you spot the words on the left, the answer is usually the one on the right.

App on EC2 / Lambda / ECS needs AWS accessIAM role (instance profile / execution role)
Humans, workforce, existing Okta / Entra ID / ADIAM Identity Center + external IdP
On-premises servers need AWS accessIAM Roles Anywhere
Limit what everyone in member accounts can doSCP
Stop outsiders reaching our resources, even with a bad policyRCP + aws:PrincipalOrgID
Let developers create roles without escalatingPermissions boundary
Narrow one assumed-role sessionSession policy
Trust every account in the organizationaws:PrincipalOrgID
IP rule breaks behind a VPC endpointaws:SourceVpce / aws:SourceVpc
Require MFA, access-key scripts slip throughBoolIfExists
Block unapproved RegionsSCP Deny + NotAction + aws:RequestedRegion
Bucket policy locked everyone outRoot user, or AssumeRoot + S3UnlockBucketPolicy
ExpiredToken / InvalidClientTokenIdCredentials problem, not policies
Day 1 in one sentence

Nothing is allowed until a key grants it, every fence must agree, and one Deny ends the story.

12

Must-remember cheat sheet

Read this the night before the exam. Each line is something the exam has a habit of testing.

1. Default deny. No Allow anywhere means denied.
2. Explicit Deny always wins, whatever policy it is in.
3. Only identity policies, resource policies, ACLs and RAM shares grant.
4. SCP, RCP, boundary, session and endpoint policies only limit.
5. SCP = my principals. RCP = my resources.
6. SCP/RCP skip the management account and service-linked roles.
7. Root: IAM can't restrict it; SCP can in member accounts. MFA on, no keys.
8. Locked-out bucket policy = root (or central privileged task) fixes it.
9. Prefer roles (temporary creds) over IAM user access keys, always.
10. Groups are not principals and cannot be nested.
11. Principal only appears in resource-based and trust policies.
12. ListBucket → bucket ARN. Get/PutObject → bucket/*.
13. aws:PrincipalOrgID to trust the whole org in one line.
14. aws:SourceIp fails behind a VPC endpoint → use aws:SourceVpce.
15. MFA deny → BoolIfExists, because access keys lack the key.
16. Region lock = SCP Deny + NotAction (global services) + aws:RequestedRegion.
17. Password policy covers IAM users only; federated → IdP, Cognito → user pool.
18. Rotate keys: create 2nd → switch → deactivate → test → delete.
19. Inline limits: user 2,048 · group 5,120 · role 10,240 chars; managed 6,144.
13

Practice quiz

Pick an answer, then press Check. Multiple-response questions say how many to choose.

0 / 8 checked
14

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.