AWS IAM, Under the Hood
An expert-level walkthrough of how the IAM policy evaluation engine actually decides "allow" or "deny" on every single API call — for engineers who already know what a policy document and a role are, and want to understand the precise evaluation order, the interaction between six different policy types, and where that complexity quietly produces permission bugs in production.
AWS IAM is deceptively simple at the surface — attach a policy, grant a permission, done. Underneath that surface sits a genuinely intricate, multi-policy-type evaluation engine that runs on every single API call made anywhere in an AWS account, reconciling identity-based policies, resource-based policies, permission boundaries, Service Control Policies, and session policies into one final allow-or-deny decision, following a strict and non-obvious precedence order. This guide skips the “how to attach a policy” introduction and goes straight into the advanced mechanics: the exact order the evaluation engine actually applies, why an explicit deny anywhere in that chain is absolute and unrecoverable, how permission boundaries and SCPs differ despite looking superficially similar, and where experienced teams still get bitten by IAM’s eventual-consistency model and increasingly complex organizational policy layering.
1Internal Working: The Policy Evaluation Engine
Every single AWS API call — regardless of which service it targets — passes through the same centralized policy evaluation logic, and understanding that evaluation logic’s precise rules is the single highest-leverage thing an advanced IAM practitioner can know.
At the core of IAM sits an evaluation engine that, for every request, gathers every policy potentially relevant to that request — identity-based policies attached to the calling principal, resource-based policies attached to the target resource, any permission boundary set on the principal, any Service Control Policies applying to the account, and any session policies passed during role assumption — and evaluates all of them together according to a fixed, deterministic precedence order rather than any single policy “winning” in isolation.
Think of the evaluation engine as a courtroom with several judges who must all agree before a verdict of “allowed” is reached — an Organizations SCP judge, a permission-boundary judge, an identity-policy judge, a resource-policy judge — and any single judge issuing an explicit “no” ends the proceeding immediately, regardless of how enthusiastically every other judge might have voted “yes.”
Two default states matter enormously here. First, everything is implicitly denied by default — a principal with no applicable allow statement anywhere is denied, not by an explicit rule but simply by the absence of a permit. Second, an explicit deny statement anywhere in any applicable policy overrides every other allow, no matter how many other policies grant the permission — this is the single most important rule in the entire IAM model, and it is the direct mechanism behind both permission boundaries and Service Control Policies functioning as effective “guardrails” that cannot be overridden by a more permissive identity policy layered on top.
Because an explicit deny is absolute, a well-intentioned but overly broad deny statement placed in one policy layer — even a resource-based policy on a single S3 bucket — can silently override permissions granted everywhere else, including administrator-level identity policies, producing an access denial that looks inexplicable until every applicable policy layer is checked, not just the most obvious one.
2Data Flow & the Request Evaluation Lifecycle
The evaluation engine applies its policy layers in a specific, memorizable order, and knowing that order precisely is what separates fast IAM troubleshooting from guesswork.
flowchart TD
A["API request made by a principal"] --> B{"Organizations SCP allows action?"}
B -- No / Explicit Deny --> Z["Request Denied"]
B -- Yes/Not Applicable --> C{"Resource-based policy explicit deny?"}
C -- Yes --> Z
C -- No --> D{"Identity-based policy explicit deny?"}
D -- Yes --> Z
D -- No --> E{"Permission Boundary allows action?"}
E -- No --> Z
E -- Yes/Not Applicable --> F{"Session Policy allows action?"}
F -- No --> Z
F -- Yes/Not Applicable --> G{"Any applicable policy grants explicit Allow?"}
G -- No --> Z
G -- Yes --> H["Request Allowed"]
Note the structural asymmetry: denials at any layer terminate evaluation immediately, but a “yes” or “not applicable” at any single layer does not grant access on its own — it simply means evaluation continues to the next layer, and ultimately at least one policy must contain an explicit allow statement for the specific action and resource in question, or the default implicit deny applies. This is why permission boundaries and SCPs are correctly described as setting a maximum possible permission ceiling rather than granting any permission themselves — they can only narrow what an identity policy’s explicit allows are permitted to grant, never expand it.
A frequently overlooked consistency detail: IAM is a globally replicated service, and changes — a new policy attachment, a new role, a permission update — can take a brief period to propagate across all AWS regions and services. Automated deployment pipelines that create an IAM role and immediately attempt to assume it in the very next step occasionally hit this propagation window, producing a transient “access denied” that resolves itself moments later with no configuration change required.
SCP Check
Organizational guardrail evaluated first; an SCP that doesn’t explicitly allow an action effectively denies it account-wide.
Explicit Deny Scan
Resource-based and identity-based policies are scanned for any explicit deny, which is absolute if found.
Boundary & Session Ceiling
Permission boundaries and session policies further cap what the identity policy’s allows can actually grant.
Explicit Allow Requirement
At least one applicable policy must explicitly allow the action, or the default implicit deny applies.
3Six Policy Types, One Engine: Choosing the Right Layer
IAM offers six distinct places to attach a policy, and each exists to answer a genuinely different governance question — conflating them is the root cause of most advanced permission-modeling confusion.
Identity-based policies, attached to users, groups, or roles, are the primary mechanism for granting permissions and the one most engineers reach for by default. Resource-based policies, attached directly to a resource (an S3 bucket policy, a KMS key policy), grant access from the resource’s own perspective, which is what makes cross-account access without role assumption possible for services that support it. Permission boundaries set a maximum permission ceiling on a specific principal, used heavily to let a delegated administrator create IAM roles without those roles ever being able to exceed a pre-approved permission scope. Service Control Policies, applied at the AWS Organizations level, set an organization- or account-wide maximum ceiling that no identity policy anywhere in that account or OU can exceed, regardless of how permissive it is. Session policies, passed at role-assumption time, further narrow (never expand) what a specific temporary session can do below what the role’s own identity policy already allows. Access Control Lists, a legacy mechanism still present on a small number of resource types like S3 objects, predate the modern policy language and are increasingly superseded by resource-based policies.
| Policy Type | Attached To | Can Grant Access? | Primary Use |
|---|---|---|---|
| Identity-based | User / Group / Role | Yes | Primary permission-granting mechanism |
| Resource-based | Resource (bucket, key, etc.) | Yes | Cross-account access without role assumption |
| Permission Boundary | User / Role | No — ceiling only | Delegated administration guardrails |
| Service Control Policy | Org / OU / Account | No — ceiling only | Organization-wide governance guardrails |
| Session Policy | Assumed-role session | No — ceiling only | Further narrowing a specific temporary session |
Anti-Pattern
Assuming a Service Control Policy grants any permission at all, and omitting the corresponding identity-based policy allow because “the SCP already allows it.”
Why It Fails
SCPs, like permission boundaries and session policies, can only restrict the maximum available permission — they never grant anything on their own. A principal with an SCP permitting an action but no identity-based policy explicitly allowing it is still denied by the default implicit-deny rule.
Better Approach
Treat SCPs purely as an organization-wide ceiling, and always ensure the actual granting permission exists in an identity-based (or resource-based) policy independently.
4Advanced Configuration: Trust Policies, Cross-Account Roles, and ABAC
Beyond basic permission policies, trust policies governing who may assume a role and attribute-based access control (ABAC) using tags represent the two configuration mechanisms most responsible for scaling IAM cleanly across a growing organization.
A role’s trust policy is a distinct, separate document from its permission policy — the trust policy defines who (which account, which service, which federated identity provider) is allowed to assume the role, while the permission policy defines what that role can do once assumed. Cross-account access architectures depend entirely on correctly scoping trust policies: a role in Account A with a trust policy naming Account B lets a principal in Account B assume that role and inherit exactly the permissions defined in Account A’s permission policy, all without ever creating a long-lived credential shared between the two accounts.
Attribute-based access control uses resource and principal tags directly within policy conditions, rather than hardcoding specific resource ARNs into every policy — a single well-designed ABAC policy can grant a principal access only to resources tagged with a matching project or environment attribute, scaling automatically as new resources are created with the correct tags, without ever requiring a policy update. This is a fundamentally different scaling model from traditional role-based access control (RBAC), where growth in resource count typically requires growth in policy complexity.
ABAC pays off most clearly in multi-tenant or rapidly growing environments where new resources are created continuously — the tagging discipline required to make it work (consistent, enforced tag keys and values) is itself a meaningful organizational investment, and ABAC should be adopted deliberately, not as a wholesale replacement for RBAC in environments with a small, stable, well-understood resource set.
5High Availability & Reliability
IAM is a global service with no regional deployment for the operator to manage, but its eventual-consistency propagation model and its role in the credential lifecycle for every other service make it a genuine dependency worth understanding for reliability planning.
IAM policies, roles, and users are replicated globally and are not scoped to a single AWS Region the way most other services are, meaning there is no separate multi-region IAM deployment decision to make. What does require reliability awareness is the eventual-consistency propagation window discussed in Chapter 2 — automated infrastructure-as-code pipelines that create a role and immediately depend on it should build in retry logic rather than assuming instantaneous global consistency, since a role created moments ago may not yet be assumable from every AWS service endpoint simultaneously.
Because IAM sits in the credential path for literally every other AWS API call, a temporary credentials expiration (for example, an assumed role’s session token nearing its expiry) that isn’t proactively refreshed by application code is a common source of “IAM outage” reports that are actually application-side credential lifecycle bugs rather than any actual IAM service degradation — a distinction advanced incident responders should check early during any widespread “everything is throwing access denied” investigation.
6Performance & Scalability: Policy Size, Managed vs. Inline
IAM’s own evaluation performance is effectively invisible to operators, but the service imposes real, hard limits on policy size and count that shape how permission architecture must scale as an organization grows.
Managed policies (both AWS-managed and customer-managed) have character-count limits per policy and a limit on how many managed policies can be attached to a single principal, while inline policies count against a separate, per-principal aggregate size quota. Organizations with rapidly growing, fine-grained permission requirements can hit these limits faster than expected if every new requirement is expressed as a brand-new, narrowly scoped managed policy rather than being consolidated or expressed through ABAC’s tag-based conditions, which scale without a corresponding growth in policy count.
Customer-managed policies, reusable across many principals, are strongly preferred over inline policies for anything beyond a genuinely one-off, principal-specific permission, since inline policies cannot be shared, versioned, or centrally audited the way managed policies can — a scalability and governance consideration as much as a technical one.
Production Example — Large-Scale Multi-Tenant SaaS
SaaS platforms provisioning a new IAM role per customer tenant consistently hit managed-policy-count and attachment limits far sooner than expected if each tenant’s permissions are expressed as unique per-tenant policies — the durable fix is almost always a shift toward ABAC, where a small, fixed number of policies reference tenant-identifying tags rather than growing linearly with tenant count.
7Security: Least Privilege as an Ongoing Discipline, Not a One-Time Setup
IAM provides genuinely powerful tooling for approaching least privilege, but none of it enforces least privilege automatically — it is a discipline that has to be actively practiced and continuously audited, not a property the service guarantees by default.
IAM Access Analyzer is the most consequential advanced security tool in the service: it can generate policies based on actual observed access activity (letting teams replace an overly broad hand-written policy with one precisely scoped to what a role has genuinely used), and it separately identifies resources shared with external entities that may represent unintended cross-account or public access. Credential reports and access-advisor data provide the complementary view — which permissions granted to a principal have actually never been used — making it possible to identify and remove genuinely unnecessary access rather than guessing at what “least privilege” should look like from policy documents alone.
Access Analyzer Use Cases
- Generate least-privilege policies from actual CloudTrail activity
- Detect unintended external or cross-account resource sharing
Credential & Access Reporting
- Identify unused permissions and stale credentials
- Surface access keys that have never been rotated
Treating a permission boundary as a substitute for actually scoping an identity policy tightly — a permission boundary only prevents a principal from exceeding a ceiling, it does nothing to narrow an identity policy that’s already far broader than the principal genuinely needs, so both layers require deliberate scoping, not just the boundary.
8Monitoring, Logging & Metrics
CloudTrail is the foundational observability layer for IAM — nearly every meaningful IAM investigation, from a permission denial to a security incident, begins by tracing the actual API calls and the identity behind them through CloudTrail.
Every IAM-related action (policy attachment, role creation, role assumption) and every action taken by an assumed identity is recorded in CloudTrail, including the specific principal, the source IP, and — critically for debugging complex multi-policy evaluation failures — enough context to reconstruct exactly which identity was making the call and under which assumed role or session. CloudTrail’s `AccessDenied` events, correlated against the policy layers from Chapter 2, are the standard starting point for diagnosing exactly which layer produced an unexpected denial.
Capture
CloudTrail records every IAM-related action and every API call made under any assumed identity, account-wide.
Correlate
Cross-reference an AccessDenied event against SCPs, permission boundaries, and identity/resource policies in evaluation order.
Analyze Usage
Access Analyzer and access-advisor data reveal which granted permissions are actually being exercised.
Tighten
Generate and apply narrower, usage-informed policies, then re-monitor for any newly surfaced legitimate denial.
9Design Patterns & Anti-Patterns
The durable IAM architectures favor roles over long-lived users, centralized organizational guardrails via SCPs, and tag-driven ABAC for anything that scales with resource count — while the recurring anti-patterns almost always involve long-lived credentials and overly broad wildcard permissions.
Roles Over Users for Workloads
Application and service access uses assumable roles with temporary credentials rather than long-lived IAM user access keys.
SCPs as Organizational Guardrails
Broad, rarely-changed SCPs establish an account-wide ceiling (region restrictions, prohibited services) that individual account administrators cannot override.
Wildcard Resource and Action Permissions
Policies using `”Action”: “*”` or `”Resource”: “*”` out of convenience grant far more access than almost any real workload genuinely needs.
Long-Lived Access Keys for Human Users
Static, long-lived access keys issued to individual humans bypass the temporary-credential and federation benefits IAM Identity Center or SSO-based access provides.
10Advantages, Disadvantages & Trade-offs
IAM’s trade-off is between genuinely comprehensive, fine-grained control and the real cognitive overhead of correctly reasoning about six interacting policy types across every single API call in an account.
Advantages
- Extremely fine-grained control over every action, resource, and condition across the entire AWS platform
- Multiple independent guardrail layers (SCPs, permission boundaries) enable safe delegated administration at scale
- Access Analyzer and access-advisor tooling make genuine least-privilege iteration achievable, not just theoretical
- No infrastructure to provision or manage — a fully managed, globally available identity layer
Disadvantages
- Six interacting policy types create genuine complexity in troubleshooting unexpected allow or deny outcomes
- Explicit denies being absolute means a single misplaced rule anywhere can silently override intended access broadly
- Policy size and attachment limits require architectural planning (ABAC, consolidation) at scale
- Least privilege requires ongoing active discipline — the service does not enforce it automatically
11Best Practices & Common Mistakes
Nearly every advanced IAM incident traces back to either an unnoticed explicit deny buried in an unexpected policy layer, a wildcard permission granted for convenience, or long-lived credentials that should have been temporary roles from the start.
Debugging an unexpected access denial by only inspecting the identity-based policy attached to the principal, missing an explicit deny sitting in a resource-based policy, permission boundary, or SCP layer entirely — the fix is almost always somewhere other than where the investigation started.
12Real-World & Industry Examples
Advanced IAM patterns consistently cluster around organizations managing many AWS accounts, delegating administration safely, or operating multi-tenant platforms where permission scope needs to scale with customer count rather than engineering effort.
Multi-Account Enterprise Governance
Large enterprises running dozens or hundreds of AWS accounts under AWS Organizations use SCPs as an unbreakable organizational guardrail (restricting regions, prohibiting specific high-risk services) while leaving day-to-day permission management within each account to that account’s own administrators.
Delegated Platform Administration
Platform engineering teams that let individual product teams create their own IAM roles rely on permission boundaries to guarantee those self-service roles can never exceed a pre-approved permission ceiling, enabling genuine self-service without a central team reviewing every single role creation.
Multi-Tenant SaaS Isolation
SaaS platforms isolating customer data at the AWS resource level use ABAC, tagging every resource with a tenant identifier and scoping access policies to match a principal’s own tenant tag, letting tenant isolation scale automatically as new customers and resources are provisioned.
13Frequently Asked Questions
14Summary and Key Takeaways
Key Takeaways
- Every API call passes through a fixed-order evaluation of up to six policy types — SCPs, resource-based, identity-based, permission boundaries, and session policies.
- An explicit deny anywhere is absolute — it overrides every allow from every other applicable policy, with no exceptions.
- SCPs, permission boundaries, and session policies never grant permissions — they only set a ceiling that an identity-based or resource-based policy’s allow must still fit within.
- Trust policies and permission policies are separate documents answering “who can assume this role” versus “what can this role do.”
- ABAC scales permission management with resource count, avoiding the policy-count growth that pure RBAC eventually hits at scale.
- Access Analyzer and CloudTrail-driven usage data make genuine least privilege achievable, replacing guesswork with actual observed access patterns.
- Most advanced IAM incidents are debugging problems, not configuration problems — the fix usually requires checking every policy layer, not just the most visible one.