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

Access control models: RBAC, ABAC, paths and Cedar

Days 1 to 4 covered who you are and how policies are evaluated. Today is about designing permissions that scale: role-based vs attribute-based access, the tag keys that make ABAC work and the guardrails that keep it safe, IAM paths for delegation, and Amazon Verified Permissions for authorization inside your own apps.

Skill 4.2.1 Verified Permissions, IAM pathsSkill 4.2.2 ABAC and RBAC strategies~80 min read + 30 min lab
01

RBAC and ABAC: two ways to grant access

Role-based access control (RBAC) grants permissions by job function: a “Developer” role, a “DBA” group, an AWS managed job-function policy. Attribute-based access control (ABAC) grants permissions by attributes, which AWS calls tags: a principal may touch a resource when their tags match. Most real companies use both: RBAC for the broad job, ABAC to keep each team inside its own resources.

Concept profile

Attribute-based access control (ABAC)

often confused with role-based access control (RBAC)
?What is it?

An authorization strategy where a small number of policies compare tags on the principal with tags on the resource (and in the request). Access changes when tags change, not when policies change.

⚙How does it work?
  • Tag principals (IAM roles, users, or session tags from your IdP) with attributes such as project or team.
  • Tag resources with the same keys.
  • Write one policy whose condition says aws:ResourceTag/project must equal ${aws:PrincipalTag/project}.
  • New resources become reachable as soon as they carry the right tag.
{ }Structure
RBAC: one policy per job × projectDev-HeartpolicyDev-StarpolicyDev-BoltpolicyOps-HeartpolicyOps-StarpolicyOps-BoltpolicyNew project → write 2 more policiesABAC: one policy, matching tagsaws:ResourceTag/project ==${aws:PrincipalTag/project}project=Heartproject=Starproject=BoltNew project → just tag it. No policy change.
RBAC grows with every project. ABAC stays one policy.
⇄Attribute-based access control vs role-based access control (RBAC)
ABACRBAC
Grants byMatching attributes (tags)Job function (role, group)
New resourceTag it; no policy changeUpdate the policy to list it
Number of policiesFewMany (job × project)
Risk to manageWho can change tagsPolicy sprawl, over-broad roles
Works with IdP attributesYes, session tagsThrough group → role mapping
Supported everywhere?No; check each service's tag supportYes
!Common mistakes the exam tests
  • Forgetting that ABAC is only as safe as tag control: whoever can change a tag can change access.
  • Assuming every service and action supports aws:ResourceTag. Check the service authorization reference.
  • Using aws:ResourceTag with iam:PassRole; AWS warns this is unreliable. Use paths or names.
  • Picking RBAC when the question stresses “many new projects, minimal policy updates”.
✓Benefits and use cases
  • Fast-growing environments with many projects or teams.
  • Fewer policies to audit.
  • Use corporate directory attributes directly (SAML, OIDC, Identity Center).
Think of it like · a school

Two ways a school can decide who enters which classroom.

AWS conceptFamiliar thing
RBAC≈A key per job: “teacher key”, “cleaner key”; new classroom means new keys
ABAC≈Colour stickers: a red-sticker student opens red-sticker rooms
Principal tag≈The sticker on your student card
Resource tag≈The sticker on the classroom door
Tag governance≈Only the office may print stickers
Real AWS example · AWS managed job-function policies (RBAC)

AWS ships policies for common jobs. Attach them to roles or permission sets.

arn:aws:iam::aws:policy/job-function/DatabaseAdministrator
arn:aws:iam::aws:policy/job-function/NetworkAdministrator
arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
arn:aws:iam::aws:policy/job-function/SystemAdministrator

These are a starting point. They are broad, so the exam usually prefers a least-privilege customer managed policy for production.

Exam-style scenario

A company creates 10–20 new projects every month. Each project has developers who must reach only that project's Secrets Manager secrets and EC2 instances. The security team wants to avoid editing IAM policies every time a project starts. What is the MOST scalable approach?

  1. A. Create a role and policy per project listing each secret ARN.
  2. B. Tag roles and resources with project, and use one policy that allows access when aws:ResourceTag/project equals ${aws:PrincipalTag/project}.
  3. C. Create an IAM group per project.
  4. D. Put each project in its own Region.
Show the worked answer

Answer: B

This is ABAC. One policy covers all current and future projects; access follows tags.

Why not the others

  • A: RBAC with resource lists means constant policy edits.
  • C: Groups are still RBAC and still need a policy per project.
  • D: Regions aren't an access control model.
How the exam tests this
  • “Many new projects, fewest policy changes”→ ABAC with tags.
  • “Permissions by job function”→ RBAC (roles, groups, job-function policies).
  • “Employee department from IdP decides access”→ Session tags + ABAC.
