AWS from zero · Part 1: Foundations · Lesson 3 of 38 · about 40 minutes

IAM: users, roles, policies and Identity Center

Who can do what in an AWS account, and how AWS decides. Users, groups and roles; policies read line by line; why every request starts denied and an explicit deny can't be outvoted. A read-only role for the bakery, tested in the policy simulator, switched into with keys that expire in 15 minutes, then overruled by a single deny. Plus what the new sign-up blocks, and why it blocks it.

By the end you'll know

  • The three kinds of IAM identity, users, groups and roles, and which one to use for people and for programs
  • How to read an IAM policy line by line: Effect, Action, Resource, Condition
  • How AWS decides whether a request is allowed, and why a Deny always wins
  • The kinds of policy, from the ones on a role to the service control policies over a whole account
  • Why roles and temporary keys beat users with access keys
  • How people sign in at companies, through IAM Identity Center, and how the new sign-up does it for you

Before you start

  • You need: the account from Lesson 2, and AWS CloudShell.
  • Cost: $0. IAM is free, and so are the temporary keys a role hands out.
  • The script: iam-lab.py from the practice folder's lesson-03. Everything it creates is named jr-lesson3-*, and its last step deletes it all.
  • The example: Juniper & Rye, the made-up bakery, giving someone read-only access. Every output below is from a real run on November 9, 2026.

The short version

  • IAM (Identity and Access Management) decides who can do what in an AWS account. It's free, and every request to AWS goes through it.
  • A policy is a rulebook written in JSON: Allow or Deny, which actions, on which resources, under which conditions.
  • Every request starts denied. A policy has to allow it, and an explicit Deny anywhere beats every Allow.
  • Roles beat users. A role hands out keys that expire on their own; an IAM user's access keys last until someone deletes them.
  • People sign in through IAM Identity Center at companies, not as IAM users. The new sign-up enforces that: it blocks groups and console passwords for IAM users.
  • Test a policy before anyone uses it, with the policy simulator.

What this lesson costs

PartCost
IAM policies, roles and users$0
Temporary keys from a role (AWS STS, the Security Token Service)$0
The policy simulator$0
This lesson$0, and nothing left behind

A rulebook for the bakery

Juniper & Rye has a few people and a few rules:

  • The cashier may open the till and read the menu, but not change prices.
  • The head baker may change the menu.
  • Nobody may take money out of the safe, except the owner. Even if someone's job description says "handles money", the safe rule wins.

That's IAM. The people are identities, the things they do are actions, the things they do them to are resources, and the rules are policies. The safe rule is an explicit deny: it overrides anything else that says yes. Everything below builds on that.

Who: users, groups and roles

IdentityWhat it isCredentialsUse it for
IAM userOne person or program, inside one accountA console password and/or access keys, which last until deletedOnly programs that can't use a role, like some third-party tools
GroupA set of users that share policiesNone of its ownGiving the same permissions to several users at once
RoleA set of permissions that someone or something switches intoTemporary keys, from 15 minutes to 12 hours, that expire on their ownPrograms on AWS (servers, Lambda functions), pipelines, and people signing in through Identity Center

A role is like the bakery's "on duty" badge: you pick it up when your shift starts, it opens the doors you need, and it stops working when the shift ends. Nobody takes it home. A user's access key is a key you cut and keep, and if it's lost it keeps working for whoever finds it. That's why AWS's own best practices say: people should sign in through federation with temporary credentials, and workloads should use roles.

So who are the people? At companies, IAM Identity Center: one place where staff sign in once, with MFA, and pick which account and role to use. Their sessions are temporary, and when they leave the company, one switch turns everything off.

What the new sign-up allows

iam-lab.py fence tries each kind of identity and deletes every test item straight away. In CloudShell, upload iam-lab.py (Actions, then Upload file), then:

(CloudShell)

BASH
chmod +x iam-lab.py
BASH
./iam-lab.py fence
OUTPUT
== What IAM allows here (each test item is deleted straight away)
  allowed  create a policy
  allowed  create a role
  denied   create a group: AccessDenied (explicit deny in a service control policy)
  allowed  create a user (no password, no keys)
  denied   give that user a console password: AccessDenied (explicit deny in a service control policy)
== Identity Center
  info     Identity Center instances visible from this account: 0
  • Policies and roles: allowed. Those are what the rest of this lesson, and the track, use.
  • Groups and console passwords: blocked by a service control policy, the same kind of rule that blocked the contacts in Lesson 2. An IAM user here can exist, but only for a program; it can never sign in to the console.
  • Identity Center: none visible. AWS runs it from its own organization, outside your account. When you invite someone through Manage team, that's Identity Center at work.

In other words, the new sign-up enforces AWS's own advice: people come in through managed sign-in, and IAM is for roles and policies.

What: reading a policy

