Course outline

2.1

Users, Groups and Policy Documents

The root account, IAM users and groups, and every field of a JSON policy.

22 min Reading + animations

Tip: tap or hover any dotted-underlined term to see what it means.

By the end of this lesson you will be able to

  • Tell authentication (who are you?) from authorization (what may you do?), and say which part of IAM does each.
  • Explain why IAM is a global service, and what that means for users, groups and policies.
  • Use the root user, IAM users and IAM groups for what each is for, and know the rules of groups (users only, no nesting, up to 10 per user).
  • Apply the principle of least privilege when you grant permissions.
  • Choose between AWS managed, customer managed and inline policies.
  • Read and write every field of a JSON policy: Version, Id, Statement, Sid, Effect, Principal, Action, NotAction, Resource and Condition, and write ARNs correctly.
  • Tell identity-based policies from resource-based policies, and check a policy with IAM Access Analyzer and the policy simulator before you trust it.

#One account, many hands

An AWS account starts with exactly one identity: the email address and password you signed up with. Everything you create (servers, databases, buckets) belongs to that account, and the account pays for all of it.

Very soon other people and programs need to work in the same account:

  • a developer who deploys the application;
  • an operations engineer who restarts servers at night;
  • an auditor who must read everything and change nothing;
  • the application itself, which writes files to S3 and reads from a database.

Sharing the sign-up password with all of them would be a disaster: everyone could do everything, nobody could tell who did what, and one leaked password would expose the whole account. AWS Identity and Access Management (IAM) solves this. It lets you create separate identities inside the account, give each one exactly the permissions it needs, and check every single request against those permissions.

IAM itself has no charge. You pay only for the resources that your users and applications create.

#Authentication and authorization

Every request to AWS (a click in the console, a CLI command, an API call from code) goes through two separate checks:

AuthenticationAuthorization
QuestionWho are you?Are you allowed to do this, to this resource, right now?
How AWS checksYour credentials: a password at sign-in, or a signature made with an access key on every API callPolicies: JSON documents attached to you, to your groups or to the resource
Failure looks likeSign-in fails, or the API says the security token or signature is invalidAccessDenied: AWS knows who you are, but says no

After authentication, AWS builds a request context for the authorization check. It holds:

  • the principal: who is asking (an IAM user, a role, the root user);
  • the action: what they want to do, such as s3:GetObject or ec2:StartInstances;
  • the resource: what they want to do it to, named by its ARN;
  • the environment: the source IP address, the Region, the time, whether the connection uses TLS;
  • data about the resource, such as its tags.

AWS compares this context with every policy that applies. By default the answer is no: a request is allowed only if some policy explicitly allows it (and none denies it). Lesson 2.2 walks through the complete decision, including what happens when policies disagree.

What happens to one request: who are you, then may you?

Alice asks S3 for a file. Watch the two separate checks every request goes through, and what each failure looks like.

Scenario

Valid credentials and a policy that allows the action.

aliceIAM user
Console or CLI
IAMglobal
example-reportsS3
download jan.csv
Step 1 of 6
1

Alice asks for one object. Whether she clicks in the console or types a command, it becomes the same thing: one signed API call, s3:GetObject.

#IAM is a global service

Most AWS services are regional: an EC2 instance lives in one Region, an AMI belongs to one Region (lesson 4.3). IAM is different. IAM is global: when you create a user, a group, a role or a policy, it exists in every Region of the account at once. You do not choose a Region in the IAM console, and a user named alice can work in Mumbai and in Ireland with the same password and the same permissions.

What this means in practice:

  • You manage identities once per account, not once per Region.
  • The resources those identities use are still regional. A policy can name a resource in a particular Region, and the aws:RequestedRegion condition key can limit where a user may work.
  • IAM is eventually consistent. A change you make (a new user, an edited policy) is stored centrally and copied around the world, so it can take a short time before every Region sees it. Do not put IAM changes in the critical path of an application; create users and policies ahead of time.

#The identities in an account

IAM has a few kinds of identity. This lesson covers the first three; roles get their own lesson.

