📄 AWS SAP-C02 Study Report: Advanced IAM & Multi-Account Governance

Executive Summary

For the SAP-C02 (2026) exam, IAM is the foundational perimeter for multi-account governance. The exam rarely tests basic user/group creation. Instead, it aggressively tests your ability to evaluate intersecting policy layers, architect secure cross-account data sharing at scale, and implement dynamic authorization using modern Identity Center (SSO) features like ABAC.


1. Policy Evaluation Logic: The "Mental Algorithm"

When multiple policies apply to a single API request, AWS uses a strict evaluation logic. You must memorize this flow to successfully answer scenario questions involving assumed roles or federated users.

  • The Golden Rule: A request is always Denied by Default. It requires an explicit Allow at every applicable layer and an explicit Deny at none.
  • The "Venn Diagram" of Assumed Roles: When a principal assumes a role that has a Permissions Boundary and passes a Session Policy, the effective permissions are the strict intersection of:
    1. The Identity-based Policy
    2. The Permissions Boundary
    3. The Session Policy
  • Crucial Exam Concept: Permissions Boundaries and Session Policies never grant access on their own. They only restrict the maximum permissions granted by the identity policy.

2. Architecting Cross-Account Access (S3 Focus)

Cross-account data sharing is a massive domain on the SAP exam. You must choose the right architecture based on scale and identity context.

  • Small Scale (Bucket Policies): Use resource-based policies when an application in Account A needs access to Account B without losing its original AWS identity context.
  • Human / Multi-Service Access (sts:AssumeRole): Use cross-account roles when a principal needs to temporarily switch contexts to access multiple resources in the target account.
  • At-Scale / Data Mesh (S3 Access Points): Standard bucket policies have a 20 KB hard limit. When sharing a central data lake bucket with 50+ accounts, you will hit this limit. The correct architectural pattern is to create an S3 Access Point for each consuming account to bypass the limit and simplify routing.

🚨 Cross-Account Traps to Remember:

  1. The KMS Double-Lock: If the S3 bucket is encrypted with a Customer Managed Key (KMS CMK), you must allow cross-account access in both the S3 Bucket Policy and the KMS Key Policy.
  2. Object Ownership: Always look for S3 Object Ownership set to BucketOwnerEnforced in cross-account upload scenarios to ensure the central account actually owns and can read the data uploaded by member accounts.

3. The Blast Radius of Service Control Policies (SCPs)

SCPs act as the ultimate guardrails in an AWS Organization.

  • What they do: SCPs filter the maximum available permissions for all IAM principals in a member account (including the Root user).
  • The Exam Trap: SCPs do not just protect resources inside your Organization. They restrict the outbound API calls of the identities they are attached to. If an SCP denies s3:GetObject without MFA, that identity is blocked from reading any bucket in the world, public or private, internal or external.

4. Attribute-Based Access Control (ABAC) at Scale

The SAP-C02 exam heavily favors modern, scalable governance over legacy Role-Based Access Control (RBAC).

  • The Problem: Creating a unique IAM role for every department or project (RBAC) leads to role proliferation and management overhead.
  • The Solution (ABAC): Use AWS IAM Identity Center to map corporate Identity Provider (IdP) attributes (like Department or CostCenter) to Principal Tags.
  • Dynamic Policies: Create a single, scalable Permission Set using dynamic variables.
    • Example: ${aws:PrincipalTag/Department} == aws:ResourceTag/Project
    • This eliminates hardcoding. When a new department is added to your IdP, zero changes are required in AWS IAM.
  • Tagging Contexts:
    • aws:ResourceTag/Key: Evaluates tags already attached to an existing AWS resource.
    • aws:RequestTag/Key: Evaluates tags being passed in the API request (for example, enforcing that a user applies a specific tag when launching a new EC2 instance).

5. SAP-C02 IAM Practice Question Bank & Explanations

Question 1: The Three-Layer Policy Intersection

Scenario: A developer in Account A assumes an IAM role within the same account to perform S3 operations.

  • The IAM role has an identity-based policy granting s3:* on all resources.
  • A permissions boundary is attached to this role, explicitly allowing only s3:PutObject and s3:GetObject.
  • During the AssumeRole API call, the developer passes a session policy that explicitly allows s3:PutObject and s3:DeleteObject.

If the developer subsequently attempts to perform an s3:DeleteObject operation using the assumed role credentials, what will be the outcome and why?

Options:

  • A) (Correct) Denied, because the permissions boundary establishes the maximum permitted permissions and it does not explicitly allow s3:DeleteObject.
  • B) Allowed, because the identity-based policy grants full s3:* access, which overrides both the session policy and the boundary.
  • C) Allowed, because the session policy passed during AssumeRole explicitly allows s3:DeleteObject, taking precedence over the boundary.
  • D) Denied, because passing a session policy that conflicts with a permissions boundary automatically results in an explicit deny for all actions.

