Amazon IAM Essentials

IAM essentials, policies, roles, CLI commands, and best practices

@geomenaWed Jun 25 20251,028 views

AWS Identity and Access Management (IAM) is the service that controls who can access which resources in your AWS account and under what conditions. It's AWS's centralized authentication and authorization system.

The problem it solves: Granular permission control, secure credential management, access auditing, and eliminating hardcoded credentials in applications.

When to use it: ALWAYS. IAM is mandatory in any AWS architecture. The question isn't "should I use IAM?" but rather "how do I design my permission structure securely and scalably?"

Key Concepts

ConceptDescription
PoliciesJSON documents that define specific permissions (what actions on what resources). They answer the question: "WHAT can you do?"
Identity-based PoliciesPolicies attached to identities (users, groups, roles). Example: "This user can read S3 and write to CloudWatch"
Resource-based PoliciesPolicies attached to resources (S3 buckets, Lambda functions). Example: "This S3 bucket allows access from this specific role"
Users (IAM Users)Identities for human beings. Have permanent credentials (password and/or access keys) until manual rotation
GroupsCollections of users that share the same permissions. Simplifies management when multiple users need identical access
Roles (IAM Roles)Identities that can be temporarily assumed by AWS services, applications, or users. Grant temporary credentials with automatic expiration
Trust Policy (Assume Role Policy)Special policy that defines WHO can assume a role. It's the role's "guest list". Answers: "WHO can use these permissions?"
Instance ProfileWrapper/container that makes a Role "attachable" to EC2 instances. It's the technical bridge between Roles and EC2
Managed PoliciesPre-created policies maintained by AWS. Examples: AmazonS3ReadOnlyAccess, AmazonSSMManagedInstanceCore
Custom PoliciesPolicies created by you for specific use cases. You have full control over exact permissions
Instance Metadata Service (IMDS)Service at 169.254.169.254 that provides temporary credentials to EC2 instances. Allows boto3/SDKs to obtain credentials automatically
ARN (Amazon Resource Name)Unique identifier for AWS resources. Format: arn:aws:service:region:account:resource

Essential AWS CLI Commands

Creating Resources

# CREATE POLICY (Custom)
aws iam create-policy \
  --policy-name MyCustomPolicy \
  --policy-document file://policy.json \
  --description "Policy description"

# CREATE USER
aws iam create-user \
  --user-name developer-john

# Create access keys for the user (for CLI/SDK)
aws iam create-access-key --user-name developer-john

# Create password for console login
aws iam create-login-profile \
  --user-name developer-john \
  --password 'TempPassword123!' \
  --password-reset-required

# CREATE GROUP
aws iam create-group --group-name backend-developers

# Add users to the group
aws iam add-user-to-group \
  --user-name developer-john \
  --group-name backend-developers

# CREATE ROLE
# 1. Create the Trust Policy first
cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Service": "ec2.amazonaws.com"
    },
    "Action": "sts:AssumeRole"
  }]
}
EOF

# 2. Create the Role
aws iam create-role \
  --role-name EC2AccessRole \
  --assume-role-policy-document file://trust-policy.json \
  --description "Role for EC2 instances"

# CREATE INSTANCE PROFILE
aws iam create-instance-profile \
  --instance-profile-name EC2AccessProfile

# Associate Role to Instance Profile
aws iam add-role-to-instance-profile \
  --instance-profile-name EC2AccessProfile \
  --role-name EC2AccessRole

Attaching Policies

# Attach policy to USER
aws iam attach-user-policy \
  --user-name developer-john \
  --policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy

# Attach policy to GROUP
aws iam attach-group-policy \
  --group-name backend-developers \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# Attach policy to ROLE
aws iam attach-role-policy \
  --role-name EC2AccessRole \
  --policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy

# Attach AWS managed policy
aws iam attach-role-policy \
  --role-name EC2AccessRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

Querying Resources

# List users
aws iam list-users

# List groups
aws iam list-groups

# List roles
aws iam list-roles

# List policies (custom)
aws iam list-policies --scope Local

# List policies (AWS managed)
aws iam list-policies --scope AWS

# View policies attached to a user
aws iam list-attached-user-policies --user-name developer-john

# View policies attached to a group
aws iam list-attached-group-policies --group-name backend-developers

# View policies attached to a role
aws iam list-attached-role-policies --role-name EC2AccessRole

# View policy content
aws iam get-policy-version \
  --policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy \
  --version-id v1

# View which users are in a group
aws iam get-group --group-name backend-developers