Must remember
  • RBAC = job; ABAC = tags.
  • ABAC: aws:ResourceTag/k = ${aws:PrincipalTag/k}.
  • ABAC security = control who can change tags.
  • Not every action supports resource tags; check the service.
02

Tag condition keys: ResourceTag, RequestTag, TagKeys, PrincipalTag

ABAC policies use four global condition keys. The exam often gives two nearly identical policies and asks which one works. The trick is to know where each key reads its value: from the caller, from the request being sent, or from the resource that already exists.

Concept profile

aws:RequestTag

often confused with aws:ResourceTag
?What is it?

A condition key holding the tag values sent in the request, for example the tags on a CreateSecret or TagResource call. Use it to control which tags people may set.

⚙How does it work?
  • The caller sends tags with a create or tag action.
  • aws:RequestTag/key = the value being sent; aws:TagKeys = the list of keys being sent.
  • Your policy allows the action only when the values are the right ones (often equal to the caller's own aws:PrincipalTag).
  • After creation, later actions on the resource check aws:ResourceTag.
{ }Structure
Caller aws:PrincipalTag tags on the caller The request CreateSecret, TagResource Tags: project=Heart aws:RequestTag/project value being sent aws:TagKeys list of keys being sent Existing resource aws:ResourceTag tags already on it Principal = who asks · Request = what they send · Resource = what is already there
Ask: who asks, what are they sending, what is already there?
⇄aws:RequestTag vs aws:ResourceTag
aws:RequestTagaws:ResourceTag
ReadsTags in the API requestTags already on the resource
Typical actionsCreate*, Tag*, RunInstancesGet, Put, Start, Stop, Delete…
PurposeControl what tags can be setControl access based on existing tags
Partner keyaws:TagKeys (which keys)aws:PrincipalTag (compare to caller)
!Common mistakes the exam tests
  • Checking aws:ResourceTag on a create call: the resource doesn't exist yet, so use aws:RequestTag.
  • ForAllValues:StringEquals on aws:TagKeys is true when no tags are sent. Add a check that the required tag is present (aws:RequestTag/key or Null = false).
  • Forgetting tag keys in conditions are case-insensitive, while values are compared as written.
  • Using ForAnyValue when you mean “only these keys are allowed” (that's ForAllValues).
✓Benefits and use cases
  • Force every new secret or instance to carry the creator's project tag.
  • Allow only an approved list of tag keys.
  • Name resources with ${aws:PrincipalTag/team} in the Resource ARN.
Think of it like · posting a parcel

The post office checks the label you write, the label already on the box, and who you are.

AWS conceptFamiliar thing
aws:PrincipalTag≈Your ID card at the counter
aws:RequestTag≈The label you are writing on a new parcel right now
aws:TagKeys≈Which fields you filled in on the form
aws:ResourceTag≈The label already stuck on a parcel in the warehouse
ForAllValues on TagKeys≈“Only these fields may be filled in” (an empty form passes too!)
Real AWS example · create only with your own project tag, then use only your project's secrets

Two statements from the AWS ABAC tutorial pattern.

[{ "Sid": "CreateWithMyTags",
   "Effect": "Allow",
   "Action": ["secretsmanager:CreateSecret", "secretsmanager:TagResource"],
   "Resource": "*",
   "Condition": {
     "StringEquals": { "aws:RequestTag/project": "${aws:PrincipalTag/project}" },
     "ForAllValues:StringEquals": { "aws:TagKeys": ["project", "team", "Name"] } } },
 { "Sid": "UseMyProject",
   "Effect": "Allow",
   "Action": ["secretsmanager:GetSecretValue", "secretsmanager:PutSecretValue"],
   "Resource": "*",
   "Condition": { "StringEquals": {
     "aws:ResourceTag/project": "${aws:PrincipalTag/project}" } } }]

The first statement needs the project tag to be present and equal to the caller's own; the StringEquals on RequestTag also blocks a create with no project tag. The second uses the tag already on the secret.

Exam-style scenario

A policy should let developers launch EC2 instances only if every instance is tagged team equal to their own team. The draft checks aws:ResourceTag/team = ${aws:PrincipalTag/team} on ec2:RunInstances, and launches keep failing. What should change?

  1. A. Use aws:PrincipalTag only.
  2. B. Check aws:RequestTag/team on the instance resource for RunInstances, and allow ec2:CreateTags with ec2:CreateAction = RunInstances.
  3. C. Use ForAnyValue:StringEquals on aws:ResourceTag.
  4. D. Attach AmazonEC2FullAccess.
Show the worked answer

Answer: B

At launch the instance doesn't exist yet, so its tags arrive in the request. Tagging on create also needs ec2:CreateTags, limited to the RunInstances create action.

Why not the others

  • A: Doesn't check the instance's tag at all.
  • C: Still reads tags on an existing resource.
  • D: Throws away least privilege.
How the exam tests this
  • “Restrict which tags can be set on create”→ aws:RequestTag + aws:TagKeys.
  • “Access based on existing tag”→ aws:ResourceTag.
  • “Only an allowed set of keys”→ ForAllValues:StringEquals on aws:TagKeys (+ require the key).
  • “Tag on launch only”→ ec2:CreateTags with ec2:CreateAction.
Must remember
  • Principal = caller, Request = being sent, Resource = already there.
  • Create calls → RequestTag, not ResourceTag.
  • ForAllValues is true for an empty set.
  • Tag keys: case-insensitive in conditions.
03

Protecting the tags that protect you

In ABAC the tag is the permission. If a developer can change their own role's project tag, or retag a production database as project=mine, they have just granted themselves access. So every ABAC design needs tag guardrails: limit who can tag principals, limit which tags can be changed on resources, and back it up with an SCP. AWS Organizations tag policies help keep tags consistent, but they are not an access control.

Concept profile

Tag guardrails (deny tag changes)

often confused with tag policies (AWS Organizations)
?What is it?

Deny statements in SCPs or IAM policies that stop people changing the tags used for authorization, except a trusted admin role or tagging at creation.

⚙How does it work?
  • Deny iam:TagRole, iam:UntagRole, iam:TagUser, iam:UntagUser for everyone except the identity admin.
  • Deny tagging actions on resources when aws:TagKeys includes the authorization keys, unless it is part of a create.
  • Put the deny in an SCP so even account admins can't bypass it.
  • Monitor with CloudTrail / Config for tag changes.
{ }Structure
{ "Sid": "ProtectAuthzTags",
  "Effect": "Deny",
  "Action": ["ec2:CreateTags", "ec2:DeleteTags"],
  "Resource": "*",
  "Condition": {
    "ForAnyValue:StringEquals": { "aws:TagKeys": ["project", "team"] },
    "StringNotEquals": { "ec2:CreateAction": "RunInstances" },
    "ArnNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/TagAdmin" } } }
⇄Tag guardrails vs tag policies (AWS Organizations)
Tag guardrailsTag policies
ServiceIAM policies / SCPsAWS Organizations policy type
PurposeStop unauthorized tag changes (security)Standardize keys, values, capitalization (consistency)
Grants or denies access?Denies the tagging actionNever grants access; can block noncompliant tagging for enforced resource types
Typical exam word“prevent users from changing tags to gain access”“ensure consistent tag values across accounts”
!Common mistakes the exam tests
  • Using ForAllValues in a Deny on TagKeys: a request with other keys too would slip through; use ForAnyValue to catch any protected key.
  • Protecting resource tags but not principal tags (iam:TagRole).
  • Thinking tag policies alone secure ABAC.
  • Forgetting session tags from the IdP: protect the IdP attribute source too.
✓Benefits and use cases
  • Makes ABAC trustworthy.
  • Separation of duties: tag admins vs builders.
  • Consistent tagging for cost reports too.
Think of it like · a hotel's coloured wristbands

If guests could recolour their own wristband, the VIP pool would be full.

AWS conceptFamiliar thing
Principal tag≈Your wristband colour
Resource tag≈The colour painted on the pool gate
Tag guardrail (SCP deny)≈Only reception may change wristbands, and the rule is above the hotel manager
Tag policy≈The style guide saying colours must be “Red”, never “red” or “RED”
Tag on create≈Your wristband is put on at check-in, not changed later
Real AWS example · stop anyone except the identity team from retagging roles

An SCP statement for the whole organization.

{ "Sid": "OnlyIdentityAdminsTagRoles",
  "Effect": "Deny",
  "Action": ["iam:TagRole", "iam:UntagRole", "iam:TagUser", "iam:UntagUser"],
  "Resource": "*",
  "Condition": { "ArnNotLike": {
      "aws:PrincipalArn": "arn:aws:iam::*:role/identity-admin/*" } } }

The exception uses aws:PrincipalArn with a path (next section), so every role under /identity-admin/ is trusted.

Exam-style scenario

A company uses ABAC on S3 and DynamoDB with the team tag. An audit shows a developer added team=finance to an S3 bucket they created and to their own IAM role, and then read finance data. Which combination prevents this? (Choose the BEST.)

  1. A. An AWS Organizations tag policy that defines allowed team values.
  2. B. An SCP that denies iam:TagRole/UntagRole and resource tagging of the team key except by a tag-admin role (and only on create for builders).
  3. C. Enable S3 Block Public Access.
  4. D. Turn on CloudTrail data events.
Show the worked answer

Answer: B

The attack is changing tags. Only a deny on tag changes, set above account admins with an SCP, removes the ability. Tag policies would still accept finance because it's a valid value.

Why not the others

  • A: Consistency, not security: finance is an allowed value.
  • C: Unrelated to tag abuse.
  • D: Detects after the fact; doesn't prevent.
How the exam tests this
  • “Prevent users from granting themselves access by changing tags”→ Deny tag actions (SCP) on authz keys.
  • “Standardize tag keys/values across org”→ Tag policies.
  • “Protect principal tags”→ Deny iam:TagRole / TagUser.
Must remember
  • In ABAC, tag change = permission change.
  • Deny with ForAnyValue on protected keys.
  • Tag policies = consistency, not access control.
  • Protect both resource and principal tags.
04

IAM paths

An IAM path is a folder-like prefix in the name of a user, group, role or customer managed policy, such as /platform/ or /team-a/. It shows up in the ARN: arn:aws:iam::111122223333:role/team-a/ETL. Paths grant nothing by themselves, but they let one policy statement cover a whole folder with a wildcard. That makes them great for delegation: “team A may create and pass only roles under /team-a/”.

Concept profile

IAM path

often confused with Organizations OU path (aws:PrincipalOrgPaths)
?What is it?

An optional hierarchical prefix (starting and ending with /) on IAM users, groups, roles, policies, instance profiles and identity providers, used for organizing and for wildcard matching in ARNs.

⚙How does it work?
  • Set it when creating with the API or CLI: aws iam create-role --path /team-a/ …. The console can't create or change paths.
  • The ARN includes it: role/team-a/ETL.
  • Policies match it with wildcards: arn:aws:iam::111122223333:role/team-a/*.
  • Use aws:PrincipalArn with ArnLike to match callers by path in resource policies and SCPs.
{ }Structure
//platform/role/platform/AppDeployrole/platform/LambdaExec/team-a/role/team-a/Analystrole/team-a/ETL One policy covers a whole folder "Action": "iam:PassRole", "Resource": "arn:aws:iam::111122223333: role/platform/*" Paths name and group things. They grant nothing by themselves. assumed-role/ETL/minh The session ARN has NO path.
Folders for roles. One wildcard covers the folder.
⇄IAM path vs Organizations OU path (aws:PrincipalOrgPaths)
IAM pathOU path
Lives inIAM, inside one accountAWS Organizations, across accounts
GroupsUsers, roles, policiesAccounts
Matched withARN wildcard, aws:PrincipalArnaws:PrincipalOrgPaths, aws:ResourceOrgPaths
Grants access?No, only used for matchingNo, only used for matching
Console supportNo (API/CLI only)Yes
!Common mistakes the exam tests
  • Thinking a path is a security boundary. Users with different paths can still access each other's resources if a policy allows it.
  • Matching the session ARN by path: arn:aws:sts::…:assumed-role/ETL/minh doesn't include the path. Use aws:PrincipalArn (the role ARN, with path).
  • Wildcards in the Principal element aren't allowed; use "AWS": "*" plus an aws:PrincipalArn condition.
  • Expecting a user to join a group just because they share a path.
✓Benefits and use cases
  • Delegated role creation: team admins create roles only under their path.
  • Safe iam:PassRole: pass only roles under /service-roles/.
  • SCP exceptions for a whole family of admin roles (Identity Center roles live under /aws-reserved/sso.amazonaws.com/).
Think of it like · a company file server

Instead of sharing files one by one, you share a whole folder.

AWS conceptFamiliar thing
IAM path≈A folder on the file server: /team-a/
Role or policy≈A file inside that folder
Wildcard role/team-a/*≈“Everything in the team-a folder”
Path isn't a boundary≈A folder name doesn't lock the files; permissions do
Session ARN without path≈A printout of the file: it shows the file name, not the folder
Real AWS example · delegate role creation to team A, safely

Team A admins can create roles only in their folder, and only with the required permissions boundary (Day 2). They can pass only roles from that folder.

[{ "Effect": "Allow",
   "Action": ["iam:CreateRole", "iam:PutRolePolicy", "iam:AttachRolePolicy"],
   "Resource": "arn:aws:iam::111122223333:role/team-a/*",
   "Condition": { "StringEquals": {
     "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/TeamABoundary" } } },
 { "Effect": "Allow",
   "Action": "iam:PassRole",
   "Resource": "arn:aws:iam::111122223333:role/team-a/*",
   "Condition": { "StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" } } }]

Paths + boundaries + iam:PassedToService are the classic delegation trio. This is also how AWS suggests limiting PassRole instead of using tags.

Exam-style scenario

A central S3 bucket must allow access to every role under the /data-eng/ path in account 111122223333, including roles created in the future. Which bucket policy approach works?

  1. A. "Principal": {"AWS": "arn:aws:iam::111122223333:role/data-eng/*"}
  2. B. "Principal": {"AWS": "*"} with condition ArnLike aws:PrincipalArn = arn:aws:iam::111122223333:role/data-eng/*
  3. C. "Principal": {"AWS": "arn:aws:sts::111122223333:assumed-role/data-eng/*"}
  4. D. Tag the bucket path=data-eng.
Show the worked answer

Answer: B

Principal ARNs can't contain wildcards. Allowing any principal and filtering with aws:PrincipalArn (the role ARN, which includes the path) works for current and future roles.

Why not the others

  • A: Wildcards aren't allowed in the Principal element.
  • C: Session ARNs don't include the path, and wildcards still aren't allowed.
  • D: There's no such matching key.
How the exam tests this
  • “Delegate role creation to a team”→ Path in Resource + iam:PermissionsBoundary condition.
  • “Restrict which roles can be passed”→ iam:PassRole on role/path/* + iam:PassedToService.
  • “Allow every role under a path in a resource policy”→ Principal * + aws:PrincipalArn ArnLike.
Must remember
  • Paths = folders; not a security boundary.
  • CLI/API only; the console can't set paths.
  • Session ARN has no path; aws:PrincipalArn has it.
  • Prefer paths, not tags, for iam:PassRole.
05

Amazon Verified Permissions and Cedar

IAM decides who can call AWS APIs. But your own application also has permissions: “can Minh view this invoice?”, “can a vet edit this pet's record?”. Developers usually hard-code those rules. Amazon Verified Permissions moves them into a managed service: you write rules in the Cedar policy language, and your app asks Verified Permissions IsAuthorized? before each action.

Concept profile

Amazon Verified Permissions

often confused with IAM policies
?What is it?

A managed, fine-grained authorization service for your own applications. It stores Cedar policies in a policy store and returns Allow or Deny for a principal, action, resource and context.

⚙How does it work?
  • Create a policy store (one per app or tenant) and a schema describing entity types and actions.
  • Write Cedar policies (permit / forbid), or templates that create linked policies per user or resource.
  • Your app (or an API Gateway Lambda authorizer) calls IsAuthorized, IsAuthorizedWithToken or BatchIsAuthorized.
  • Verified Permissions returns Allow or Deny plus the determining policies. Your code enforces it.
{ }Structure
App user Cognito / OIDC token Your application or API Gateway Lambda authorizer enforces the decision IsAuthorized Allow / Deny Verified Permissions policy store • Schema: entity types, actions • Cedar policies: permit / forbid • Policy templates + linked policies • Identity source: Cognito or OIDC principal · action · resource + context (IP, time, MFA…) Verified Permissions decides; your code must still enforce. Verified Permissions never touches your data.
Your app asks; Verified Permissions answers; your app enforces.
⇄Amazon Verified Permissions vs IAM policies
Verified PermissionsIAM policies
ProtectsActions inside your applicationAWS API calls
LanguageCedarJSON policy grammar
PrincipalsApp users from Cognito or any OIDC IdPIAM users, roles, sessions
Who enforcesYour application codeAWS automatically
Deny modelDefault deny; forbid overrides permitDefault deny; explicit Deny overrides Allow
!Common mistakes the exam tests
  • Picking IAM for in-app permissions like “who can approve this expense”.
  • Thinking Verified Permissions blocks requests by itself. The app must call it and act on the answer.
  • Picking Cognito groups alone when the question needs fine-grained, auditable, centrally managed rules.
  • Forgetting that a forbid beats every permit.
✓Benefits and use cases
  • Centralized, auditable app permissions instead of if-statements in code.
  • RBAC and ABAC in the same policy.
  • Multi-tenant SaaS: a policy store per tenant, or shared store with tenant attributes.

How Cedar decides

1Any forbid matches?→ DENY (forbid always wins)2Any permit matches?→ ALLOW3Nothing matches→ DENY (default deny)Same idea as IAM: explicit deny beats allow, and no allow means deny.
Three questions, always in this order.
Think of it like · a nightclub bouncer with a rulebook

The club owner writes the rules; the bouncer checks them at the door.

AWS conceptFamiliar thing
Your application≈The club
Verified Permissions≈The bouncer
Policy store≈The rulebook for this club
Schema≈The list of what can appear in rules: guests, rooms, actions
permit≈“VIPs may enter the lounge”
forbid≈“No one under 18 enters the bar”, even VIPs
Context≈Time of night, ID shown, dress code
Real AWS example · Cedar policies: RBAC, ABAC and forbid

Three policies in one policy store for a photo-sharing app.

// RBAC: admins may do anything to photos
permit (principal in PhotoApp::Group::"Admins", action, resource);

// ABAC: owners may view and edit their own photos
permit (
  principal,
  action in [PhotoApp::Action::"viewPhoto", PhotoApp::Action::"editPhoto"],
  resource
) when { resource.owner == principal };

// Guardrail: nobody may delete a photo under legal hold
forbid (principal, action == PhotoApp::Action::"deletePhoto", resource)
when { resource.legalHold == true };

Even an admin can't delete a photo on legal hold: the forbid wins.

Exam-style scenario

A healthcare SaaS app has complex rules such as “a vet can view records of pets at their own clinic, but never records marked confidential unless they are the pet's primary vet”. Today these rules are scattered across microservices and hard to audit. What should the team use?

  1. A. IAM identity policies with condition keys for each app user.
  2. B. Amazon Verified Permissions with Cedar policies (permit for clinic match, forbid for confidential unless primary vet), called from each service.
  3. C. Cognito user pool groups only.
  4. D. S3 bucket policies.
Show the worked answer

Answer: B

These are application-level, fine-grained rules. Verified Permissions centralizes them in Cedar, supports ABAC and forbid rules, and returns auditable decisions.

Why not the others

  • A: App users aren't IAM principals; IAM guards AWS APIs.
  • C: Groups give coarse roles, not attribute rules.
  • D: Only protects S3 objects, not app actions.
How the exam tests this
  • “Fine-grained permissions inside a custom app”→ Verified Permissions (Cedar).
  • “Rule that must override all others”→ Cedar forbid.
  • “Reusable rule per user/resource”→ Policy template + linked policies.
Must remember
  • Verified Permissions = authorization for your app, IAM = for AWS APIs.
  • Cedar: forbid > permit > default deny.
  • PARC: principal, action, resource, context.
  • The app must enforce the decision.
06

Verified Permissions in practice: tokens, templates and API Gateway

In a real app the user usually signs in with Cognito or another OIDC provider (Day 4). Verified Permissions can read that token directly through an identity source, turn its claims into a principal with attributes and groups, and decide. For APIs, a Lambda authorizer in API Gateway can make the call before the request reaches your backend.

Concept profile

IsAuthorizedWithToken

often confused with IsAuthorized
?What is it?

An authorization call where you pass the user's ID or access token from the policy store's identity source instead of describing the principal yourself.

⚙How does it work?
  • Add an identity source to the policy store: a Cognito user pool or an OIDC IdP.
  • Your app passes the token, the action and the resource.
  • Verified Permissions validates the token and maps it: the user becomes the principal; a groups claim becomes principal groups.
  • ID token claims become principal attributes; access token claims become context.
{ }Structure
APIPrincipal comes fromUse when
IsAuthorizedEntities you pass in the requestYour own identity system, machine principals
IsAuthorizedWithTokenA Cognito / OIDC tokenUsers signed in through the identity source
BatchIsAuthorizedMany requests at onceFiltering a list (“which of these 50 documents can I see?”)
⇄IsAuthorizedWithToken vs IsAuthorized
Policy templateStatic policy
ContainsPlaceholders ?principal and/or ?resourceFixed principal/resource or conditions
CreatesTemplate-linked policies (one per share)Itself
Change once, applies to all?Yes, editing the template updates all linked policiesOnly that policy
Example“Share this album with this user”“Admins may delete”
!Common mistakes the exam tests
  • Putting placeholders other than ?principal and ?resource in a template (actions can't be placeholders).
  • Forgetting the schema: with strict validation, policies that reference unknown types or attributes are rejected.
  • Expecting access-token claims to be principal attributes (they map to context).
  • Calling Verified Permissions from the browser instead of a trusted backend or authorizer.
✓Benefits and use cases
  • “Share” buttons: each share is a template-linked policy.
  • API Gateway + Lambda authorizer for every route.
  • Per-tenant policy stores in SaaS.
Think of it like · a library card system

One card, many rules, and personal loans.

AWS conceptFamiliar thing
Identity source (Cognito/OIDC)≈The city's resident card office
Token≈Your library card
groups claim → principal group≈The “student” or “staff” stamp on the card
Policy template≈A blank loan slip: “__ may borrow __”
Template-linked policy≈A filled-in slip: “Minh may borrow Book 42”
BatchIsAuthorized≈Checking a whole basket of books at once
Real AWS example · a share-album template and one linked policy

The template is written once; each share creates a linked policy with real values.

// Template
permit (
  principal == ?principal,
  action in [PhotoApp::Action::"viewPhoto"],
  resource in ?resource
);

// Create a template-linked policy (CLI)
aws verifiedpermissions create-policy --policy-store-id ps-1234 \
  --definition '{"templateLinked": {"policyTemplateId": "tpl-share",
     "principal": {"entityType": "PhotoApp::User", "entityId": "minh"},
     "resource":  {"entityType": "PhotoApp::Album", "entityId": "holiday-2026"}}}'

If the company later decides sharing should also allow downloadPhoto, editing the template updates every linked policy at once.

Exam-style scenario

An API on Amazon API Gateway uses a Cognito user pool. Each route must be authorized with rules based on the user's Cognito group and attributes, with the rules managed outside the application code. What is the MOST appropriate design?

  1. A. A Cognito authorizer on API Gateway only.
  2. B. A Verified Permissions policy store with the Cognito user pool as identity source, and an API Gateway Lambda authorizer that calls IsAuthorizedWithToken.
  3. C. IAM authorization on API Gateway with one IAM role per user.
  4. D. AWS WAF rules per group.
Show the worked answer

Answer: B

Verified Permissions reads the Cognito token, maps groups and attributes, and evaluates Cedar policies managed centrally. The Lambda authorizer enforces the decision per route.

Why not the others

  • A: Validates the token, but can't express attribute rules per route.
  • C: IAM roles per user don't scale and IAM isn't for app logic.
  • D: WAF filters traffic, not user permissions.
How the exam tests this
  • “Pass a Cognito token to get a decision”→ IsAuthorizedWithToken.
  • “Authorize many items at once”→ BatchIsAuthorized.
  • “Share a resource with one user, repeatable”→ Policy template + linked policy.
  • “Authorize API Gateway routes with Cedar”→ Lambda authorizer → Verified Permissions.
Must remember
  • Identity sources: Cognito user pool or OIDC.
  • ID token claims → principal attributes; access token claims → context.
  • Templates only have ?principal and ?resource.
  • The schema validates policies.
07

Cheatsheet extras: how conditions combine, and the Not* elements

Your cheat sheet lists the condition rules in one breath: “operators are ANDed, multiple values in an operator are ORed”, plus warnings about NotAction, NotResource and NotPrincipal. ABAC policies are full of conditions, so this is the place to make those rules automatic.

"Condition": { "StringEquals": {"aws:PrincipalTag/team": ["blue","green"]"aws:RequestedRegion": "eu-west-1"} "Bool": {"aws:MultiFactorAuthPresent": "true"} ANDblue OR greenAND }
Every operator must match AND every key inside it must match; a key with a list of values matches if ANY value matches.
Part of the Condition blockHow it combinesExample meaning
Several operators (StringEquals, Bool…)ANDRight team and MFA
Several keys in one operatorANDRight team and right Region
Several values for one keyORTeam is blue or green
…IfExistsMatch if the key is missing tooDeny without MFA, even for access-key calls (Day 1)
Null true / falseKey is absent / present"Null": {"aws:RequestTag/team": "true"} + Deny = “tag is required”
Need OR between two different keysWrite two statementsAllow if tag matches, second Allow if path matches
Not* elements with Allow are dangerous. "Effect": "Allow", "NotAction": "iam:*" allows every other action in every service, including ones AWS launches next year. Allow + NotResource likewise covers every other resource. The safe, common patterns are Deny + NotAction (for example the Region-lock SCP that exempts global services) and Deny + NotResource. NotPrincipal is only sensible with Deny, and AWS now recommends aws:PrincipalArn conditions instead.
Think of it like · a club door policy

The bouncer reads the rules on the door.

AWS conceptFamiliar thing
Two operators≈“Over 18” and “wearing shoes”: both must be true
Two values for one key≈“Member of club A or club B”: either card works
IfExists≈“If you have a bag, it must be checked”: no bag also passes
Null = true + Deny≈“No name badge, no entry”
Allow + NotAction≈“You may do anything except dance”: a much wider promise than it sounds
Deny + NotAction≈“Everything is forbidden except the cloakroom and the exit”
Real AWS example · requiring a team tag when instances are created

Two Deny statements: one requires the tag, one limits its values. Separate statements give OR between them; inside each, the rules are AND.

{ "Version": "2012-10-17", "Statement": [
  { "Sid": "RequireTeamTag", "Effect": "Deny", "Action": "ec2:RunInstances",
    "Resource": "arn:aws:ec2:*:*:instance/*",
    "Condition": { "Null": { "aws:RequestTag/team": "true" } } },
  { "Sid": "OnlyMyTeam", "Effect": "Deny", "Action": "ec2:RunInstances",
    "Resource": "arn:aws:ec2:*:*:instance/*",
    "Condition": { "StringNotEquals": { "aws:RequestTag/team": "${aws:PrincipalTag/team}" } } } ] }

Missing tag → first Deny matches. Wrong tag → second Deny matches. Correct tag → neither matches, so an Allow elsewhere can work.

Exam-style scenario

An engineer wants developers to manage EC2 instances when either the instance’s team tag equals their own or the instance’s shared tag is yes. They write one statement with both keys under StringEquals. Developers can only manage shared instances that also belong to their team. Why, and what is the fix?

  1. A. Tag keys are case-sensitive; rename the tags.
  2. B. Keys inside one operator are ANDed. Use two Allow statements, one per condition.
  3. C. Values are ANDed; use ForAnyValue.
  4. D. Use StringLike instead of StringEquals.
Show the worked answer

Answer: B

Two keys under the same operator must both match. To get OR across different keys, write two statements.

Why not the others

  • A: Tag keys in conditions are case-insensitive; the logic is the problem.
  • C: Multiple values of one key are already ORed; ForAnyValue is for multivalued keys.
  • D: Wildcards don’t change AND into OR.
How the exam tests this
  • “Allow only if team is blue or green”→ One key, two values (OR).
  • “Allow if tag matches OR path matches”→ Two statements.
  • “Require a tag on create”→ Deny with Null = true on aws:RequestTag/….
  • “Policy uses Allow + NotAction”→ Too broad: allows everything else, including future services.
  • “Region lock that keeps IAM and CloudFront working”→ Deny + NotAction + aws:RequestedRegion.
Must remember
  • Operators AND · keys AND · values OR.
  • OR across keys → separate statements.
  • Null: true = key missing.
  • Allow + NotAction / NotResource = very broad.
  • Deny + NotAction = safe exception list.
08

Lecture summary map

Day 5 on one screen: the mind map, the “which lock answers which question” picture, and the keyword chart.

1. Mind map

① RBAC vs ABACRBAC = job functionABAC = matching tagsFew policies, scales② Tag keysPrincipal / Request / ResourceCreate → RequestTagForAllValues: empty = true③ Tag guardrailsTag change = access changeSCP deny tag actionsTag policies ≠ security④ IAM pathsrole/team-a/* wildcardNot a boundarySession ARN: no path⑤ Verified PermissionsAuthz for YOUR appCedar permit / forbidForbid always wins⑥ AVP in practiceIsAuthorizedWithTokenTemplate slots: 2 onlyAPI GW Lambda authorizerIAM Day 5access control models
Tap a branch to jump back to that part of the lecture. Try redrawing this map from memory on paper.

2. Which lock answers which question?

Who you are (job)→RBACroles, groups, job-function policiesWhat you are tagged with→ABACPrincipalTag = ResourceTagWhich folder you live in→IAM pathsrole/team-a/* in ResourceWho may change the tags→Tag guardrailsRequestTag · TagKeys · SCP denyInside YOUR app→Verified PermissionsCedar permit / forbid
Cover the middle column and say the model for each question out loud.

3. “If the question says… pick…”

Permissions by job functionRBAC: roles, groups, job-function policies
Many new projects, no policy editsABAC: ResourceTag = PrincipalTag
Control tags set when creatingaws:RequestTag + aws:TagKeys
Only these tag keys are allowedForAllValues on aws:TagKeys (plus require the key)
Users retag things to get accessSCP deny on tag actions / iam:TagRole
Consistent tag values across the orgTag policies
Team may create roles only in its folderIAM path + permissions boundary condition
Allow every role under a path in a bucket policyPrincipal * + aws:PrincipalArn ArnLike
Limit which roles can be passediam:PassRole on role/path/* + PassedToService
Fine-grained permissions in a custom appAmazon Verified Permissions (Cedar)
A rule that overrides every permitCedar forbid
Authorize with a Cognito tokenIsAuthorizedWithToken
Day 5 in one sentence

RBAC asks what your job is, ABAC asks what colour your sticker is, so guard the stickers; and inside your own app, let Cedar be the bouncer.

09

Must-remember cheat sheet

1. RBAC = job. ABAC = tags.
2. ABAC core: aws:ResourceTag/k = ${aws:PrincipalTag/k}.
3. Create calls use aws:RequestTag; existing resources use aws:ResourceTag.
4. aws:TagKeys = list of keys in the request.
5. ForAllValues = true when no tags are sent.
6. Deny on protected keys: ForAnyValue.
7. Protect iam:TagRole / TagUser too.
8. Tag policies = consistency, not access.
9. EC2 tag-on-create: ec2:CreateAction.
10. Paths: CLI/API only; not a boundary.
11. Session ARN has no path; use aws:PrincipalArn.
12. No wildcards in Principal ARNs.
13. PassRole: limit by path, not ResourceTag.
14. Verified Permissions = authz for your app.
15. Cedar: forbid > permit > default deny.
16. Templates: only ?principal and ?resource.
17. Conditions: operators AND, keys AND, values OR; OR across keys = two statements.
18. Allow + NotAction = everything else, even future services. Prefer Deny + NotAction.
10

Core quiz

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

11

Hard quiz

Harder than most real exam questions on purpose. The last one is a matching question, like the new SCS-C03 format. Aim for 9 out of 12 before Day 6.

12

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.

13

Hands-on lab (30 minutes)

  1. Create two roles with the CLI: aws iam create-role --path /lab/ --role-name RedDev … tagged team=red, and BlueDev tagged team=blue.
  2. Attach one inline policy to both that allows secretsmanager:GetSecretValue when aws:ResourceTag/team equals ${aws:PrincipalTag/team}.
  3. Create two secrets tagged team=red and team=blue. Assume each role and confirm it reads only its own secret.
  4. Run aws sts get-caller-identity as RedDev and notice the session ARN has no /lab/ path.
  5. Open the Verified Permissions console, create a policy store from the PhotoFlash sample, and use the test bench to try a permit and a forbid.