A policy is a JSON document. Here's the one the lab creates, for a role that may look at IAM but change nothing:

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadIam",
      "Effect": "Allow",
      "Action": ["iam:Get*", "iam:List*"],
      "Resource": "*"
    }
  ]
}
PartMeaningHere
VersionThe policy language version. It's always this date; it isn't when you wrote it.The current language
StatementA list of rulesOne rule
SidAn optional label for the rule"ReadIam"
EffectAllow or DenyAllow
ActionWhat may be done, as service:Action. * matches anything.Every IAM action starting with Get or List
ResourceWhich things, by ARN (Amazon Resource Name). * means all.Anything
ConditionOptional: only when something is trueNone here

An ARN names one thing in AWS: arn:aws:iam::111122223333:role/jr-lesson3-reader is a role called jr-lesson3-reader in account 111122223333. A condition narrows a rule further, for example only when the request used MFA:

JSON
"Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }

The kinds of policy

KindAttached toSaysIn this track
Identity-based, managed or inlineA user, group or roleWhat this identity may doThis lesson
Trust policyA roleWho may switch into this roleThis lesson
Resource-basedA thing, like an S3 bucketWho may use this thingLesson 15, S3
Service control policy (SCP)A whole account, from its organizationThe most anything in the account may do. It grants nothing by itself.Met in Lesson 2; written in Lesson 36
Permissions boundary, session policyA role or a sessionAn upper limitNamed here; for later

A managed policy is a separate object you attach to many identities; an inline policy lives inside one identity and goes when it goes. AWS also publishes AWS managed policies, like ReadOnlyAccess, which are a quick start but rarely least privilege: they're written for every customer, not for you.

How AWS decides

Simplified from AWS's evaluation logic. Every box in the bottom row is a real result from this lesson.

Three rules cover almost everything:

  1. Every request starts denied. AWS calls this an implicit deny.
  2. An Allow lets it through, if nothing else stops it.
  3. An explicit Deny, in any policy, beats every Allow. It can't be outvoted.

Service control policies sit above all of it: if the account's organization doesn't allow an action, no policy inside the account can.

Build it: a read-only role for the bakery

Create it, and test it before anyone uses it

In the console: IAM > Roles > Create role. To test a policy before anyone uses it, open the Policy simulator, linked from the IAM dashboard under Tools.

(CloudShell)

BASH
./iam-lab.py setup
OUTPUT
== Create the reader policy and role
  created policy jr-lesson3-read-iam: allow iam:Get* and iam:List* on everything
  created role jr-lesson3-reader, trusted by this account, with jr-lesson3-read-iam attached
== Test the policy before using it (the policy simulator)
  allowed       iam:ListRoles
  allowed       iam:GetRole
  implicitDeny  iam:CreateRole
  implicitDeny  iam:DeleteRole
  implicitDeny  s3:ListAllMyBuckets

The role needs two policies. The permissions policy is the one above. The trust policy says who may switch into the role, here anyone in this account who's allowed to:

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:root" },
      "Action": "sts:AssumeRole"
    }
  ]
}

:root here means "this account", not the root user.

The policy simulator checked each action against the policy without anyone using it: two allowed, three implicitDeny, AWS's name for "nothing allows it". Run it on any policy you write before it goes anywhere near real work.

Switch into the role

(CloudShell)

BASH
./iam-lab.py try
OUTPUT
== Signed in as assumed-role jr-lesson3-reader, with keys that expire in 15 minutes
  allowed  list roles
  allowed  list users
  allowed  read its own role
  denied   create a role: AccessDenied (no policy allows it)
  denied   list S3 buckets: AccessDenied (no policy allows it)

The script asked AWS STS for the role's keys, which came back set to expire in 15 minutes, then tried five things. The results match the simulator exactly: reading IAM works, creating anything doesn't, and S3, another service entirely, isn't mentioned in the policy, so it's denied.

Add one Deny

The lab adds an inline policy to the role:

JSON
{ "Effect": "Deny", "Action": "iam:ListUsers", "Resource": "*" }

In the console: IAM > Roles > pick the role > Add permissions > Create inline policy.

(CloudShell)

BASH
./iam-lab.py deny
OUTPUT
== Added inline policy jr-lesson3-no-list-users: deny iam:ListUsers
== Signed in as assumed-role jr-lesson3-reader, with keys that expire in 15 minutes
  allowed  list roles
  denied   list users: AccessDenied (explicit deny in an IAM policy)
  allowed  read its own role
  denied   create a role: AccessDenied (no policy allows it)
  denied   list S3 buckets: AccessDenied (no policy allows it)

List users flipped to denied, while list roles, covered by the very same Allow, kept working. The iam:List* Allow still matches iam:ListUsers, but a Deny can't be outvoted. Look at the reasons, too: "explicit deny" versus "no policy allows it". Reading that difference in an error message tells you whether to remove a deny or add an allow.

Clean up

In the console: IAM > Roles > tick the role > Delete.

(CloudShell)

