Three users. Three permission walls. The whole IAM model fits in one sentence: a user can do only what the policies attached to their groups allow.
The IAM lab gave me three users and one instruction: sign in as each of them and see what changes.
The first user could open S3 and nothing else. The second could inspect EC2 and nothing else. The third could do almost anything in the account. Same console, same password rules, same sign-in page. The only difference was the group each user belonged to.
This is the whole mental model. Permissions are inherited, not personal. A user is not given abilities directly. The user belongs to a group, the group carries policies, and the policies decide what the user can do. A user with a valid password and no group memberships is a user who can sign in and immediately meet a wall.
I did not understand this the first time I touched IAM. I assumed the console was showing me what each person was allowed to do, as if permissions were a property of the person, like a level in a game. They are not. Permissions are a property of the attachments: user to group, group to policy, policy to actions.
The three-user experiment
The lab hands you three users and three groups:
s3-userin the S3-ReadOnly group. Sees buckets. Cannot touch EC2.ec2-userin the EC2-ReadOnly group. Sees instances. Cannot touch S3.admin-userin the Admin group. Sees everything.
Signing in as each one feels like three different accounts. The console literally renders different menus. The moment that made it click for me was the fourth user. The lab also gives you a user with a valid password and no group at all. The password worked. The sign-in URL worked. The console opened. And behind it there was nothing. No services, no buttons, no access. Just an empty dashboard and the quiet truth of implicit deny.
Nothing is allowed unless something explicitly allows it. That single rule is why an empty user stays empty no matter how long they have been in the account.
How a policy actually works
Every IAM policy is a list of statements, and every statement has three parts:
Effect. Allow or Deny, and a Deny always wins over an Allow.
Action. The API actions it covers, like
s3:GetObjectorec2:DescribeInstances.Resource. What those actions can be applied to.
A read-only S3 policy looks like this:
{
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetObject"],
"Resource": "*"
}That statement says: this identity can list and read objects. It cannot delete anything, because deletion was never allowed. It cannot touch EC2, because EC2 was never mentioned. The policy is the entire world, and anything outside it does not exist for that identity.
Two consequences follow from this design, and both matter in real work.
Least privilege is the default shape of the system, not a special discipline. Attach nothing and the user gets nothing. The actual work is deciding how much to attach, and the safe answer is almost always the smallest set that does the job. Read-only groups are the best starting point, and write access should be the exception that gets reviewed.
Groups exist so permissions stay maintainable. Three people doing the same job means one group, one policy, three memberships. When the job changes, you edit one policy, not three users. This is also why the answer to most access questions is a group, not a user.
The distinction that saves you later: identity policy versus resource policy
There is a second layer that confuses people right after they understand groups. An identity policy is attached to a user, group, or role and says what that identity can do. A resource policy is attached to the resource itself and says who can use it. S3 bucket policies are the classic example: the bucket can carry its own policy that grants access to specific accounts or roles, separate from what any user's identity policy allows.
Both must agree before access happens. A user with full S3 permissions in their identity policy still hits a wall if the bucket policy denies them. A bucket policy that grants everyone access does nothing for a user whose identity policy never allows S3 in the first place. This is why the question "can this person read that bucket?" has two answers to check, and why the fastest diagnosis is to trace both sides of the attachment.
How machines get permissions
Users are not the only identities in an account. An EC2 instance often needs to call AWS APIs, to read from S3, or to write logs. It cannot type a password, and it should never carry long-lived access keys. The AWS answer is a role.
You create a role, attach a policy to it, and assign the role to the instance through an instance profile. AWS then issues the instance short-lived temporary credentials automatically. The instance inherits permissions the same way a user does, through attachments: instance to role, role to policy, policy to actions.
Two habits come from this. Never put access keys inside scripts or user data, because a role removes the need. And remember that temporary credentials expire, which is a feature. A stolen temporary key stops working; a stolen long-lived key does not.
Reading a permission problem like a support engineer
The most common support question in any cloud environment is some version of "why can this person not do X?" The wrong way to answer it is to stare at the user and wonder. The right way is to trace the chain: which groups does the user belong to, which policies do those groups carry, and does any statement allow the action in question?
A concrete trace makes the habit visible. Say s3-user opens the console, finds EC2 missing, and opens a ticket. The trace runs in three steps. s3-user belongs to the S3-ReadOnly group. That group carries the s3-readonly policy. That policy mentions only s3: actions. The action ec2:LaunchInstances appears nowhere, so implicit deny explains the wall in one sentence. No console setting, no account flag, no password reset would have changed it.
If the answer is no, the fix is rarely "give the user more access." It is "find the group that owns the job and check its policy." Sometimes the policy is missing an action. Sometimes the user is in the wrong group. Sometimes the user is in no group at all. All three look identical from the user's side, a wall. All three are different questions from the policy side.
One more scope detail is worth keeping straight: IAM is global, while most services are regional. An IAM user exists once for the whole account, but an EC2 instance lives in a specific Region. When something looks inconsistent between identities and resources, check whether the confusion is really a Region problem wearing an IAM costume.
The wall is the feature
IAM feels restrictive until you see the walls as the point. A cloud account is a shared building, and the walls are what let many people work in it without stepping on each other. The user who can only read S3 is not a limited user. They are a user with a bounded blast radius. If their credentials ever leak, the damage is contained by the same walls that felt annoying.
Which is why the admin user is the least safe identity in the lab, not the most important one. Full access is a liability you assign to the fewest people possible, and you audit it constantly. The console may call it power. The security model calls it exposure.
Try it yourself
If you have any AWS account, even a training sandbox: create three groups with small policies, one that can read S3, one that can read EC2, one with full access. Add one user to each group. Sign in as all three in separate browser sessions. Then create a fourth user, add them to no group, and sign in as them.
You will learn more from that empty dashboard than from any diagram. Write down, for each user, one action they can perform and one action they cannot, then find the policy line that explains both. When you are unsure whether a policy allows an action, the IAM Policy Simulator can test a user, an action, and a resource combination before you change anything. When you are done, delete the users and groups. The lab creates nothing else, and cleanup is part of the habit.
The durable lesson
When you look at an IAM screen, do not ask who the user is. Ask what is attached to them. Every permission question in AWS answers to the same sentence: permissions are inherited, not personal. The earlier that sentence becomes instinct, the faster policies, tickets, and consoles start making sense.


