IAM Fundamentals (Users, Groups, Roles, Policies)
Key concepts
IAM users represent individual identities
IAM groups simplify permissions management
IAM roles provide temporary credentials
IAM policies define permissions using JSON
Principal, Action, Resource, Effect, Condition
The Day Everything Was Root
Imagine your first week at Trailbase, a small startup that runs a hiking app on AWS. The whole company signs in with the same credentials: the root account email and password, taped to a note in the team chat. It works fine, right up until an intern named Theo, trying to clean up a test environment, deletes the production photo bucket. Nobody knows it was Theo. The audit log just says "root did it," because as far as AWS is concerned, everyone at Trailbase is the same all-powerful person.
That failure is exactly what AWS Identity and Access Management (IAM) exists to prevent. IAM answers two questions for every single request that touches your AWS account: authentication (who is making this request?) and authorization (are they allowed to do this?). Everything in this lesson, users, groups, roles, and policies, is a tool for answering those two questions precisely instead of handing everyone the master key.
One more thing before we rebuild Trailbase properly: IAM is a global service. There is no "IAM in eu-west-1." An identity you create exists across every region at once, which is why the exam loves asking about it in scenarios that span regions.
The One Principle That Rules Them All
IAM is built around least privilege: every identity should hold exactly the permissions its job requires, and nothing more. Theo should have been able to delete test buckets and nothing else. When you are unsure of an exam answer, the choice that grants the narrowest permissions is usually the right one.
Security is 30% of SAA-C03, and IAM shows up inside questions from every other domain too. Expect 8 to 10 questions that hinge on IAM alone, most of them about choosing roles vs users and predicting policy evaluation.
The Cast of Identities
Here is the whole IAM system on one map. Keep it in view as we meet each piece: identities on the left (users, groups, roles), policies in the middle defining what is allowed, and AWS resources on the right receiving the requests.