# View instance profiles
aws iam list-instance-profiles

# View credentials from EC2 (inside the instance)
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

Modifying Resources

# Change user password
aws iam update-login-profile \
  --user-name developer-john \
  --password 'NewPassword456!'

# Rotate access keys (create new, delete old)
aws iam create-access-key --user-name developer-john
aws iam delete-access-key \
  --user-name developer-john \
  --access-key-id AKIAIOSFODNN7EXAMPLE

# Update trust policy of a role
aws iam update-assume-role-policy \
  --role-name EC2AccessRole \
  --policy-document file://new-trust-policy.json

Deleting Resources

# Detach policy from user/group/role
aws iam detach-user-policy \
  --user-name developer-john \
  --policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy

aws iam detach-group-policy \
  --group-name backend-developers \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

aws iam detach-role-policy \
  --role-name EC2AccessRole \
  --policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy

# Remove user from group
aws iam remove-user-from-group \
  --user-name developer-john \
  --group-name backend-developers

# Delete access keys
aws iam delete-access-key \
  --user-name developer-john \
  --access-key-id AKIAIOSFODNN7EXAMPLE

# Delete login profile (console access)
aws iam delete-login-profile --user-name developer-john

# Delete user (must have no policies or access keys)
aws iam delete-user --user-name developer-john

# Delete group (must be empty)
aws iam delete-group --group-name backend-developers

# Remove role from instance profile
aws iam remove-role-from-instance-profile \
  --instance-profile-name EC2AccessProfile \
  --role-name EC2AccessRole

# Delete instance profile
aws iam delete-instance-profile \
  --instance-profile-name EC2AccessProfile

# Delete role (must have no attached policies)
aws iam delete-role --role-name EC2AccessRole

# Delete custom policy
aws iam delete-policy \
  --policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy

Architecture and Flows

Typical Architecture Diagram

EC2 Authentication Flow with IAM Role

Comparison: User vs Role

Best Practices Checklist

Security

  • Principle of Least Privilege: Grant only the minimum permissions needed
  • Don't use Root User: Create IAM users for administrative tasks
  • Enable MFA: Multi-Factor Authentication for privileged users
  • Rotate credentials: Access keys every 90 days maximum
  • Use Roles instead of hardcoding keys: For applications in AWS, always use Roles
  • Specific policies: Avoid "Resource": "*" when possible
  • Restrictive Trust Policies: Limit who can assume roles
  • Review permissions regularly: Use IAM Access Analyzer to identify unused permissions
  • Never commit credentials: Don't include access keys in source code

Cost Optimization

  • IAM is free: No cost for users, groups, roles, or policies
  • Delete unused resources: Users, roles, or policies that are no longer needed
  • Consolidate policies: Reuse policies instead of duplicating

Performance

  • Credential caching: SDKs cache temporary credentials automatically
  • Roles over Users: For applications, Roles have better performance (automatic renewal)

Reliability

  • Multiple admin users: Don't depend on a single admin user
  • Backup policies: Document or version custom policies in IaC
  • Service Control Policies (SCPs): For multi-account organizations

Operational Excellence

  • Naming conventions: Descriptive names (e.g., prod-api-ec2-role, dev-s3-access-policy)
  • Tagging: Tag roles and policies with Environment, Team, Project
  • Infrastructure as Code: Manage IAM with Terraform/CloudFormation
  • Enable CloudTrail: Audit all IAM actions
  • Environment separation: Different roles for dev/staging/prod

Common Mistakes to Avoid

Hardcoding Access Keys in Code

Why it happens: It's the "fastest" way to make it work locally.

The problem:

  • Keys exposed in GitHub repositories
  • Keys compromised if the server is hacked
  • Difficult to rotate (need to change code and redeploy)

How to avoid it:

# NEVER do this
s3 = boto3.client('s3',
    aws_access_key_id='AKIAIOSFODNN7EXAMPLE',
    aws_secret_access_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCY'
)

# DO this (boto3 gets credentials from Instance Profile)
s3 = boto3.client('s3')  # Automatically uses the EC2's Role

Forgetting the Instance Profile When Creating EC2

Why it happens: You create the Role but not the Instance Profile, or create the EC2 without associating it.

The problem: EC2 without permissions, boto3 fails with "Unable to locate credentials".

How to avoid it:

# Always create both and associate
aws iam create-role --role-name MyRole ...
aws iam create-instance-profile --instance-profile-name MyProfile
aws iam add-role-to-instance-profile --role-name MyRole --instance-profile-name MyProfile

