Control who/what can call which AWS APIs with least-privilege granularity.
IAM is a security checkpoint with badges. Users are individual badges; Roles are costumes a service wears; Policies are the rulebook.
Policy (JSON)
{ Effect, Action, Resource }
|
v
User <---> Group <---> Role ---> STS AssumeRole
(auto-rotates)
IAM is the universal authorisation plane: every AWS API call must be signed by an IAM principal whose policy explicitly allows the action. A Principal can be a User (long-lived), a Role (temporary STS-issued credentials - used by EC2, ECS, Lambda, cross-account), or a service-linked Role. Policies are JSON documents with Effect, Action, Resource. Default for new users: NO permissions. Always use Roles for applications.
aws iam create-user --user-name alice
creates IAM user Alice with no permissions; attach policies next.
aws iam attach-user-policy --user-name alice \
--policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess
grants AWS-managed ReadOnlyAccess; pre-curated and versioned by AWS.
aws iam create-role --role-name myapp-role \
--assume-role-policy-document file://trust.json
Role that EC2 / Lambda can assume via instance profile / function config.
Granting *:* AdministratorAccess to application roles ‘just to make it work’. Hardcoding long-lived AWS_ACCESS_KEY_ID in env vars / code.
principle of least privilege; never use root after initial setup; never share long-lived keys; prefer Roles / OIDC / IRSA. Use IAM Access Analyzer to find unused permissions.
s3:GetObject *.s3:ListAllMyBuckets; GuardDuty ExposedS3Bucket.* Action in prod; IAM Access Analyzer on every role.CompromisedInstance.AWS_ACCESS_KEY_ID=AKIA... hard-coded; checked into public repo.