AWS IAM, Under the Hood

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.

Analogy

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.

!
Advanced Gotcha

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"]
    
Fig. 1 — The evaluation order: SCPs and explicit denies are checked first and are absolute; an explicit allow must still be found for the request to succeed

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.

Stage

SCP Check

Organizational guardrail evaluated first; an SCP that doesn’t explicitly allow an action effectively denies it account-wide.

Stage

Explicit Deny Scan

Resource-based and identity-based policies are scanned for any explicit deny, which is absolute if found.

Stage

Boundary & Session Ceiling

Permission boundaries and session policies further cap what the identity policy’s allows can actually grant.

Stage

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 TypeAttached ToCan Grant Access?Primary Use
Identity-basedUser / Group / RoleYesPrimary permission-granting mechanism
Resource-basedResource (bucket, key, etc.)YesCross-account access without role assumption
Permission BoundaryUser / RoleNo — ceiling onlyDelegated administration guardrails
Service Control PolicyOrg / OU / AccountNo — ceiling onlyOrganization-wide governance guardrails
Session PolicyAssumed-role sessionNo — ceiling onlyFurther narrowing a specific temporary session
ADR-IAM-03Anti-Pattern
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.

i
Advanced Tip

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.

Global
IAM identities and policies replicate worldwide, no per-region deployment
Eventual
Brief propagation window after policy or role changes
Session TTL
Common root cause of “IAM” incidents that are actually credential refresh bugs

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
!
Common Trap

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.

1

Capture

CloudTrail records every IAM-related action and every API call made under any assumed identity, account-wide.

2

Correlate

Cross-reference an AccessDenied event against SCPs, permission boundaries, and identity/resource policies in evaluation order.

3

Analyze Usage

Access Analyzer and access-advisor data reveal which granted permissions are actually being exercised.

4

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.

Pattern

Roles Over Users for Workloads

Application and service access uses assumable roles with temporary credentials rather than long-lived IAM user access keys.

Pattern

SCPs as Organizational Guardrails

Broad, rarely-changed SCPs establish an account-wide ceiling (region restrictions, prohibited services) that individual account administrators cannot override.

Anti-Pattern

Wildcard Resource and Action Permissions

Policies using `”Action”: “*”` or `”Resource”: “*”` out of convenience grant far more access than almost any real workload genuinely needs.

Anti-Pattern

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.

Prefer roles with temporary credentials over long-lived IAM user access keys for both workloads and human access.
Check every applicable policy layer — SCP, resource-based, identity-based, boundary, session — when diagnosing an unexpected denial, not just the most obvious one.
Use Access Analyzer’s policy-generation feature to replace hand-written broad policies with usage-informed, tightly scoped ones.
Adopt ABAC deliberately for environments where resource count grows continuously and policy-per-resource doesn’t scale.
Avoid wildcard actions and resources except in genuinely narrow, deliberate, well-justified cases.
!
Most Common Mistake

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

Q1If an SCP allows an action, does that mean every principal in the account can perform it?
No — an SCP only sets the maximum possible permission ceiling for the account; the principal still needs an explicit allow from an identity-based or resource-based policy to actually perform the action, since SCPs never grant permissions on their own.
Q2What’s the practical difference between a permission boundary and an SCP?
A permission boundary applies to a specific IAM user or role and caps what that individual principal can do; an SCP applies at the AWS Organizations level to an entire account or organizational unit, capping every principal within that scope regardless of their individual permission boundaries.
Q3Why did access get denied even though the identity policy clearly allows the action?
An explicit deny anywhere else in the evaluation chain — a resource-based policy, a permission boundary, an SCP, or a session policy — overrides any allow from the identity policy, so the denial’s actual source is very likely in one of those other layers, not the identity policy itself.
Q4Is it safe to use IAM user access keys for automated scripts and CI/CD pipelines?
It’s technically possible but not recommended — assumable roles with short-lived, automatically rotated temporary credentials (via OIDC federation for CI/CD systems, for example) eliminate the long-lived-credential exposure risk that static access keys carry indefinitely.
Q5Can ABAC and traditional RBAC be used together in the same account?
Yes, and this is common in practice — many organizations use RBAC for a small set of stable, well-understood administrative roles while adopting ABAC specifically for the rapidly growing, resource-count-driven portion of their permission model, such as per-tenant or per-project access.

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.