Skip to main content
Navigation
HomeTechnical ReferenceJournalGitHubGitHub
Sidebar — toggle document categories via the logo
Categories

AWS IAM & User Management

Overview

IAM (Identity and Access Management) is the central access control service for AWS. Every API call passes through IAM for authentication (who are you?) and authorization (what are you allowed to do?). This reference covers users, groups, roles, policies, and the CLI commands to manage them.

Core concepts

EntityPurpose
UserA human or service with long-term credentials (access keys or console password).
GroupA collection of users; policies attached to groups flow to members.
RoleAn identity with temporary credentials; intended for AWS services, cross-account access, or federated users.
PolicyA JSON document defining permissions (allow/deny for specific actions/resources).
Instance ProfileA container for an IAM role that can be attached to an EC2 instance.

Users

Creating and managing users

Users represent humans or services that need long-term credentials. These commands create, inspect, and delete them — --query is handy for extracting a clean, scriptable list of user names.

# Create a user
aws iam create-user --user-name alice

# List all users
aws iam list-users
aws iam list-users --query "Users[].UserName"

# Get user details
aws iam get-user --user-name alice

# Delete a user
aws iam delete-user --user-name alice

# Delete a user's login profile (console password)
aws iam delete-login-profile --user-name alice

Access keys (for CLI/SDK access)

Access keys grant programmatic access to the AWS APIs from the CLI and SDKs. Treat them like passwords: issue them per user, rotate them on a schedule, and deactivate before deleting so nothing breaks mid-flight.

# Create access key
aws iam create-access-key --user-name alice

# List access keys
aws iam list-access-keys --user-name alice

# Deactivate an access key
aws iam update-access-key \
--user-name alice \
--access-key-id AKIA... \
--status Inactive

# Delete an access key
aws iam delete-access-key \
--user-name alice \
--access-key-id AKIA...

# Rotate: create a new key, update all services to use it, then deactivate + delete the old one

Groups

Groups let you attach permissions once and apply them to many users at once. Policies attached to a group are inherited by every member, so access is managed in one place instead of per user.

# Create a group
aws iam create-group --group-name Developers

# Add user to group
aws iam add-user-to-group --group-name Developers --user-name alice

# Remove user from group
aws iam remove-user-from-group --group-name Developers --user-name alice

# List group members
aws iam get-group --group-name Developers

# Delete a group
aws iam delete-group --group-name Developers

Roles

Creating a role

A role is an identity meant to be assumed by a service, application, or another account — it holds no long-lived credentials. The --assume-role-policy-document passed at creation is the trust policy that decides who can assume it.

# Create a role for EC2
aws iam create-role \
--role-name ec2-s3-readonly \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}'

Trust policies (who can assume the role)

This JSON document is the gatekeeper for a role: it lists the principals allowed to call sts:AssumeRole on it. The example below lets a specific account's root user and the Lambda service both assume the role.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "sts:AssumeRole"
},
{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}

Instance profiles

An instance profile is a thin container that carries a role onto an EC2 instance. Once the profile is attached, the instance fetches temporary credentials from the role automatically — no access keys live on the box.

# Create instance profile
aws iam create-instance-profile --instance-profile-name ec2-s3-readonly-profile

# Add role to instance profile
aws iam add-role-to-instance-profile \
--instance-profile-name ec2-s3-readonly-profile \
--role-name ec2-s3-readonly

# Attach to EC2 (at launch time, or via modification)
aws ec2 associate-iam-instance-profile \
--instance-id i-abc123 \
--iam-instance-profile Name=ec2-s3-readonly-profile

Assuming a role

Assuming a role returns temporary credentials scoped to the role's permissions. Export the returned AccessKeyId, SecretAccessKey, and SessionToken as environment variables to make every subsequent CLI call run as that role.

# Use STS to assume a role (returns temporary credentials)
aws sts assume-role \
--role-arn arn:aws:iam::222222222222:role/cross-account-readonly \
--role-session-name audit-session

# The output includes AccessKeyId, SecretAccessKey, SessionToken
# Export them as environment variables to use the assumed role
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...

aws s3 ls # Now running as the assumed role

Policies