IdentityWhat it isCredentialsUse it for
Root userThe identity created with the account, signed in with the account emailEmail + password (and optionally access keys, which you should not create)Only the few tasks that need it, such as closing the account. Lesson 2.3.
IAM userA person or program you create inside the accountLong-term: a console password and/or up to two access keysPeople and programs that need their own permanent identity
IAM groupA collection of IAM usersNone: you cannot sign in as a groupGiving the same permissions to many users at once
IAM roleAn identity that someone or something assumes for a whileTemporary credentials onlyAWS services (EC2, Lambda), other accounts, people who sign in elsewhere. Lesson 2.4.

#The root user

The root user has complete access to everything in the account, including billing and closing the account, and IAM policies cannot limit it. That is exactly why you should not use it for daily work: one stolen root password and the attacker owns the account. Create an administrator for yourself, protect root with strong sign-in protection, and put the root credentials away. Lesson 2.3 covers what root is (and is not) for.

#IAM users

An IAM user is a permanent identity inside your account. It has:

  • a name, unique in the account and not case-sensitive (you cannot have both Alice and alice), up to 64 characters;
  • an ARN that names it everywhere, for example arn:aws:iam::111122223333:user/alice;
  • optionally a console password, to sign in at the account’s own sign-in page (https://111122223333.signin.aws.amazon.com/console, or an alias you choose);
  • optionally up to two access keys, for the CLI and code (lesson 1.3). Two, so that you can create a new one, switch over, then delete the old one;
  • permissions: policies attached to the user directly and through its groups.

A new IAM user can do nothing until a policy allows something. That is the starting point of least privilege.

Both the password and the access keys are long-term credentials: they work until someone changes or deletes them. That is their weakness. AWS’s current advice is to give people access through federation or IAM Identity Center (they sign in with the company’s identity system and get temporary credentials), and to give workloads an IAM role. IAM users remain common, especially in small accounts and on the exam, and everything you learn about policies applies to every identity.

#IAM groups

A group is a way to manage permissions for many users at once. You attach policies to the group, put users in it, and every member gets those permissions. When someone joins the operations team, add them to the operations group; when they move on, remove them.

The rules of groups:

  • A group contains only users, never other groups. There is no nesting: you cannot put developers inside engineering.
  • A user does not have to be in a group. A user can have policies attached directly, or inline, instead.
  • A user can be in several groups, up to 10. Their permissions are the combination of all of them: everything their groups and their own policies allow. No group has priority over another; when two policies disagree, the evaluation logic in lesson 2.2 decides (and an explicit deny wins).
  • A group is not an identity. It has no credentials, nobody signs in as a group, and a group cannot be named as the Principal in a resource-based policy.
  • An account can have 300 groups by default (raise it to 500), and 5,000 users.

Grouping people by job (developers, operations, auditors) and giving permissions to the job is called role-based access control (RBAC). A more flexible style, which grants permissions by matching tags on the user and on the resource, is attribute-based access control (ABAC); you will see its condition keys later in this lesson.

Groups: give permissions to a job, not to each person

Three groups, six users. Watch how users get permissions from their groups, what happens with two groups, and what groups cannot do.

Scenario

111122223333
developersec2:*, s3:*
operationsrestart EC2
auditViewOnlyAccess
alice
bob
charles
david
edward
fred

Effective permissions

UserFromCan do
alicedevelopersec2:*, s3:*
bobdevelopersec2:*, s3:*
davidoperationsrestart EC2
edwardauditview only
Step 1 of 3
1

Policies are attached to the groups, not to the people. Every member gets everything the group’s policies allow.

Self-checkQuestion

An engineer wants to place the developers group and the testers group inside a new group called engineering, so that one policy on engineering applies to everyone. What happens?

#The principle of least privilege

Least privilege means: give every identity only the permissions it needs for its job, and nothing more. It is the single most tested best practice in IAM.

Why it matters:

  • A leak does less damage. If an access key with permission to read one bucket leaks, the attacker reads one bucket. If an administrator key leaks, they own the account.
  • Mistakes stay small. A script with a bug cannot delete a database it was never allowed to touch.
  • Audits are easier. You can answer “who can delete production data?” by reading a short list of policies.

How to apply it:

  1. Start from nothing. New identities have no permissions, and that is the right default.
  2. Grant specific actions (s3:GetObject, not s3:*) on specific resources (one bucket, not *), with conditions where they help (from the office network, in one Region).
  3. It is fine to start a little broader while a project is new, for example with an AWS managed policy. Then tighten it: look at what the identity actually used (IAM Access Advisor shows last-accessed information, lesson 2.4) and remove the rest.
  4. Review regularly. People change jobs; permissions should follow.

#Policies: what they are and the three kinds

A policy is a JSON document that lists permissions. When a policy is attached to an identity (a user, a group or a role), it is an identity-based policy: it says what that identity may do. Identity-based policies come in three kinds:

AWS managedCustomer managedInline
Who writes itAWSYouYou
Can you edit itNoYesYes
ReusableAttach to any number of identities in any accountAttach to any number of identities in your accountNo: it lives inside one user, group or role
VersionsAWS updates it (new services and actions appear automatically)Up to 5 versions; one is the default; roll back by choosing an older defaultNo versions
When the identity is deletedPolicy staysPolicy staysDeleted with it
Its ARNarn:aws:iam::aws:policy/ReadOnlyAccessarn:aws:iam::111122223333:policy/reports-readNone of its own
Good forGetting started; standard job functionsYour real, least-privilege permissionsA strict one-to-one permission that must never be attached to anything else

Some AWS managed policies you will meet:

  • AdministratorAccess: every action on every resource. For administrators only.
  • PowerUserAccess: everything except managing IAM users, groups and Organizations. A common choice for developers in sandbox accounts.
  • ReadOnlyAccess: read everything, including data such as S3 objects.
  • ViewOnlyAccess: list and describe resources, without reading the data inside them. Closer to what an auditor usually needs.
  • Service policies such as AmazonS3ReadOnlyAccess and job-function policies such as DatabaseAdministrator and SecurityAudit.

AWS managed policies are convenient but broad, and AWS can change them. Because they name * resources, they rarely meet least privilege on their own. The usual path: start with an AWS managed policy, then replace it with a customer managed one written for your resources.

Limits worth knowing: a user can have 10 managed policies attached (raise it to 20), a group 10, a role 20 (raise it to 25). A customer managed policy can be up to 6,144 characters; all inline policies of one user together up to 2,048 characters (group 5,120, role 10,240). White space does not count.

AWS managed, customer managed and inline policies

The same permissions can live in three kinds of policy. Watch who can edit each, how it is shared, and what happens on a bad change or a delete.

Scenario

111122223333
ReadOnlyAccessAWS managed
reports-readcustomer managed
analysts
finance
developers
fredinline inside
attach
Step 1 of 3
1

ReadOnlyAccess is written by AWS. You can attach it, but not edit it, and AWS updates it when services gain new read actions.

#Anatomy of a JSON policy

Here is a complete identity-based policy. It lets its holder list one bucket and read and write the objects in it, but only from the office network:

{
  "Version": "2012-10-17",
  "Id": "reports-team-access",
  "Statement": [
    {
      "Sid": "ListTheBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::example-reports"
    },
    {
      "Sid": "ReadWriteObjectsFromOffice",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::example-reports/*",
      "Condition": {
        "IpAddress": { "aws:SourceIp": "203.0.113.0/24" }
      }
    }
  ]
}

The fields, top to bottom:

FieldRequired?What it does
VersionStrongly recommendedThe version of the policy language, not of your policy. Always "2012-10-17".
IdOptionalA name for the whole policy. Some services (SQS, SNS) like it in resource policies; for IAM it is informational.
StatementRequiredOne statement, or a list of statements. Each one is evaluated on its own.
SidOptionalA statement ID, a label for one statement (letters and numbers). Makes policies easier to read and talk about.
EffectRequired"Allow" or "Deny".
PrincipalOnly in resource-based policiesWho the statement is about. Never in identity-based policies: there, the principal is whoever the policy is attached to.
Action / NotActionOne of them is requiredWhat may (or may not) be done.
Resource / NotResourceOne of them is required in identity-based policiesTo what, as ARNs.
ConditionOptionalWhen the statement applies: IP range, Region, time, tags, and more.

#Version: the language, not your edit number

"2012-10-17" is the current version of the IAM policy language. The older "2008-10-17" still exists for compatibility. If you leave Version out, AWS treats the policy as the old language, and newer features silently stop working. The best-known one: policy variables such as ${aws:username} are not replaced, so arn:aws:s3:::home/${aws:username}/* matches the literal text instead of each user’s own folder. Always write "Version": "2012-10-17", and never change it to today’s date.

#Effect

Each statement either allows or denies. Most policies are lists of Allow statements. Deny statements are used as guard rails (“never delete this bucket”, “never work outside these Regions”). Remember the default: anything not allowed is denied anyway. Lesson 2.2 explains why an explicit deny always beats an allow.

#Action: service prefix, name, wildcards

An action is written service:Operation: s3:GetObject, ec2:StartInstances, iam:CreateUser, dynamodb:Query. The prefix is the service’s short name, and the name matches an API operation.

  • A list of actions is allowed: ["s3:GetObject", "s3:PutObject"].
  • Wildcards: * matches any run of characters and ? matches one character. s3:* is every S3 action; ec2:Describe* is every read-only EC2 “describe” call; "*" alone is every action of every service.
  • Action names are not case-sensitive, but write them as the documentation does.
  • A misspelled action (s3:GetObjects) is not an error at run time. It simply matches nothing, so the request is denied. The policy editor’s validation (below) flags it.

Wildcards are powerful and dangerous. s3:Get* today includes actions AWS adds next year. Prefer explicit lists for anything sensitive.

#NotAction: everything except…

NotAction matches every action except the ones listed. It is not a deny; what it does depends on the Effect:

  • Allow + NotAction: allows everything except the listed actions. "Effect": "Allow", "NotAction": "iam:*", "Resource": "*" gives access to every service except IAM, including every service AWS launches in future. That is broad; use it knowingly.
  • Deny + NotAction: denies everything except the listed actions. A classic guard rail: deny every action outside two Regions, except the global services (IAM, Organizations, CloudFront, Route 53…) that are always called in us-east-1.

NotAction never grants the listed actions, and never denies them either; they are simply not covered by that statement. NotResource and NotPrincipal work the same way for their fields, and are rarely the right tool.

#Resource and ARNs

An Amazon Resource Name (ARN) names one AWS resource uniquely across all of AWS:

arn:partition:service:region:account-id:resource
  • partition: aws for normal Regions (aws-cn for China, aws-us-gov for the American government Regions);
  • service: s3, ec2, iam, dynamodb…;
  • region: empty for global resources;
  • account-id: the 12-digit account; empty where it is not needed;
  • resource: the type and the name or ID, written the way that service defines it.

Real examples:

ResourceARNNote
IAM userarn:aws:iam::111122223333:user/aliceNo Region: IAM is global
IAM grouparn:aws:iam::111122223333:group/developers
AWS managed policyarn:aws:iam::aws:policy/ReadOnlyAccessAccount is the word aws
S3 bucketarn:aws:s3:::example-reportsNo Region, no account: bucket names are globally unique
S3 objects in itarn:aws:s3:::example-reports/*The bucket and the objects are different resources
Objects under a prefixarn:aws:s3:::example-reports/2026/*
EC2 instancearn:aws:ec2:ap-south-1:111122223333:instance/i-0abcd1234ef567890Region and account both present
DynamoDB tablearn:aws:dynamodb:eu-west-1:111122223333:table/orders

Resource can be one ARN, a list, or contain wildcards (arn:aws:s3:::example-*). "Resource": "*" means every resource.

Some actions do not work on a particular resource at all, so they must use "Resource": "*". Most list and describe calls are like this: ec2:DescribeInstances lists all instances in a Region, so there is no single instance ARN to name. The Service Authorization Reference lists, for every action, which resource types and condition keys it supports.

#Condition: when the statement applies

A Condition block narrows a statement to certain situations. Its shape is always operator → key → value(s):

"Condition": {
  "StringEquals": { "aws:RequestedRegion": ["eu-west-1", "ap-south-1"] },
  "IpAddress": { "aws:SourceIp": "203.0.113.0/24" }
}
  • Operators compare: StringEquals, StringLike (with * and ?), StringNotEquals, NumericLessThan, DateGreaterThan, Bool, IpAddress and NotIpAddress, ArnLike. Adding IfExists (StringEqualsIfExists) means “if the key is missing, treat it as a match”.
  • Keys are facts in the request context. Global keys start with aws:; service keys with the service prefix, such as s3:prefix or ec2:InstanceType.
  • The logic: every operator and every key in the block must match (and); several values for one key mean any of them (or). The example above matches a request to either of the two Regions and from the office range.

Global keys you will use often:

KeyWhat it holdsTypical use
aws:SourceIpThe public IP address the request came fromAllow only from the office or VPN range
aws:RequestedRegionThe Region the API call goes toKeep work inside approved Regions
aws:SecureTransportWhether the request used TLSDeny plain HTTP to a bucket
aws:CurrentTimeDate and time of the requestAccess that ends on a date
aws:PrincipalTag/<key>, aws:ResourceTag/<key>Tags on the caller and on the resourceABAC: “team = payments may touch resources tagged team = payments”
aws:MultiFactorAuthPresentWhether the caller signed in with a second factorProtect dangerous actions (lesson 2.3)

Two aws:SourceIp traps: a request that reaches AWS through a VPC endpoint carries a private address, so aws:SourceIp does not match it (use aws:SourceVpce or aws:VpcSourceIp); and when an AWS service (a deployment tool, for example) calls another service on your behalf, the source address is AWS’s, not yours, so a strict IP rule can break it (aws:ViaAWSService handles that case).

How a statement matches a request, field by field

The statement allows GetObject on example-reports/* from 203.0.113.0/24. Every field must match for it to apply. Try the usual mistakes.

Scenario

report-builderIAM user
policy check
example-reports
s3:GetObject

Request context

principal

arn:aws:iam::111122223333:user/report-builder

action

s3:GetObject

resource

arn:aws:s3:::example-reports/input/jan.csv

aws:SourceIp

203.0.113.25

Step 1 of 3
1

The app downloads input/jan.csv from the office. IAM lines the request up against the statement.

#Identity-based and resource-based policies

So far every policy was attached to an identity. Some services also let you attach a policy to the resource itself: an S3 bucket policy, an SQS queue policy, an SNS topic policy, a KMS key policy, a Lambda function’s resource policy, and the trust policy of an IAM role. These are resource-based policies, and they are always inline on that one resource.

Identity-based policyResource-based policy
Attached toA user, group or roleA resource (bucket, queue, key, role trust…)
SaysWhat this identity may do, on which resourcesWho may do what to this resource
Principal fieldNot allowed (it is the identity it is attached to)Required
KindsAWS managed, customer managed, inlineInline only
Can grant other accounts accessNo, only to itselfYes: name a principal from another account

A bucket policy that lets one user from another account read the bucket:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PartnerCanRead",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::444455556666:user/partner-app" },
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::example-reports/*"
    }
  ]
}

How the two combine (lesson 2.2 has the full picture):

  • Inside one account, either kind is enough: if the user’s identity policy allows it or the bucket policy allows that user, the request is allowed (unless something denies it). Two exceptions to remember: a KMS key policy and a role’s trust policy must themselves allow the access.
  • Across accounts, both are needed: the other account’s identity policy must allow its user to call your bucket, and your bucket policy must allow that user. Each account controls its own side.
  • "Principal": "*" means anyone, including the anonymous internet. In a bucket policy that makes data public, which is why S3 Block Public Access exists and is on by default.

Identity-based and resource-based policies, in one account and across two

A user’s policy speaks for the user; a bucket policy speaks for the bucket. Watch when one of them is enough and when both must agree.

Scenario

Account A 111122223333
aliceidentity policy
example-reportsbucket policy
allowed
Step 1 of 3
1

Alice’s identity policy allows GetObject on example-reports. The bucket policy says nothing about her. Inside one account, that is enough.

Self-checkQuestion

Which statement about the Principal element is correct?

#Building a policy step by step

A realistic request: “The analytics app runs as the IAM user report-builder. It must read last month’s input files under input/ in the bucket example-reports, write finished reports under output/, and list only those two folders. It must not delete anything. It runs from the office network 203.0.113.0/24.”

  1. Write down the actions

    Reading files is s3:GetObject, writing is s3:PutObject, listing is s3:ListBucket. Deleting is s3:DeleteObject, which we simply leave out: anything not allowed is denied.

  2. Match each action to its resource

    GetObject on arn:aws:s3:::example-reports/input/*. PutObject on arn:aws:s3:::example-reports/output/*. ListBucket on the bucket itself, arn:aws:s3:::example-reports. Three different resources, so three statements.

  3. Narrow the listing

    ListBucket acts on the whole bucket, so limit which folders may be listed with the S3 condition key s3:prefix.

  4. Add the network condition

    Add IpAddress with aws:SourceIp to every statement. (If the app moves into a VPC and reaches S3 through a gateway endpoint, this key stops matching; switch to aws:SourceVpce.)

  5. Validate, test, then attach

    Paste the policy into the IAM policy editor, read the validation findings, test it in the policy simulator, and only then save it as a customer managed policy named report-builder-s3 and attach it.

The result:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadInput",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::example-reports/input/*",
      "Condition": { "IpAddress": { "aws:SourceIp": "203.0.113.0/24" } }
    },
    {
      "Sid": "WriteOutput",
      "Effect": "Allow",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::example-reports/output/*",
      "Condition": { "IpAddress": { "aws:SourceIp": "203.0.113.0/24" } }
    },
    {
      "Sid": "ListOnlyTheTwoFolders",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::example-reports",
      "Condition": {
        "StringLike": { "s3:prefix": ["input/*", "output/*"] },
        "IpAddress": { "aws:SourceIp": "203.0.113.0/24" }
      }
    }
  ]
}

Read it back as a sentence per statement: “allow reading objects under input, from the office”; “allow writing objects under output, from the office”; “allow listing the bucket, only for those prefixes, from the office”. If you cannot read a statement as one clear sentence, it is probably wrong.

#What goes wrong with policies

SymptomCauseFix
AccessDenied although “the policy allows S3”The action is misspelled (s3:GetObjects) or under the wrong service prefixCopy action names from the Service Authorization Reference; read the editor’s validation
Listing works, downloads fail (or the other way round)Bucket ARN and object ARN mixed upListBucket → bucket; object actions → bucket/*
Saving fails with “Principal not allowed”Principal in an identity-based policyRemove it; it belongs only in resource-based policies
${aws:username} matches nothingVersion missing or set to 2008-10-17"Version": "2012-10-17"
A user can suddenly use a brand-new serviceAllow + NotAction, or a broad wildcardReplace with an explicit list of actions
An IP condition blocks a deployment run by an AWS serviceThe AWS service called on the user’s behalf from AWS’s own addressesAdd aws:ViaAWSService handling, or condition only the direct calls
A new permission works “a bit later”IAM is eventually consistentMake IAM changes ahead of time, not inside the application’s request path
Cannot add another inline policyThe 2,048-character total for a user’s inline policiesUse a customer managed policy (6,144 characters each)
Cannot attach an eleventh managed policy to a userThe default of 10 managed policies per userCombine policies, use groups, or request the increase to 20

#Checking a policy before you trust it

Never attach a policy you have not checked. Three tools help, all free:

  • IAM Access Analyzer policy validation. Runs automatically in the console’s policy editor, and through the access-analyzer validate-policy API. It checks the policy against more than a hundred rules and reports errors (the policy will not work: invalid action, bad ARN format), security warnings (it is more permissive than it looks, such as Allow with NotAction), warnings (it does not follow the grammar well) and suggestions (cleaner ways to write it).
  • Custom policy checks in Access Analyzer answer a precise question about a change, such as “does this new version grant any access the old one did not?”, and can run in a deployment pipeline.
  • The IAM policy simulator (https://policysim.aws.amazon.com, or aws iam simulate-principal-policy) asks: “if this user called this action on this resource, with this context, would it be allowed, and which statement decided?” It evaluates the real policies without making the call, so you can test DeleteObject safely.

Access Analyzer can also generate a starting policy from the actions an identity really used, and show unused access. Lesson 2.4 covers those, with the credentials report and Access Advisor.

#How it is used in system design

SituationGood design
A team of eight developersOne group, one customer managed policy for their actual work; no policies on individual users
One person needs a temporary extra permissionAdd them to a second group, or attach a time-limited policy (condition on aws:CurrentTime); remove it afterwards
An application on EC2 needs S3An IAM role, not an IAM user with stored keys (lesson 2.4)
Another company’s account must read one bucketA bucket policy naming their account or role, plus their own identity policy
Many teams, many resourcesTag both, write one ABAC policy that matches aws:PrincipalTag to aws:ResourceTag
“Nobody may work outside our two Regions”A Deny with aws:RequestedRegion and NotAction for the global services, ideally as a service control policy (chapter 20)
Prove that a policy change grants nothing newAccess Analyzer custom policy check in the pipeline

Exam patterns from this lesson:

  • “Several users need the same permissions; least operational effort” → put them in a group and attach the policy to the group.
  • “Give a user the permissions, but follow least privilege” → a customer managed policy naming specific actions and resources, not AdministratorAccess.
  • “Make sure a policy is only ever used by this one user and is deleted with it” → an inline policy.
  • “Roll back a bad change to a policy used by many groups” → set an earlier version of the customer managed policy as the default.
  • “Allow access only from the corporate IP range” → Condition with IpAddress and aws:SourceIp.
  • “Another account must access our S3 bucket” → a bucket policy (resource-based, with Principal).

#Try it yourself

With the AWS CLI configured for an administrator (lesson 1.3), in a test account:

# Create a group and a user, and put the user in the group
aws iam create-group --group-name reports-team
aws iam create-user --user-name report-builder
aws iam add-user-to-group --group-name reports-team --user-name report-builder

# Validate the policy from this lesson before using it
aws accessanalyzer validate-policy \
  --policy-type IDENTITY_POLICY \
  --policy-document file://report-builder-s3.json

# Save it as a customer managed policy and attach it to the group
aws iam create-policy --policy-name report-builder-s3 \
  --policy-document file://report-builder-s3.json
aws iam attach-group-policy --group-name reports-team \
  --policy-arn arn:aws:iam::111122223333:policy/report-builder-s3

# Ask the simulator: may this user delete an object? (expect implicitDeny)
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::111122223333:user/report-builder \
  --action-names s3:DeleteObject s3:GetObject \
  --resource-arns arn:aws:s3:::example-reports/input/jan.csv

# See everything that applies to the user: direct, inline and through groups
aws iam list-groups-for-user --user-name report-builder
aws iam list-attached-user-policies --user-name report-builder
aws iam list-user-policies --user-name report-builder

Then try the mistakes on purpose: change s3:GetObject to s3:GetObjects and read the validation output; remove the bucket ARN statement and run aws s3 ls s3://example-reports/ as the user. Clean up with delete-user, delete-group and delete-policy when you finish (detach and remove first).

#Summary

Key takeaways

  • IAM answers two questions on every request: authentication (who are you?) and authorization (may you do this?). By default the answer is no until a policy allows it.
  • IAM is global: users, groups, roles and policies exist in every Region; the resources they use are regional. IAM is free and eventually consistent.
  • The root user has unlimited access; never use it for daily work (lesson 2.3). IAM users have long-term credentials: a password and/or up to two access keys. Groups contain only users, cannot be nested, are not principals, and a user can be in up to 10; the user’s permissions are the combination of all of them.
  • Least privilege: specific actions, specific resources, conditions where they help; start narrow, tighten over time.
  • Identity-based policies are AWS managed (convenient, broad, not editable), customer managed (yours, reusable, up to 5 versions with rollback) or inline (one-to-one, deleted with the identity).
  • A policy has Version (always 2012-10-17), optional Id, and Statements with optional Sid, Effect, Action/NotAction, Resource/NotResource, optional Condition, and Principal only in resource-based policies.
  • ARNs: arn:partition:service:region:account:resource. IAM ARNs have no Region; S3 ARNs have neither Region nor account; a bucket (bucket) and its objects (bucket/*) are different resources.
  • Conditions: operator → key → values; different keys must all match, values of one key are alternatives. aws:SourceIp, aws:RequestedRegion, tags for ABAC.
  • Resource-based policies (bucket, queue, key, trust policies) name a Principal and can let other accounts in; across accounts both sides must allow.
  • Check policies with Access Analyzer validation and the policy simulator before attaching them.