Architectural Explanation: When assuming an IAM role that has both a Permissions Boundary and a Session Policy attached, AWS evaluates the effective permissions as a strict intersection of three distinct layers: the Identity-based Policy, the Permissions Boundary, and the Session Policy.

For an action to be allowed, it must be explicitly allowed in all overlapping layers. Because the Permissions Boundary only allowed PutObject and GetObject, it set a hard ceiling. Even though the Identity Policy and the Session Policy both said "yes" to DeleteObject, the boundary said "no" (resulting in an implicit deny). Neither a Permissions Boundary nor a Session Policy can ever grant access on their own; they only limit the existing identity policy.


Question 2: The Cross-Account SCP Filter

Scenario: Account A contains a central S3 bucket. The bucket policy explicitly grants s3:GetObject to arn:aws:iam::AccountB:root.

An IAM User in Account B has an administrator identity-based policy granting * actions on * resources.

Account B is part of an AWS Organization, and an SCP is attached to Account B's OU that explicitly denies s3:GetObject if the request does not include MFA (aws:MultiFactorAuthPresent == "false").

The IAM User in Account B makes an API call to download an object from Account A's bucket without using MFA. What is the authorization outcome?

Options:

  • A) The request is allowed because SCPs only govern access to resources that reside within the same AWS Organization as the principal making the request.
  • B) The request is denied because the bucket policy in Account A delegates access to the root user of Account B, which cannot be assumed by an IAM User.
  • C) (Correct) The request is denied because the SCP applied to Account B restricts the IAM User from making the API call, even though the resource resides in an external account.
  • D) The request is allowed because the resource-based policy in Account A does not contain a condition enforcing MFA, and resource policies dictate external access.

Architectural Explanation: A massive exam trap is the belief that SCPs only protect resources inside your AWS Organization. SCPs act as a filter on the IAM Principal making the call, not just the resource being accessed. If an SCP says "Account B cannot call s3:GetObject without MFA," it means Account B's identities cannot call that API anywhere in the world, not in their own account, not in Account A, and not in a public bucket.

Furthermore, granting access to arn:aws:iam::AccountB:root in a bucket policy is AWS shorthand for delegating trust to Account B as a whole; it does not mean the literal root user email/password must be used. Account B's administrators can then decide which of their IAM users get to use this permission.


Question 3: Attribute-Based Access Control (ABAC) at Scale

Scenario: An enterprise uses AWS IAM Identity Center integrated with an external corporate Identity Provider (IdP) like Okta or Entra ID. The company mandates Attribute-Based Access Control (ABAC) to manage AWS resources at scale.

A Solutions Architect must ensure that developers can only invoke ec2:StartInstances and ec2:StopInstances on EC2 instances that possess a Project tag matching the developer's Department attribute in the corporate IdP.

What is the MOST scalable way to architect this?

Options:

  • A) Create an IAM Role for each department in the target AWS accounts. Create an AWS Lambda function to sync the IdP's Department attribute to the AWS accounts and automatically assume the respective IAM Role based on the user's group.
  • B) (Correct) Configure "Access control attributes" in IAM Identity Center to map the IdP's Department attribute to a PrincipalTag. Create a single Permission Set with a policy allowing the actions where the StringEquals condition matches aws:ResourceTag/Project to ${aws:PrincipalTag/Department}.
  • C) Apply a Permissions Boundary to the developers' IAM Identity Center roles that restricts ec2:StartInstances based on the aws:PrincipalTag/Department. Use SCPs to enforce the Project tag on all EC2 instances.
  • D) In the IAM Identity Center Permission Set, create an inline policy that explicitly lists all department names in a Condition block under aws:RequestTag/Project and attach it to all user groups.

Architectural Explanation: Option B is the textbook definition of Attribute-Based Access Control (ABAC) in AWS.

Instead of creating dozens of different IAM roles for different departments (Role-Based Access Control, or RBAC), ABAC allows you to create one dynamic Permission Set. By configuring Access Control Attributes in IAM Identity Center, you map the external IdP attribute (Department) to an IAM Principal Tag. AWS injects that attribute into the user's session dynamically.

The policy condition ${aws:PrincipalTag/Department} acts as a dynamic variable. If a user from "Finance" logs in, AWS evaluates the policy as if it says StringEquals: {"aws:ResourceTag/Project": "Finance"}. This eliminates the need to hardcode static lists of departments in your JSON policies (as seen in Option D) or build custom Lambda sync functions (Option A). Remember: aws:ResourceTag checks the tag on an existing resource, while aws:RequestTag checks the tag passed during an API request (like resource creation).