AWS 266: Evaluate SCP and cross-account policy behavior without restructuring the personal account
Why this lab matters
An authorization failure rarely means only “IAM denied it.” A request can cross two accounts and meet an identity policy, permissions boundary, session policy, service control policy (SCP), resource control policy (RCP), resource policy, role trust policy, KMS key policy, VPC endpoint policy, and service-specific rule. Each layer has a different owner and purpose. A broad identity-policy allow cannot overrule an explicit deny, a boundary does not grant permission, and a successful AssumeRole does not prove that the resulting session can use the target resource.
This lab teaches a repeatable way to evaluate that chain without creating an AWS Organization, moving an account, attaching an SCP, changing a role, or touching a real bucket or key. All identifiers are fictional. You first reason from supplied policies, then use validation or simulation only where the tool supports the relevant layer. That distinction matters: policy validation finds policy defects; simulation predicts an authorization decision; only an approved live positive and negative test proves service behavior.
What you will be able to do
By the end, you can:
- convert a request into principal, action, resource, account, Region, and condition context;
- distinguish role trust from the permissions used after role assumption;
- calculate intersections among identity policies, boundaries, session policies, SCPs, and RCPs;
- evaluate the two sides of cross-account access: trusted caller account and trusting resource account;
- explain same-account differences among an IAM role ARN and an assumed-role session ARN in a resource policy;
- include S3, KMS, VPC endpoint, dependent-action, ownership, and Block Public Access controls;
- distinguish implicit deny from explicit deny and identify the deciding statement;
- use IAM Access Analyzer policy validation and IAM simulation without overstating their evidence;
- propose the smallest safe correction and a negative regression test; and
- produce an auditable 20-request decision matrix with no organization changes.
Safety boundary and starting checks
This is a zero-create lab. Do not create an Organization, enable all features, invite or move accounts, attach policies to a root/OU/account, change a trust policy, create access keys, or test against an unowned resource. The supplied account IDs, ARNs, bucket names, keys, and sessions do not exist.
If an instructor authorizes AWS CLI use, only local policy documents are submitted to read-only validation or simulation APIs. Begin by checking the caller privately:
export AWS_DEFAULT_REGION="ap-south-1"
aws sts get-caller-identity --query Arn --output text
aws configure list
Redact account IDs and session names before sharing output. IAM and Organizations are global control planes, but request context can still contain aws:RequestedRegion. Never assume the shell's default Region is the same as the Region in a simulated request.
Authorization vocabulary
| Term | Plain-language meaning | What it does not mean |
|---|---|---|
| Identity policy | Grants or denies actions to an IAM user, group, or role | It does not make a role trust a caller |
| Role trust policy | Resource-based policy on a role that controls who may call STS to assume it | It does not grant S3, KMS, or application permissions after assumption |
| Permissions boundary | Maximum that identity policies may grant to a user or role | It is not a grant by itself |
| Session policy | Further restricts a temporary session | It cannot add beyond the role/user permissions used to create the session |
| SCP | Organization guardrail limiting principals in affected member accounts | It grants nothing and does not restrict the management account or service-linked roles |
| RCP | Organization guardrail limiting supported resources in affected member accounts | It is not currently evaluated by IAM Policy Simulator |
| Resource policy | Policy attached to a bucket, queue, key, role, or other supported resource | Cross-account access usually still needs the caller side to allow the request |
| Explicit deny | Matching Deny in any applicable policy layer | No matching allow can override it |
| Implicit deny | No complete authorization path supplies the required allow | It may be fixed by adding the missing narrow allow |
The fictional organization and request path
The lab models this hierarchy:
Northwind organization root r-example
├── Platform OU (ou-example-platform)
│ └── Tooling account 111122223333
└── Workloads OU (ou-example-workloads)
├── Sandbox account 777788889999
└── Production OU (ou-example-production)
└── Payments account 444455556666
The root retains FullAWSAccess. A Workloads guardrail denies leaving the organization. The Production OU denies selected actions outside ap-south-1 and protects audit resources. Remember that an account inherits applicable SCPs from every node in its path. With an allow-list design, an action needs an allow at every level; with deny-list guardrails plus FullAWSAccess, a matching explicit deny blocks the action. SCPs do not create permissions.
A representative cross-account path is:
Tooling identity
| caller may call sts:AssumeRole
v
Payments DeployRole trust policy accepts caller and conditions
| STS issues assumed-role session
v
role identity policy ∩ boundary ∩ session policy ∩ SCP
| plus request context
v
S3 bucket policy/RCP/Block Public Access/ownership/endpoint policy
| if SSE-KMS is used
v
KMS key policy + IAM permission + grant/condition evaluation
Treat role assumption and resource use as separate requests. A caller can pass the trust-policy test yet be denied later by the role's permissions, SCP, bucket policy, endpoint policy, or key policy.
A deterministic evaluation algorithm
For each request, do not start by reading random policies. Use this order.
- Normalize the request. Record the exact principal ARN and principal type, action, resource ARN, resource owner, caller account, Region, source IP or VPC endpoint, tags, organization path, MFA state, source identity, external ID, and whether temporary credentials are used.
- Check explicit denies first. Search every applicable identity, boundary, session, SCP, RCP, resource, endpoint, and key policy. A matching explicit deny ends the decision.
- Evaluate the caller side. For cross-account access, the trusted account must authorize its principal. Identity permission may be narrowed by a boundary, session policy, and SCP.
- Evaluate role assumption separately. The caller needs permission for
sts:AssumeRole, and the target role trust policy must accept that principal and all conditions. Organization, MFA, external ID, source identity, and session-tag conditions are evaluated from the request context. - Evaluate the resource side. The trusting account's resource policy must authorize cross-account access. Applicable RCPs and service controls must also permit it.
- Apply principal-type semantics. Within one account, a resource policy granting to a role ARN remains limited by implicit denies in a boundary or session policy. A direct grant to an exact assumed-role session ARN is not limited by those implicit denies, although any explicit deny still wins. Do not generalize this same-account exception to ordinary cross-account access.
- Check service-specific authorization. S3 object and bucket actions use different ARNs. SSE-KMS can require both S3 and KMS permission. Some actions have dependent actions. Object ownership, S3 Block Public Access, encryption context, KMS grants, and VPC endpoint policies can change the service result.
- Classify the result. State
allowed,implicit deny, orexplicit deny; name the deciding statement and owner. - Design verification. Give one authorized positive test and one unauthorized negative test. State whether each can be simulated or needs an approved sandbox.
How policy types combine
| Request shape | Minimum authorization model | Frequent mistake |
|---|---|---|
| Same-account identity request | Identity/resource allows, constrained by applicable limits and explicit denies | Believing a boundary grants access |
| Cross-account resource access | Caller side allows and resource side allows | Checking only the bucket policy |
| Cross-account role assumption | Caller policy allows STS and role trust accepts caller/conditions | Treating trust as post-assumption permission |
| Role-session resource use | Role identity ∩ boundary ∩ session ∩ SCP, plus resource-side controls | Forgetting the session policy or SCP |
| S3 object encrypted with customer-managed KMS key | S3 authorization plus KMS authorization and usable key state | Adding s3:GetObject but omitting kms:Decrypt |
| Request through VPC endpoint | Normal authorization plus endpoint policy | Assuming endpoint policy is an IAM grant |
Lab files and workflow
Download the AWS266 policy-evaluation workbook bundle. It contains:
SUPPLIED_POLICY_CASES.md: organization model and 20 requests;POLICY_EVALUATION_WORKBOOK.md: one row per policy layer and decision;ANSWER_DIRECTIONS.md: expected decisions and reasoning;SIMULATOR_LIMITS_AND_COMMANDS.md: safe commands and evidence limits;DELEGATED_ROLE_GUARDRAIL.md: architecture extension;- eight JSON policies used by the cases; and
MANIFEST.md: file purpose and integrity workflow.
Work in four passes:
- Complete all 20 predictions without opening the answers.
- Mark the exact matching statement, missing allow, or context mismatch.
- Validate the supplied JSON and simulate only supported portions.
- Compare results, explain discrepancies, and design positive/negative live tests for an approved sandbox. Do not run those live tests in a personal or production account.
Validation is not simulation
IAM Access Analyzer validate-policy checks a document against policy grammar and AWS best practices. It supports identity, resource, SCP, and RCP policy types. A clean validation result does not say that a request will be allowed.
From the extracted lab directory:
aws accessanalyzer validate-policy \
--policy-document file://scp-region.json \
--policy-type SERVICE_CONTROL_POLICY \
--output table
aws accessanalyzer validate-policy \
--policy-document file://identity-deploy.json \
--policy-type IDENTITY_POLICY \
--output table
aws accessanalyzer validate-policy \
--policy-document file://s3-bucket-policy.json \
--policy-type RESOURCE_POLICY \
--validate-policy-resource-type AWS::S3::Bucket \
--output table
Findings have severity such as error, security warning, suggestion, or warning. Record the finding code, location, and proposed remediation. Do not silently “fix” a supplied policy before evaluating its intended case.
Simulation is not execution
simulate-custom-policy evaluates policy documents you supply as identity policies. It can accept a boundary, actions, resources, and context entries, but it does not reconstruct every service control. Example:
aws iam simulate-custom-policy \
--policy-input-list file://identity-deploy.json \
--permissions-boundary-policy-input-list file://boundary-workload.json \
--action-names s3:GetObject s3:PutObject \
--resource-arns arn:aws:s3:::northwind-artifacts/releases/v7/app.zip \
--context-entries ContextKeyName=aws:RequestedRegion,ContextKeyType=string,ContextKeyValues=ap-south-1 \
--query 'EvaluationResults[].{Action:EvalActionName,Decision:EvalDecision,Missing:MissingContextValues}' \
--output table
simulate-principal-policy can evaluate an IAM user or role in the current account if the caller is authorized to inspect it. That is not required for this lab. Never point it at an unowned production principal merely because you have read access.
The simulator sends no request to S3, KMS, STS, or another target service. It does not prove that a resource exists, a key is enabled, a bucket has the expected ownership mode, a trust chain can actually be assumed, or a network path works. It does not support RCP evaluation, and advanced combinations such as endpoint policies, role chaining, or multiple resource policies can differ from live behavior. Supply required context values deliberately; a missing value may produce a deny that is different from a value that fails a condition.
The 20-request exercise
The supplied catalog deliberately changes one variable at a time. It covers:
| Cases | Focus |
|---|---|
| 1–4 | Identity, boundary, session, Region SCP intersection |
| 5–7 | Trust policy, external ID, MFA, and source identity |
| 8–11 | Cross-account identity/resource dual authorization and explicit deny |
| 12–13 | Role ARN versus assumed-role session ARN behavior |
| 14–16 | S3 bucket/object ARNs, Block Public Access, object ownership, and dependent KMS permission |
| 17–18 | KMS key policy, encryption context, key state, and grants |
| 19 | VPC endpoint policy as an additional limiter |
| 20 | RCP and unsupported-simulation evidence handling |
For every case, submit the normalized request, applicable policy path, decision, deciding statement, control owner, smallest safe correction, positive test, negative test, and tool limitation. “Denied by IAM” earns no credit because it does not identify the control or remediation owner.
Common traps and diagnosis
| Symptom | Likely cause | Evidence to inspect | Safe correction direction |
|---|---|---|---|
AssumeRole denied | Caller lacks STS allow or trust condition fails | Caller policy, trust principal, MFA/external ID/source identity | Change only the missing side or context requirement |
| Assume succeeds but S3 fails | Role/session/SCP/bucket/endpoint/KMS layer denies | Session ARN, action/resource, all policy layers, CloudTrail | Isolate S3 from KMS and test one action at a time |
| Simulator allows, live request denies | Unsupported endpoint/RCP/service rule, wrong context, key state, or another resource policy | Simulator inputs, live request context, service/CloudTrail event | Treat simulation as hypothesis, not proof |
GetObject denied although bucket ARN is allowed | Object action needs bucket/* resource | Statement resource and action documentation | Add only the required object ARN pattern |
| SSE-KMS object read fails | Missing KMS allow, key policy path, context mismatch, or disabled key | KMS key policy/grants/state and encryption context | Repair the narrow KMS path; do not grant kms:* |
| Boundary appears to allow but request fails | Boundary is only a ceiling | Identity policy and boundary intersection | Add approved identity allow, retain boundary |
| Resource policy names role session and bypasses implicit limits | Direct same-account session grant semantics | Exact Principal, principal type, explicit denies | Prefer stable role principal and conditions where supported |
Evidence standard
Strong evidence is a chain, not a green screenshot. Submit:
- completed 20-row decision register with UTC review time;
- policy hashes or an unchanged bundle manifest;
- validation findings for each supplied JSON policy type;
- simulator command, inputs, output, and missing-context list for supported cases;
- explicit
not simulatedlabels for RCP, endpoint, KMS/service-state, and other unsupported claims; - one least-privilege correction with before/after expected decisions;
- one positive and one negative test design per case; and
- a reviewer sign-off that no Organization or resource mutation occurred.
Redact real account IDs, ARNs, usernames, source identity, external IDs, and session names. Do not submit credentials, signed requests, object contents, or presigned URLs.
Cost and cleanup
Local document review costs nothing. IAM Access Analyzer policy validation and IAM policy simulation do not create the fictional organization or resources, but always check current pricing and account policy before calling an AWS API. This lesson does not authorize paid analyzers, access previews, CloudTrail data-event trails, S3, KMS keys, VPC endpoints, or Organizations changes.
Cleanup is evidence-based: show that no resources were created and no policies were attached or changed. Remove only local extracted copies if required by your workstation policy; retain the completed workbook according to the course evidence schedule. Never delete an AWS resource as “cleanup” unless the lab created it, ownership is proven, and deletion was explicitly approved.
Architecture extension: delegated role creation
Complete DELEGATED_ROLE_GUARDRAIL.md. Application teams may create roles only under /workloads/, must attach a centrally owned boundary, may pass roles only to approved services, and cannot alter the boundary or create arbitrary external trust. Evaluate escape paths through iam:PassRole, trust updates, session tags, policy version changes, resource policies, and KMS administration. The design must include CloudTrail/Config detection, emergency revocation, and both positive and negative tests.
This exercise connects individual policy calculation to an architect's job: permission delegation is safe only when the person who can create the role cannot also widen every constraint on that role.
Knowledge check
- Why does a permissions boundary allow statement not grant an action?
Because the boundary is a maximum; an identity or applicable resource policy must supply the permission.
- What must be true for ordinary cross-account resource access?
The trusted caller side and trusting resource side must both authorize the request, with no applicable explicit deny.
- Does a role trust policy grant the assumed session permission to read S3?
No. It governs role assumption; post-assumption permissions are evaluated separately.
- Why can a simulator result differ from a live result?
It sends no service request, depends on supplied context, does not evaluate every control such as RCPs, and cannot prove resource state or network/service-specific behavior.
- What is stronger than one successful positive test?
A matching authorized positive and unauthorized negative test correlated with the exact principal, context, policy versions, and audit event.
Lesson acceptance
The lab is complete only when the learner:
- decides all 20 requests and distinguishes implicit from explicit deny;
- identifies the exact policy layer and control owner for every result;
- explains trust policy versus post-assumption permissions;
- correctly applies boundary, session, SCP, RCP, resource, endpoint, S3, and KMS concepts;
- runs or interprets validation/simulation only within documented support;
- proposes least-privilege corrections and regression tests;
- completes the delegated-role guardrail extension; and
- proves that no Organization, account, role, bucket, endpoint, or key was changed.
Official sources
- IAM policy evaluation logic
- Cross-account policy evaluation logic
- Permissions boundaries for IAM entities
- Testing policies with the IAM Policy Simulator
- IAM Access Analyzer policy validation
- Cross-account access to AWS KMS keys
- Amazon S3 policy actions and resource mapping
- AWS Organizations service control policies
- AWS Organizations resource control policies