Policy types

TypeScopeUse case
Managed (AWS)Available to all accountsAmazonS3ReadOnlyAccess
Managed (Customer)Per-account, reusableCompany-wide ReadOnlyAccess
InlineEmbedded in a single user/group/roleOne-off exceptions

IAM policy structure

Every IAM policy is a JSON document with a Version and one or more Statement blocks. Each statement declares an effect (allow or deny), the actions it covers, the resources they apply to, and optionally a Condition that constrains when the rule applies.

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3List",
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetObject"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
},
{
"Sid": "DenyProdWithoutMFA",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}

Common policy snippets

These are copy-paste building blocks for the most frequent permission patterns. Note how the EC2 snippet narrows permissions by region, and the iam:PassRole snippet limits exactly which roles a service is allowed to receive.

// Read-only access to a single S3 bucket
{
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetObject"],
"Resource": ["arn:aws:s3:::app-logs", "arn:aws:s3:::app-logs/*"]
}

// Allow EC2 full access in us-east-2 only
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*",
"Condition": { "StringEquals": { "ec2:Region": "us-east-2" } }
}

// Allow passing only specific roles to EC2
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::123456789012:role/ec2-*"
}

Managing policies

Managed policies are standalone permission documents that you attach to users, groups, or roles. The attach, list, and detach commands follow the same pattern for all three entity types.

# Attach a managed policy to a user
aws iam attach-user-policy \
--user-name alice \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# Attach to a group
aws iam attach-group-policy \
--group-name Developers \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess

# Attach to a role
aws iam attach-role-policy \
--role-name ec2-s3-readonly \
--policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess

# List attached policies
aws iam list-attached-user-policies --user-name alice
aws iam list-attached-group-policies --group-name Developers
aws iam list-attached-role-policies --role-name ec2-s3-readonly

# Detach policy
aws iam detach-user-policy \
--user-name alice \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

Inline policies

Inline policies are embedded directly into a single user, group, or role and cannot be shared or reused elsewhere. They are best reserved for one-off exceptions rather than broadly-scoped access.

# Create an inline policy from a JSON file
aws iam put-user-policy \
--user-name alice \
--policy-name allow-s3-logs \
--policy-document file://policy.json

# List inline policies
aws iam list-user-policies --user-name alice

# Get inline policy document
aws iam get-user-policy --user-name alice --policy-name allow-s3-logs

# Delete inline policy
aws iam delete-user-policy --user-name alice --policy-name allow-s3-logs

Permission boundaries

Permission boundaries define the maximum permissions an IAM entity can have — they act as a ceiling:

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:*", "ec2:Describe*", "cloudwatch:*"],
"Resource": "*"
}]
}
aws iam put-user-permissions-boundary \
--user-name alice \
--permissions-boundary arn:aws:iam::123456789012:policy/dev-boundary

Even if a user is granted AdministratorAccess, the boundary restricts their effective permissions to only S3, EC2 describe, and CloudWatch.

Best practices

  1. Never use the root account — create admin users and use root only for account-level tasks requiring root credentials.
  2. Use groups, not users, for policy assignment — assign policies to groups, then add users to groups.
  3. Use roles for services and CI/CD — never embed IAM user access keys in EC2 instances, Lambda, or CI pipelines.
  4. Enable MFA — at minimum for all privileged users. Enforce via policy condition.
  5. Rotate access keys regularly — no older than 90 days.
  6. Grant least privilege — start with nothing, add specific permissions as needed.
  7. Use IAM Access Analyzer — identifies resources shared with external entities.

Auditing and troubleshooting

These commands answer the question "who can do what" and surface the evidence when access breaks. simulate-principal-policy is especially powerful: it evaluates a hypothetical action against a user's effective permissions without actually calling the API.

# View account summary (users, roles, policies, MFA status)
aws iam get-account-summary
aws iam get-account-authorization-details

# List users without MFA
aws iam list-users --query "Users[?length(AccessKeys) > \`0\`].UserName"

# Check effective permissions
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/alice \
--action-names s3:ListBucket s3:GetObject

# View recent IAM events (CloudTrail)
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=CreateUser

See also