Create users, organize them into groups, attach permission policies, and hand out temporary access with roles. IAM is who-can-do-what in your AWS account — everything else depends on it.
IAM (Identity and Access Management) decides who can do what in your account. Why first: every other service checks IAM before it lets you act, so getting comfortable here unlocks everything else. Note: the email/password you signed up with is the "root user" — almost all-powerful and meant to be locked away. You build day-to-day identities instead.
Who am I right now? This shows the identity the CLI is using.
aws sts get-caller-identityYour 12-digit account number, your user/role name, and its unique ID come back as JSON. If this errors, run "aws configure" first.
A "user" is a long-lived identity for one person or one app. Why: you never want people sharing the root login — each person gets their own user so actions are traceable and access can be revoked individually.
Create a user named "alice"
aws iam create-user --user-name aliceGive alice a password so she can sign in to the web console
aws iam create-login-profile \
--user-name alice \
--password 'Ch00se-A-Strong-One!' \
--password-reset-requiredList every user in the account to confirm she's there
aws iam list-users --query 'Users[].UserName'A group is a labelled bucket of users. Why: instead of attaching the same permissions to ten developers one by one, you attach them to a "Developers" group and drop people in. Add or remove a permission once and everyone inherits it.
Create a group
aws iam create-group --group-name DevelopersPut alice in it
aws iam add-user-to-group --user-name alice --group-name DevelopersSee who's in the group
aws iam get-group --group-name Developers --query 'Users[].UserName'A policy is a JSON document listing allowed (or denied) actions. Nothing is permitted until a policy says so — IAM denies by default. Note: AWS ships hundreds of ready-made "managed" policies; you attach those by ARN (a unique AWS resource ID). You can also write your own.
Attach an AWS-managed policy: read-only access to S3, for the whole group
aws iam attach-group-policy \
--group-name Developers \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccessConfirm what's attached
aws iam list-attached-group-policies --group-name DevelopersWhen the built-in policies are too broad, write your own. This is an "identity-based" policy — it attaches to an identity (user/group/role) and says what that identity may do. Why least privilege: grant only the exact actions needed, so a leaked key can do little damage.
Save this policy document as s3-one-bucket.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::my-app-uploads/*"
}
]
}Create the policy from that file
aws iam create-policy \
--policy-name ReadWriteUploadsBucket \
--policy-document file://s3-one-bucket.jsonTwo sides of the same coin. Identity-based: attached to a user/group/role — "this identity can do X." Resource-based: attached to the resource itself (an S3 bucket, an SQS queue) — "these identities can use me." Why both: a resource-based policy lets you grant access to identities in OTHER accounts without touching their IAM.
A bucket policy is a resource-based policy. This one lets a specific user (by ARN) read objects from the bucket. Save it as bucket-policy.json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:user/alice" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-app-uploads/*"
}]
}(You'd apply it with: aws s3api put-bucket-policy --bucket my-app-uploads --policy file://bucket-policy.json — covered in the S3 lesson.)
A role is a set of permissions that anything can "wear" for a short while — no permanent keys involved. Why: instead of baking a secret key into an EC2 server or Lambda function, you let it assume a role and receive short-lived credentials that auto-rotate. This is the single biggest security win in AWS.
The "trust policy" says WHO may wear this role. Here: EC2 instances. Save it as trust-ec2.json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}Create the role with that trust policy
aws iam create-role \
--role-name AppServerRole \
--assume-role-policy-document file://trust-ec2.jsonGive the role its permissions (what it can DO once worn)
aws iam attach-role-policy \
--role-name AppServerRole \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccessAn EC2 instance cannot attach to a role directly; it attaches to an "instance profile," which is just a wrapper around one role. Why it matters: this is the plumbing that lets a server call AWS APIs with zero stored credentials. Create it once, then launch servers with it.
Create the instance profile (the wrapper)
aws iam create-instance-profile --instance-profile-name AppServerProfilePut the role inside it
aws iam add-role-to-instance-profile \
--instance-profile-name AppServerProfile \
--role-name AppServerRoleLater, "aws ec2 run-instances --iam-instance-profile Name=AppServerProfile" launches a server that can read S3 with no keys on disk.
You (or another account) can also assume a role on demand to get temporary credentials. Why: this is how you switch into an admin role only when needed, or how a CI pipeline gets just-in-time access. The credentials returned expire automatically — usually in an hour.
Ask STS (the token service) for temporary credentials for a role
aws sts assume-role \
--role-arn arn:aws:iam::111122223333:role/AdminRole \
--role-session-name my-temp-sessionThe response includes AccessKeyId, SecretAccessKey, and a SessionToken. Export those three as env vars and subsequent commands run AS that role: export AWS_ACCESS_KEY_ID=... export AWS_SECRET_ACCESS_KEY=... export AWS_SESSION_TOKEN=...
aws sts get-caller-identity # now shows the assumed role