DevDocsDev Docs
IAM

IAM Best Practices

Security recommendations and guidelines for AWS IAM

Follow these best practices to secure your AWS accounts and resources using IAM effectively.

Critical Security

IAM is the foundation of AWS security. Misconfigurations can lead to data breaches, unauthorized access, and significant financial impact.

Root Account Protection

The root account has unrestricted access to all resources. Protect it aggressively.

Enable MFA on Root Account

Check Root MFA Status
aws iam get-account-summary --query 'SummaryMap.AccountMFAEnabled'

Use a hardware MFA device if possible (YubiKey, etc.).

Never Use Root for Daily Tasks

Create IAM users with appropriate permissions instead.

Delete Root Access Keys

List Root Access Keys
aws iam list-access-keys

If any exist, delete them immediately.

Enable Billing Alerts

Monitor for unexpected charges that might indicate compromised credentials.

Credential Management

Multi-Factor Authentication (MFA)

Enable MFA for all users, especially those with console access or privileged permissions.

List Users Without MFA
aws iam generate-credential-report
aws iam get-credential-report --query 'Content' --output text | base64 -d | grep 'false' | cut -d',' -f1

Access Key Rotation

Rotate access keys regularly (every 90 days is common):

Find Old Access Keys
aws iam list-access-keys --user-name developer --query 'AccessKeyMetadata[?CreateDate<`2024-01-01`]'

Create New Key

aws iam create-access-key --user-name developer

Update Applications

Deploy new credentials to all applications.

Deactivate Old Key

aws iam update-access-key --user-name developer --access-key-id OLD_KEY --status Inactive

Delete Old Key

After confirming everything works:

aws iam delete-access-key --user-name developer --access-key-id OLD_KEY

Password Policy

Enforce strong passwords account-wide:

Set Strong Password Policy
aws iam update-account-password-policy \
  --minimum-password-length 14 \
  --require-symbols \
  --require-numbers \
  --require-uppercase-characters \
  --require-lowercase-characters \
  --allow-users-to-change-password \
  --max-password-age 90 \
  --password-reuse-prevention 24 \
  --hard-expiry

Least Privilege Principle

Grant only the permissions necessary to perform a task.

Start Restrictive

Begin with no permissions and add as needed:

Minimal S3 Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/specific-path/*"
    }
  ]
}

Use Access Analyzer

Identify unused permissions:

Generate Policy from Activity
aws accessanalyzer start-policy-generation \
  --policy-generation-details '{
    "principalArn": "arn:aws:iam::123456789012:role/MyRole"
  }'

Avoid Wildcards

Too Permissive
{
  "Effect": "Allow",
  "Action": "s3:*",
  "Resource": "*"
}
Specific Permissions
{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject"
  ],
  "Resource": "arn:aws:s3:::my-bucket/uploads/*"
}

Use Roles, Not Users

For AWS Services

Never embed credentials in code. Use IAM roles:

Lambda Execution Role
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

For Cross-Account Access

Use role assumption instead of sharing credentials:

Assume Role from Another Account
aws sts assume-role \
  --role-arn arn:aws:iam::TARGET:role/CrossAccountRole \
  --role-session-name my-session

For EC2 Instances

Attach instance profiles instead of storing credentials:

Attach Instance Profile
aws ec2 associate-iam-instance-profile \
  --instance-id i-1234567890abcdef0 \
  --iam-instance-profile Name=MyInstanceProfile

Use Groups for Permissions

Organize users into groups and attach policies to groups:

Create Group and Attach Policy
# Create group
aws iam create-group --group-name Developers

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

# Add user to group
aws iam add-user-to-group \
  --user-name developer \
  --group-name Developers

Benefits:

  • Easier to manage permissions at scale
  • Clear audit trail
  • Consistent permissions for similar roles

Use Permission Boundaries

Limit the maximum permissions users/roles can have:

Developer Boundary
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*",
        "dynamodb:*",
        "lambda:*",
        "logs:*"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Deny",
      "Action": [
        "iam:*",
        "organizations:*",
        "account:*"
      ],
      "Resource": "*"
    }
  ]
}
Apply Boundary
aws iam put-user-permissions-boundary \
  --user-name developer \
  --permissions-boundary arn:aws:iam::123456789012:policy/DeveloperBoundary

Monitoring and Auditing

Enable CloudTrail

Log all API calls for security analysis:

Create Trail
aws cloudtrail create-trail \
  --name security-trail \
  --s3-bucket-name my-cloudtrail-bucket \
  --is-multi-region-trail \
  --enable-log-file-validation

Monitor with CloudWatch

Create alarms for suspicious activity:

Alarm for Root Login
aws cloudwatch put-metric-alarm \
  --alarm-name RootAccountUsage \
  --metric-name RootAccountUsageCount \
  --namespace CloudTrailMetrics \
  --statistic Sum \
  --period 300 \
  --threshold 1 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts

Regular Credential Reports

Generate and review credential reports:

Generate Credential Report
aws iam generate-credential-report
aws iam get-credential-report --output text --query Content | base64 -d > report.csv

Check for:

  • Users without MFA
  • Unused credentials
  • Old access keys
  • Root account usage

Secure Conditions

Add conditions to policies for extra security:

Require MFA

MFA Required
{
  "Condition": {
    "Bool": {
      "aws:MultiFactorAuthPresent": "true"
    }
  }
}

Restrict by IP

IP Restriction
{
  "Condition": {
    "IpAddress": {
      "aws:SourceIp": ["10.0.0.0/8"]
    }
  }
}

Require HTTPS

HTTPS Required
{
  "Condition": {
    "Bool": {
      "aws:SecureTransport": "true"
    }
  }
}

Time-Based Access

Business Hours Only
{
  "Condition": {
    "DateGreaterThan": {"aws:CurrentTime": "2024-01-01T08:00:00Z"},
    "DateLessThan": {"aws:CurrentTime": "2024-01-01T18:00:00Z"}
  }
}

Security Checklist

IAM Security Checklist

  • Root account has MFA enabled
  • Root account has no access keys
  • All users have MFA enabled
  • Strong password policy is configured
  • Access keys are rotated regularly (< 90 days)
  • Unused users and roles are removed
  • Least privilege is applied
  • Roles are used instead of long-term credentials
  • CloudTrail is enabled
  • Access Analyzer is configured
  • Regular credential report reviews

Next Steps

On this page