# And specify when launching EC2
aws ec2 run-instances --iam-instance-profile Name=MyProfile ...

Incorrect Trust Policy

Why it happens: Confusing "who can assume" with "what it can do".

The problem: Role created but Lambda/EC2 can't assume it.

Example:

// Trust Policy with wrong service
{
  "Principal": {
    "Service": "ec2.amazonaws.com"  // Role for EC2
  }
}
// Trying to use it with Lambda → Error: Access Denied

// Correct Trust Policy for Lambda
{
  "Principal": {
    "Service": "lambda.amazonaws.com"
  }
}

Overly Permissive Policies

Why it happens: Using "Effect": "Allow", "Action": "*", "Resource": "*" to "make it work".

The problem: Violation of least privilege, security risk if credentials are compromised.

How to avoid it:

// Too permissive
{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

// Specific and limited
{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject"
  ],
  "Resource": "arn:aws:s3:::my-specific-bucket/*"
}

Not Distinguishing Bucket ARN vs Objects in S3

Why it happens: Not understanding that ListBucket operates on the bucket, but GetObject operates on objects.

The problem: Policy seems correct but gives Access Denied.

How to avoid it:

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::my-bucket"  // Without /*
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::my-bucket/*"  // With /*
    }
  ]
}

Mixing Identity-based and Resource-based Policies

Why it happens: Not understanding when to use each type.

Simple rule:

  • Identity-based: To control what YOUR identity can do
  • Resource-based: To control WHO can access YOUR resource

Example:

// Identity-based (attached to a Role)
// "My EC2 can read any S3 bucket"
{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "*"
}

// Resource-based (bucket policy in S3)
// "My bucket allows reading only from this specific Role"
{
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::123456789012:role/EC2Role"
  },
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-bucket/*"
}

Not Using Managed Policies for Common Cases

Why it happens: Not checking if AWS already has a policy for the use case.

The problem: Creating custom policies duplicating what AWS already maintains.

How to avoid it:

# Search for existing managed policies
aws iam list-policies \
  --scope AWS \
  --query 'Policies[?contains(PolicyName, `S3`)].PolicyName'

# Example: Instead of creating custom policy for SSM, use:
aws iam attach-role-policy \
  --role-name MyRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

Cost Considerations

What Doesn't Generate Costs in IAM

ResourceCost
IAM UsersFREE (no limit)
GroupsFREE (no limit)
RolesFREE (no limit)
Policies (custom and managed)FREE (limit: 10,000 custom policies per account)
Instance ProfilesFREE
AssumeRole operationsFREE
CloudTrail logs for IAMOnly S3 storage cost where they're saved
  • CloudTrail: $2/100,000 events (after first 5,000 free/month)
  • IAM Access Analyzer: $0.20/100 findings (after first 10,000 free)

How to Optimize

  1. Delete unused resources: Roles, users, policies that are no longer needed
  2. Consolidate policies: Reuse instead of duplicating
  3. Disable users instead of deleting: If they might be needed in the future

Free Tier

IAM is 100% covered in the free tier permanently (not just 12 months). There's no limit on normal usage.

Integration with Other Services

AWS ServiceHow it integrates with IAMTypical use case
EC2Instance Profiles allow EC2 to assume RolesGive instances access to S3, RDS, secrets without hardcoding keys
LambdaExecution Role defines function permissionsAllow Lambda to write logs, access DynamoDB, invoke other functions
S3Bucket Policies (resource-based) + IAM policies (identity-based)Control who can read/write to specific buckets
RDSIAM Database AuthenticationAuthenticate database connections without passwords
Secrets ManagerIAM policies control who can read secretsControl access to API keys, passwords, certificates
CloudWatchIAM policies allow writing logs and metricsEC2/Lambda send logs using Role permissions
DynamoDBIAM policies for granular table accessAllow only reading, or only writing, or access to specific items
STS (Security Token Service)Generates temporary credentials when assuming RolesBackend of the Roles system, AssumeRole operations
Systems ManagerSession Manager uses IAM Roles (not SSH keys)Connect to EC2 without opening SSH ports, using IAM auth
API GatewayIAM authorization for APIsRequire AWS Signature V4 signing on requests
CloudFormationService Role defines stack permissionsAllow CFN to create resources on your behalf
OrganizationsService Control Policies (SCPs)Restrict permissions at the entire account level
SSO (AWS IAM Identity Center)Temporary role assignment via SSOSingle login for multiple AWS accounts

Additional Resources

Official Documentation

Relevant Whitepapers

Useful Tools