BASH
./iam-lab.py cleanup
OUTPUT
== Clean up
  deleted  inline policy jr-lesson3-no-list-users
  deleted  detach jr-lesson3-read-iam
  deleted  role jr-lesson3-reader
  deleted  policy jr-lesson3-read-iam
  gone     role jr-lesson3-sneaky, if the test made it
== Left over: nothing

The order matters: a role can't be deleted while it has policies, and a managed policy can't be deleted while it's attached. jr-lesson3-sneaky is the role try attempted to create; it was never made, so there was nothing to delete.

Never do this

Never paste "Action": "*" with "Resource": "*" to make an error go away. That's every action on everything: administrator access. Read the error instead. "No policy allows it" means add one specific action; "explicit deny" means find the deny.

Writing policies well

  • Least privilege. Start with nothing and add only what a job needs. Broad is fine while exploring; narrow it before it's real.
  • Name actions, not wildcards, once you know them: s3:GetObject, not s3:*.
  • Name resources where you can: one bucket's ARN, not *.
  • Use conditions for the important ones: MFA, a specific network, a tag.
  • Test first in the simulator, and let IAM Access Analyzer check policies and suggest narrower ones from what a role actually used.
  • Remove what nobody uses. IAM shows when each role and permission was last used.

In real systems

  • People never get IAM users. They sign in through IAM Identity Center, often connected to the company's existing directory, and switch into roles per account.
  • Programs on AWS use roles, delivered automatically: a server's instance profile, a Lambda function's execution role, a pipeline's OIDC (OpenID Connect) role in Lesson 31.
  • Policies are code. They live in Git, go through review, and get deployed with Terraform (Lesson 29), not typed into the console.
  • Organizations set the outer limits with SCPs, like the ones you've now hit twice. Lesson 36 writes your own.

Spot the problem

Work out what's wrong before you read the explanation.

"It only needs the photos bucket." A developer's upload script fails with AccessDenied, so they attach this to its role and the error goes away:

JSON
{ "Effect": "Allow", "Action": "s3:*", "Resource": "*" }

What's wrong? The policy gives every S3 action on every bucket in the account, including deleting them and changing who can read them, when the script needed to upload to one bucket. The fix: name the action and the resource, s3:PutObject on arn:aws:s3:::juniper-rye-photos/*, then check it in the policy simulator: s3:PutObject on that bucket allowed, s3:DeleteBucket denied. Then check: does the original error say "no policy allows it"? If so, one specific allow was all it needed.

Keep this

RememberWhy
Every request starts deniedTo allow something, a policy has to say so
An explicit Deny beats every AllowDenies are how you set hard limits
SCPs sit above the accountNothing inside can override them
Roles, not access keysTemporary keys expire on their own
People through Identity CenterOne sign-in, MFA, one switch to remove access
Simulate before you attachFind out what a policy does before anyone relies on it
"No policy allows it" vs "explicit deny"Add an allow, or find the deny

Where you are now

You can read a policy line by line, predict whether AWS will allow a request, and explain why a Deny always wins. You've built a read-only role, tested it in the simulator, used it with keys that expired on their own, and overruled it with one deny. You also know why the new sign-up blocks groups and console passwords: people sign in through Identity Center, and programs use roles. Next, Lesson 4 puts the AWS command line interface on your own machine, signed in with short-lived credentials instead of keys in a file.

Check yourself

  1. A role has an Allow for iam:List* and a Deny for iam:ListUsers. What happens when it lists users?

  2. A request matches no policy at all. What does AWS do?

  3. What does a role's trust policy decide?

  4. Why prefer a role to an IAM user with access keys for a program?

  5. Your role has full access, but an action fails with an explicit deny in a service control policy. What fixes it inside the account?

  6. In a policy, what does a Resource of * mean?

  7. How do people usually sign in to AWS at a company?

  8. What does the policy simulator do?

Key terms

  • IAM (Identity and Access Management): the AWS service that decides who can do what. Free.
  • Identity, or principal: who's asking: a user, a role, or a service.
  • IAM user: an identity with long-lived credentials. For programs that can't use a role.
  • Group: a set of users that share policies.
  • Role: permissions someone or something switches into, with temporary keys.
  • AWS STS (Security Token Service): hands out a role's temporary keys.
  • Policy: a JSON document of rules: Effect, Action, Resource, and optionally Condition.
  • ARN (Amazon Resource Name): the unique name of one thing in AWS.
  • Managed policy: a policy you attach to many identities. Inline policy: one that lives inside a single identity.
  • Trust policy: a role's policy saying who may switch into it.
  • Implicit deny: denied because nothing allowed it. Explicit deny: denied because a policy said Deny.
  • Service control policy (SCP): an organization's limit over a whole account; it grants nothing by itself.
  • Least privilege: only the permissions a job needs.
  • Policy simulator: predicts what a policy allows, without running anything.
  • IAM Identity Center: where people sign in once and switch into roles across accounts.

Go deeper