IAM Users: A Key for Every Human
The first fix at Trailbase is obvious once you name the problem: five people were sharing one identity, so actions could never be traced to a person and permissions could never be tailored to a job. An IAM user is a permanent identity for one human (or, in rare legacy cases, one application). Dana, the founder, creates a user for each teammate, locks the root credentials in a password manager, and from that day on the audit log says theo instead of root.
IAM Users
An IAM user represents a single person or application with a permanent identity in your account.
A user can hold two kinds of credentials. A console password signs them into the AWS web console. Access keys (an Access Key ID plus a Secret Access Key) authenticate programmatic calls from the CLI, SDKs, and APIs. Each user can have at most 2 access keys, which exists so you can rotate: create the new key, switch your tooling over, then delete the old one.
Three facts matter constantly on the exam: a new user starts with zero permissions (implicit deny by default), a user can belong to up to 10 groups, and every user has an ARN of the form arn:aws:iam::account-id:user/username.
The root account does not disappear. Dana still needs it for a handful of account-level tasks such as changing the support plan or closing the account, so she protects it with MFA and stops using it for daily work. Everything else now happens through IAM users.
How many access keys can a single IAM user hold at once?
IAM Groups: Permissions at Team Scale
Within a month Trailbase hires four more developers. Dana could attach the same policies to each new user by hand, but hand-copied permissions drift: one developer ends up with an extra permission from an old experiment, another is missing one and files a ticket. An IAM group solves this by being a container for users. Attach the policy to the group once, and every member inherits it. New hire? Add them to Developers and they are productive in one step. Someone moves to the data team? Move their membership, and their permissions follow.
IAM Groups
An IAM group is a collection of IAM users that exists purely to manage permissions in one place.
Groups have strict boundaries worth memorizing. They contain users only, never other groups, so no nesting hierarchies. They have no credentials of their own; nobody signs in as a group. And a group is not a principal, meaning a policy cannot name a group as the party being granted access to a resource. A user can sit in up to 10 groups and inherits the union of all their policies.
Which statement about IAM groups is TRUE?
IAM Roles: Identities You Borrow, Not Own
Two new problems appear at Trailbase that users and groups cannot solve cleanly.
First, the app servers on EC2 need to upload hiker photos to S3. The tempting shortcut is to create a user, generate access keys, and paste them into the server's config file. Now a permanent secret lives on every instance, it never expires, and one leaked AMI or committed config file hands an attacker the keys for good.
Second, Priya, an external auditor with her own AWS account, needs read access to Trailbase's logs for two weeks. Creating a permanent user for a temporary outsider is exactly the kind of standing access that least privilege forbids.
An IAM role answers both. A role is an identity with permissions, just like a user, but nobody owns it and it has no permanent credentials. Instead, a trusted party assumes the role and receives temporary security credentials that expire on their own (they are issued by AWS STS, the Security Token Service, which you will meet properly in lesson 4). The EC2 instances assume a role and get short-lived keys that AWS rotates automatically. Priya assumes an auditor role from her own account and loses access the moment the trust is removed.
Every role is defined by two documents, and the exam tests whether you can keep them apart:
- The trust policy answers who may assume this role.
- The permissions policy answers what the role can do once assumed.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}Read it as a sentence: the EC2 service is allowed to assume this role. Swap the principal for another AWS account ID and you have Priya's cross-account trust policy instead.
Where Roles Show Up
| Use Case | Description | Trailbase Example |
|---|---|---|
| EC2 instance role | Lets instances call AWS services without stored keys | App servers uploading photos to S3 |
| Cross-account access | Lets identities from another account work in yours | Priya the auditor reading logs |
| Federation | Lets an external identity provider grant AWS access | Corporate AD users signing in via SAML |
| Service-linked role | Predefined role a service uses to act on your behalf | Auto Scaling launching EC2 instances |
Why Roles Beat Access Keys
No secret is ever stored on the machine, the temporary credentials rotate themselves, and changing permissions means editing the role rather than redeploying the application. When an exam question involves an AWS resource calling another AWS service, the answer is a role. Nearly every time.
An application running on EC2 needs to write to S3. What is the BEST approach?
IAM Policies: The Language of Permission
Users, groups, and roles are all just identities. None of them can do anything until a policy says so. A policy is a JSON document that states which actions are allowed or denied on which resources, optionally under which conditions. Here is the policy Dana writes so the developers can work with the production photo bucket, but only from the office network:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPhotoBucketAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::trailbase-photos",
"arn:aws:s3:::trailbase-photos/*"
],
"Condition": {
"IpAddress": {
"aws:SourceIp": "192.168.1.0/24"
}
}
}
]
}Walk through it once slowly, because every IAM policy you will ever read has this same skeleton. Version is always the literal string 2012-10-17; treat it as a magic constant. Effect is either Allow or Deny. Action lists the API operations in service:Operation form. Resource pins those actions to specific ARNs; note that the bucket itself and the objects inside it (/*) are different resources, a classic exam trap. Condition narrows when the statement applies at all.
Policy Elements
| Element | Required | What It Does |
|---|---|---|
| Version | Yes | Policy language version, always 2012-10-17 |
| Statement | Yes | Array holding one or more permission statements |
| Sid | No | Human-readable label for the statement |
| Effect | Yes | Allow or Deny |
| Principal | Only in resource-based policies | Who the statement applies to |
| Action | Yes | Operations, like s3:GetObject |
| Resource | Yes | ARNs the actions apply to |
| Condition | No | Extra requirements, like source IP or MFA |
Policies come in several flavors, and knowing which attaches where is easy exam money:
The Five Policy Types
| Type | Attaches To | Job |
|---|---|---|
| Identity-based | Users, groups, roles | Defines what an identity may do |
| Resource-based | The resource itself (S3 bucket, SQS queue) | Defines who may access this resource, using a Principal element |
| Permissions boundary | Users and roles | Caps the maximum permissions an identity can ever have |
| Service control policy (SCP) | AWS Organizations accounts | Organization-wide guardrail that limits what any identity in the account can do |
| Session policy | A single assumed-role session | Further trims permissions for just that session |
One more distinction the exam leans on: managed policies are standalone objects you can attach to many identities and version over time, while inline policies are embedded in a single identity and die with it. Prefer managed policies; save inline ones for a strict one-to-one relationship you never want reused.
Managed vs Inline Policies
| Aspect | Managed | Inline |
|---|---|---|
| Reusability | Attach to many identities | Locked to one identity |
| Maintenance | Update once, applies everywhere | Edit each copy separately |
| Versioning | Up to 5 versions with rollback | None |
| Lifecycle | Exists independently | Deleted with its identity |
| AWS-provided options | Yes, AWS managed policies | No |
When Policies Collide
Mara, Trailbase's data engineer, sits in two groups. Developers grants s3:GetObject on the photo bucket. A newer DataGovernance policy explicitly denies s3:* on that same bucket while a privacy review is running. Mara requests a photo. What happens?
She is denied, and she would be denied even if ten other policies allowed her. AWS evaluates every policy that applies to a request using three rules, in this order of power:
- Everything starts as an implicit deny: no policy mentioning you means no access.
- Any applicable Allow lifts that implicit deny; permissions across your policies are combined with a logical OR.
- One explicit Deny anywhere trumps every Allow, with no exceptions.

If the account lives inside AWS Organizations, SCPs sit above this whole process as an outer fence: an action an SCP does not permit is unavailable to every identity in the account, including administrators. The full decision flow looks like this:

Evaluation Rules Worth Tattooing
Explicit Deny always wins. Implicit deny is the silent default. Allows are unioned across every policy that applies. SCPs cap everything from above. If you can recite those four sentences, you can solve most policy-evaluation questions by inspection.
A user has one policy that allows s3:GetObject on a bucket and another policy that explicitly denies all S3 actions on it. What happens when they request an object?
Trailbase, Rebuilt
Six weeks after the Theo incident, the account looks like a textbook diagram. Every human has an IAM user with MFA. The Developers group carries the day-to-day policies, so onboarding is one action. The EC2 fleet assumes an instance role for S3 uploads, and no server has ever seen a permanent key. Priya audits through a cross-account role that Trailbase can revoke in one click. Root credentials live in a vault and have been used exactly once, to enable MFA on themselves.
Theo still works there. His user can delete anything he likes, as long as it is in the sandbox account.
Exam questions are miniature Trailbase stories: some identity needs some access under some constraint. Ask yourself two questions every time. Is the actor a human (user), a fleet of humans (group), or a machine or outsider (role)? And what is the narrowest policy that satisfies the requirement?
Scenario Pattern Matching
| Exam Scenario | Answer | Why |
|---|---|---|
| App on EC2 needs DynamoDB access | IAM role attached to the instance | Temporary credentials, nothing stored on the box |
| Admin rights needed only during incidents | A role the user assumes when needed | Separation of duties and a clean audit trail |
| Contractors need six weeks of access | Federation or a cross-account role | No permanent users for temporary people |
| Block bucket deletion across all accounts | SCP denying s3:DeleteBucket at the org level | Guardrails that even admins cannot bypass |
| External app calls AWS APIs from a data center | IAM user with access keys, rotated regularly | Roles need an AWS or federated principal; keys are the last resort |
Common Pitfalls
Living as Root
Using the root account for daily work is the original Trailbase sin. Root cannot be restricted by IAM policies and its actions cannot be attributed to a person. Create users for everything, lock MFA onto root, and reserve it for the few account-level tasks that genuinely require it.
Access Keys in Code
Hardcoded keys end up in version control, never expire, and are painful to rotate. Inside AWS, use roles. Outside AWS, keep keys in environment variables or Secrets Manager and rotate them on a schedule.
The Convenient Asterisk
Policies with Action star and Resource star feel productive and audit like a crime scene. Every wildcard widens the blast radius of a leaked credential. Name the actions, pin the ARNs, and add conditions where they help.
Quick Reference
arn:aws:iam::account-id:user/user-name
arn:aws:iam::account-id:group/group-name
arn:aws:iam::account-id:role/role-name
arn:aws:iam::account-id:policy/policy-nameActions You Will See in Answers
| Action | Meaning |
|---|---|
| iam:CreateUser | Create an IAM user |
| iam:AttachUserPolicy | Attach a managed policy to a user |
| iam:CreateRole | Create a role |
| sts:AssumeRole | Obtain temporary credentials for a role |
| iam:PassRole | Hand a role to an AWS service, a frequent trick answer |
IAM Limits That Appear on the Exam
| Resource | Default Limit |
|---|---|
| Users per account | 5,000 |
| Groups per account | 300 |
| Roles per account | 1,000 |
| Customer managed policies | 1,500 |
| Groups per user | 10 |
| Access keys per user | 2 |