Robots Atlas>ROBOTS ATLAS
Security

Policies and permissions in AWS IAM — how do they work?

Sir Robot29 July 2026 · 10 min read
polityki-i-uprawnienia-w-aws-iam-cover

IAM policies are documents (usually written in JSON) that decide who can do what in the AWS cloud. They form an authorization layer — not an AI service or a database — and understanding them is the foundation of security for any AWS environment.

What is an IAM policy?

Put simply, an IAM policy is a document (usually written in JSON) that describes who can perform which operations, on which resources, and under what conditions. It is a set of rules that define permissions — nothing more, nothing less.

AWS Identity and Access Management (IAM) is the service that controls access to resources in Amazon's cloud. Its central building block is exactly this — the policy. According to the AWS documentation, a policy is an object that, when associated with an identity (a user, group, or role) or a resource, defines their permissions. When a principal sends a request to AWS, the service evaluates the applicable policies and decides whether to allow or deny the operation.

It helps to state upfront what IAM is not. It is not a network firewall or an encryption mechanism. It is an authorization layer ("who is allowed to perform this action on this resource") that runs after authentication ("who is this in the first place"). Policies themselves store no data — they describe rules. Most of them are JSON documents with a strictly defined structure.

Here is what the simplest policy looks like in practice:

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::photos/*"
    }
  ]
}

This policy allows reading all objects in the photos bucket — and nothing else. A key property: a permission applies regardless of how the operation is performed. If a policy allows the GetUser action, the user can invoke it through the AWS Console, the CLI, or the API — the rule is identical.

Who is behind it?

IAM is an Amazon Web Services offering, provided at no additional charge as part of the AWS platform. The permission model is built on a policy language whose current version is labeled 2012-10-17 — this is the language version, not the launch date of the service, and AWS still recommends using it today. The entire mechanism described here comes directly from the official AWS IAM documentation.

How does it work?

The logic of evaluating a request looks simple but has several layers. It runs in three steps: authenticating the principal, gathering the request context (which action, which resource, which conditions), and the actual policy evaluation.

After authentication, AWS holds four elements describing the request — and only then does it run policy evaluation:

Principal
Action
Resource
Context
Policy evaluation
  • Principal — the authenticated identity behind the request: an IAM user, role, account or AWS service.
  • Action — the specific API operation the principal is asking for, e.g. s3:GetObject or ec2:StartInstances.
  • Resource — the resource (or resources) the action targets, identified by an ARN.
  • Context — extra request attributes checked via condition keys: IP address, time, MFA presence, region or transport encryption.

The foundation is the default deny rule: if no policy explicitly allows an action, the request is rejected. For an operation to succeed, there must be an explicit allow. Above everything sits the explicit deny — if any applicable policy contains a Deny, the request is blocked no matter how many allows exist. This is a hierarchy without exceptions: deny always beats allow.

Once several policy types are involved, the logic grows more nuanced. Identity-based policies: Policies attached to an IAM identity — a user, group or role. They grant it permissions. and Resource-based policies: Policies attached directly to a resource (e.g. an S3 bucket). They define who may perform which actions on it. policies combine as a union — an action is allowed if either one permits it. By contrast, permissions boundaries, SCP: Service Control Policy — an AWS Organizations policy that sets the maximum permissions for accounts in the organization and RCP: Resource Control Policy — the counterpart of an SCP, applied at the resource level rather than the identity level policies combine as an intersection: the action must be allowed by all applicable layers at once. This is spelled out on the policy evaluation logic page.

An example makes it concrete. When every layer allows:

User
Identity policyAllow
Bucket policyAllow
SCPAllow
Permission boundaryAllow
Result: ALLOWAllow

But it takes only one of the intersection layers to deny:

Identity policyAllow
Bucket policyAllow
SCPDeny
Result: DENYDeny

A single Deny in the SCP cancels every allow — that is what "intersection" means in practice.

A typical authorization flow

In practice, the whole path — from the application to the decision — looks like this:

Application uses an IAM role
Obtains temporary STS credentials
Sends a request to S3
S3 runs IAM policy evaluation for this request
The authorization mechanism checks all applicable policies
S3 returns the result: data or an AccessDenied error
IAM is not a separate server that S3 sends a network call to. Policy evaluation is an internal AWS authorization mechanism built into the service itself — S3 evaluates the request against IAM rules on its own. This is why the same permission model applies consistently across all of AWS.

What are its key components?

A policy JSON document is built from statements. Each element of the Statement array describes one independent permission rule. A single statement contains a set of fields:

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::photos/*",
      "Condition": {
        "Bool": { "aws:MultiFactorAuthPresent": "true" }
      }
    }
  ]
}
  • Version — the policy language version (recommended: 2012-10-17).
  • EffectAllow or Deny.
  • Action — a list of actions. Each AWS service defines its own set of actions, e.g. s3:GetObject, ec2:StartInstances, or dynamodb:PutItem.
  • Resource — the resources the actions apply to, identified by an ARN: Amazon Resource Name — a unique identifier for a resource in AWS. Not all actions let you point to a specific resource — some require "*". This does not automatically mean access to everything: for many actions AWS simply does not allow a more granular resource to be specified.
  • Principal — states who is granted permissions: an AWS account, an IAM user, an IAM role, or an AWS service. Required in resource-based policies, and omitted in identity-based ones, where the principal is implied.
  • Condition — optional constraints based on so-called condition keys, e.g. aws:SourceIp, aws:CurrentTime, aws:PrincipalArn, aws:RequestedRegion, or aws:SecureTransport. They let you narrow access — for example to a specific IP address or to encrypted connections only.

A rule in practice: a statement with the condition "Bool": {"aws:MultiFactorAuthPresent": "true"} takes effect only when the user has signed in with a second authentication factor. If the condition is not met, the entire statement is skipped.

When a policy holds multiple statements — or when multiple policies are attached to a principal — AWS evaluates all applicable statements. A single matching Allow is enough for the action to be permitted — unless a matching Deny appears first, which always takes precedence. AWS recommends splitting permissions into separate functional policies (for example, S3 management apart from user management) rather than one overloaded document.

How to read an IAM policy

You can read a simple policy by asking five questions in order:

FieldQuestionExample
EffectAllow or deny?Allow / Deny
ActionWhat are we doing?s3:GetObject
ResourceOn what?resource ARN
PrincipalWho?resource-based policies only
ConditionWhen / under what condition?e.g. MFA, IP address

What can it be used for?

IAM policies govern essentially every access in AWS. In practice you meet a few main types.

Identity-based policies

They attach to users, groups, and roles. They come in two kinds:

  • Managed — reusable; they include AWS-provided policies and your own customer-managed ones.
  • Inline — bound to a single identity and deleted along with it.

Resource-based policies

These attach directly to a resource — the most common examples are Amazon S3 bucket policies and IAM role trust policies. The latter does not define what a role may do, but who may assume it (the sts:AssumeRole action) — one of the most commonly confused distinctions in IAM. Resource-based policies also enable cross-account access: the Principal element can name an account or entity in another AWS account.

The restricting layers

These are higher-level controls that grant nothing and only narrow permissions:

  • Permissions boundaries — set the ceiling that an identity-based policy can grant to an entity.
  • SCPs and RCPs — guardrails in AWS Organizations, applied across an entire organization or organizational unit (OU: Organizational Unit — a group of accounts in AWS Organizations that can share common policies (e.g. SCPs).):
    • SCP (Service Control Policy) — the maximum permissions for identities (users and roles) in the organization's accounts.
    • RCP (Resource Control Policy) — an equivalent ceiling, but applied to resources.
  • Session policies — passed when temporarily assuming a role through STS: AWS Security Token Service — the service that issues temporary access credentials — they can only narrow the permissions the role grants, never widen them.

How does it differ from other approaches?

The most common misunderstanding concerns the difference between policies that grant permissions and those that restrict them. Only identity-based and resource-based policies actually grant access. Permissions boundaries, SCPs, and RCPs never grant anything — they only narrow what other policies have granted. This distinction is the source of many misconfigurations: an admin adds an SCP and is puzzled that a user can do nothing, because a granting policy is missing.

The second axis is how policies combine — union versus intersection. The table sums it up:

Policy typeGrants or restricts?How it combines
Identity-basedgrantsunion
Resource-basedgrantsunion
Permissions boundaryrestrictsintersection
SCP / RCPrestrictsintersection
Sessionrestrictsintersection

In the intersection model, every added layer can only take permissions away, never add them.

It's also worth separating policies from access control lists (ACLs) — the only policy type in AWS that does not use JSON syntax, used for cross-account access (for example, in older S3 configurations). For most new deployments, AWS recommends resource-based policies over ACLs.

Key limitations and challenges

Policies have physical size limits, so complex permissions often must be split across several documents. The bigger challenge, though, is the model's own complexity: with several overlapping layers (identity-based + resource-based + boundary + SCP + session), working out the actual, effective permissions can be far from trivial. It's easy to fall into either of two opposite errors — over-permissioning (a security risk) or over-restricting (breaking functionality).

AWS provides tools to ease this. IAM Access Analyzer offers more than 100 policy validation checks and can generate a proposed least-privilege policy based on real activity recorded in CloudTrail: an AWS service that records the history of API calls made in an account. "Last accessed" information helps remove unused permissions. Even so, responsibility for correct configuration rests with the team — it is part of the shared responsibility model.

The root account is a special case: by default it has full access to the account and IAM policies do not restrict it — you cannot attach an identity-based policy to it or set a permissions boundary for it. It is, however, still subject to SCPs and RCPs within an organization. That's why AWS recommends using root access only in exceptional cases.

Why does it matter?

IAM is, in practice, the single most important security layer in AWS — and at the same time the most frequent source of incidents. Most public cloud data leaks stem not from "hacking" in the classic sense, but from access granted too broadly: an open bucket, a role with * permissions, an access key that should never have existed. Grasping that deny always beats allow, that an SCP grants nothing, and that effective access is the intersection of many layers translates directly into whether an environment is secure.

For a junior stepping into the cloud, this is foundational knowledge. AWS consistently promotes the principle of least privilege and a preference for temporary credentials (roles) over long-term access keys — both recommendations follow directly from the IAM security best practices guide. Mastering the policy language is therefore not a technical detail but a precondition for using the cloud responsibly.

IAM policies are not a standalone product but the shared permission language of all of AWS. A handful of concepts — allow, deny, principal, action, resource, condition — and a few evaluation rules are enough to describe access to almost any service. It is a rare case where a small, coherent model drives an enormous, complex system.

Sources

  • AWS — IAM User Guide: Policies and permissions in AWS Identity and Access Management — link
  • AWS — IAM User Guide: Policy evaluation logic — link
  • AWS — IAM User Guide: Security best practices in IAM — link
  • AWS — Organizations User Guide: Service control policies (SCPs) — link
Share this insight