Skill

Harden AWS IAM Security

An AWS IAM hardening expert - finds overpermissive/wildcard policies, missing MFA, and stale keys via CLI, with least-privilege policy templates.

Works with aws

Maintainer of this project? Claim this page to edit the listing.


91
Spark score
out of 100
Updated yesterday
Version 15.5.1

Add to Favorites

Why it matters

Automate the review and hardening of AWS IAM policies to enforce least privilege and enhance security posture.

Outcomes

What it gets done

01

Identify and remediate overly permissive IAM policies.

02

Enforce Multi-Factor Authentication (MFA) for all users.

03

Manage and rotate access keys to reduce exposure.

04

Analyze role trust relationships and policy usage.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/ag-aws-iam-best-practices | bash

Overview

AWS IAM Best Practices

An AWS IAM hardening expert with CLI checks for overpermissive/wildcard policies, missing MFA, and stale access keys, plus ready-made least-privilege, MFA-required, time-boxed, and IP-restricted policy templates. Use when IAM policies need reviewing, least privilege needs implementing, or IAM security generally needs hardening.

What it does

Reviews and hardens IAM policies following AWS security best practices and least-privilege principles, anchored on three core commitments: least privilege (minimum needed permissions, managed policies over inline ones, no wildcard actions, regular reviews), defense in depth (MFA for all users, roles instead of long-term access keys, service control policies, CloudTrail enabled), and separation of duties (distinct admin and user roles, different roles per environment, approval workflows, regular permission audits). Concrete CLI checks scan for overly permissive policies by grepping policy documents for a bare wildcard action, flag inline policies attached directly to users that should be managed policies instead, list users lacking MFA from the credential report, check whether a policy actually enforces aws:MultiFactorAuthPresent, find access keys older than 90 days by comparing their creation date to today, identify IAM roles that have never been used, and flag roles whose trust policy grants access to an external AWS account. A rotate-access-key pattern creates the replacement key first, prints the exact command to delete the old one once applications are updated, and deactivates rather than immediately deletes the old key so it can be tested safely before final removal.

Four ready-made policy templates cover common hardening patterns, starting with least-privilege S3 access scoped to a user's own prefix via a policy variable:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::my-bucket/user-data/${aws:username}/*"
    },
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::my-bucket",
      "Condition": {"StringLike": {"s3:prefix": "user-data/${aws:username}/*"}}
    }
  ]
}

plus an MFA-required policy that denies all actions unless MFA is present, a time-boxed policy that only grants EC2 access within a specific date range, and an IP-restricted policy denying all actions from outside two named CIDR ranges. A 20-item hardening checklist spans user management (MFA, unused user removal, 90-day key rotation, roles over long-term credentials, password policy), policy management (replacing inline policies, removing wildcards, least privilege, condition-based policies, regular reviews), role management (EC2 instance roles, cross-account trust review, unused role removal, session tags), and monitoring (CloudTrail for IAM events, CloudWatch alarms on IAM changes, IAM Access Analyzer, privilege-escalation monitoring). A companion Python script automates three of these checks directly against the IAM API - users without MFA, keys older than 90 days, and policies with a bare wildcard action - and prints a consolidated report.

When to use - and when NOT to

Use it when IAM policies need reviewing, least privilege needs implementing, or IAM security generally needs hardening. Best practices reinforce using AWS managed policies where possible, versioning policies, testing changes in non-production first, documenting each policy's purpose, running access reviews quarterly, using IAM Access Analyzer, and layering service control policies for organization-wide guardrails on top of individual IAM policies.

Inputs and outputs

Takes an AWS account's IAM configuration; produces a report of overpermissive policies, missing MFA, stale access keys, and unused roles, plus ready-to-apply least-privilege, MFA-required, time-boxed, or IP-restricted policy documents.

Who it's for

Teams auditing or hardening AWS IAM who want concrete CLI checks and ready-made policy templates rather than reading the best-practices documentation and writing checks from scratch.

Source README

AWS IAM Best Practices

Review and harden IAM policies following AWS security best practices and least privilege principles.

When to Use

Use this skill when you need to review IAM policies, implement least privilege access, or harden IAM security.

Core Principles

Least Privilege

  • Grant minimum permissions needed
  • Use managed policies when possible
  • Avoid wildcard (*) permissions
  • Regular access reviews

Defense in Depth

  • Enable MFA for all users
  • Use IAM roles instead of access keys
  • Implement service control policies (SCPs)
  • Enable CloudTrail for audit

Separation of Duties

  • Separate admin and user roles
  • Use different roles for different environments
  • Implement approval workflows
  • Regular permission audits

IAM Security Checks

Find Overly Permissive Policies

### List policies with full admin access
aws iam list-policies --scope Local \
  --query 'Policies[*].[PolicyName,Arn]' --output table | \
  grep -i admin

### Find policies with wildcard actions
aws iam list-policies --scope Local --query 'Policies[*].Arn' --output text | \
while read arn; do
  version=$(aws iam get-policy --policy-arn "$arn" \
    --query 'Policy.DefaultVersionId' --output text)
  doc=$(aws iam get-policy-version --policy-arn "$arn" \
    --version-id "$version" --query 'PolicyVersion.Document')
  if echo "$doc" | grep -q '"Action": "\*"'; then
    echo "Wildcard action in: $arn"
  fi
done

### Find inline policies (should use managed policies)
aws iam list-users --query 'Users[*].UserName' --output text | \
while read user; do
  policies=$(aws iam list-user-policies --user-name "$user" \
    --query 'PolicyNames' --output text)
  if [ -n "$policies" ]; then
    echo "Inline policies on user $user: $policies"
  fi
done

MFA Enforcement

### List users without MFA
aws iam get-credential-report --output text | \
  awk -F, 'NR>1 && $4=="false" {print $1}'

### Check if MFA is required in policies
aws iam list-policies --scope Local --query 'Policies[*].Arn' --output text | \
while read arn; do
  version=$(aws iam get-policy --policy-arn "$arn" \
    --query 'Policy.DefaultVersionId' --output text)
  doc=$(aws iam get-policy-version --policy-arn "$arn" \
    --version-id "$version" --query 'PolicyVersion.Document')
  if echo "$doc" | grep -q "aws:MultiFactorAuthPresent"; then
    echo "MFA enforced in: $arn"
  fi
done

### Enable MFA for a user (returns QR code)
aws iam create-virtual-mfa-device \
  --virtual-mfa-device-name user-mfa \
  --outfile /tmp/qr.png \
  --bootstrap-method QRCodePNG

Access Key Management

### Find old access keys (>90 days)
aws iam list-users --query 'Users[*].UserName' --output text | \
while read user; do
  aws iam list-access-keys --user-name "$user" \
    --query 'AccessKeyMetadata[*].[AccessKeyId,CreateDate,Status]' \
    --output text | \
  while read key_id create_date status; do
    age_days=$(( ($(date +%s) - $(date -d "$create_date" +%s)) / 86400 ))
    if [ $age_days -gt 90 ]; then
      echo "$user: Key $key_id is $age_days days old"
    fi
  done
done

### Rotate access key
OLD_KEY="<AWS_ACCESS_KEY_ID>"
USER="myuser"

### Create new key
NEW_KEY=$(aws iam create-access-key --user-name "$USER")
echo "New key created. Update applications, then run:"
echo "aws iam delete-access-key --user-name $USER --access-key-id $OLD_KEY"

### Deactivate old key (test first)
aws iam update-access-key \
  --user-name "$USER" \
  --access-key-id "$OLD_KEY" \
  --status Inactive

Role and Policy Analysis

### List unused roles (no activity in 90 days)
aws iam list-roles --query 'Roles[*].[RoleName,RoleLastUsed.LastUsedDate]' \
  --output text | \
while read role last_used; do
  if [ "$last_used" = "None" ]; then
    echo "Never used: $role"
  fi
done

### Find roles with trust relationships to external accounts
aws iam list-roles --query 'Roles[*].RoleName' --output text | \
while read role; do
  trust=$(aws iam get-role --role-name "$role" \
    --query 'Role.AssumeRolePolicyDocument')
  if echo "$trust" | grep -q '"AWS":'; then
    echo "External trust: $role"
  fi
done

### Analyze policy permissions
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/myuser \
  --action-names s3:GetObject s3:PutObject \
  --resource-arns arn:aws:s3:::mybucket/*

IAM Policy Templates

Least Privilege S3 Access

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/user-data/${aws:username}/*"
    },
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::my-bucket",
      "Condition": {
        "StringLike": {
          "s3:prefix": "user-data/${aws:username}/*"
        }
      }
    }
  ]
}

MFA-Required Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

Time-Based Access

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ec2:*",
      "Resource": "*",
      "Condition": {
        "DateGreaterThan": {
          "aws:CurrentTime": "2026-01-01T00:00:00Z"
        },
        "DateLessThan": {
          "aws:CurrentTime": "2026-12-31T23:59:59Z"
        }
      }
    }
  ]
}

IP-Restricted Access

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "198.51.100.0/24"
          ]
        }
      }
    }
  ]
}

IAM Hardening Checklist

User Management

  • Enable MFA for all users
  • Remove unused IAM users
  • Rotate access keys every 90 days
  • Use IAM roles instead of long-term credentials
  • Implement password policy (length, complexity, rotation)

Policy Management

  • Replace inline policies with managed policies
  • Remove wildcard (*) permissions
  • Implement least privilege
  • Use policy conditions (MFA, IP, time)
  • Regular policy reviews

Role Management

  • Use roles for EC2 instances
  • Implement cross-account roles properly
  • Review trust relationships
  • Remove unused roles
  • Use session tags for fine-grained access

Monitoring

  • Enable CloudTrail for IAM events
  • Set up CloudWatch alarms for IAM changes
  • Use AWS IAM Access Analyzer
  • Regular access reviews
  • Monitor for privilege escalation

Automated IAM Hardening

#!/usr/bin/env python3
### iam-hardening.py

import boto3
from datetime import datetime, timedelta

iam = boto3.client('iam')

def enforce_mfa():
    """Identify users without MFA"""
    users = iam.list_users()['Users']
    no_mfa = []
    
    for user in users:
        mfa_devices = iam.list_mfa_devices(
            UserName=user['UserName']
        )['MFADevices']
        
        if not mfa_devices:
            no_mfa.append(user['UserName'])
    
    return no_mfa

def rotate_old_keys():
    """Find access keys older than 90 days"""
    users = iam.list_users()['Users']
    old_keys = []
    
    for user in users:
        keys = iam.list_access_keys(
            UserName=user['UserName']
        )['AccessKeyMetadata']
        
        for key in keys:
            age = datetime.now(key['CreateDate'].tzinfo) - key['CreateDate']
            if age.days > 90:
                old_keys.append({
                    'user': user['UserName'],
                    'key_id': key['AccessKeyId'],
                    'age_days': age.days
                })
    
    return old_keys

def find_overpermissive_policies():
    """Find policies with wildcard actions"""
    policies = iam.list_policies(Scope='Local')['Policies']
    overpermissive = []
    
    for policy in policies:
        version = iam.get_policy_version(
            PolicyArn=policy['Arn'],
            VersionId=policy['DefaultVersionId']
        )
        
        doc = version['PolicyVersion']['Document']
        for statement in doc.get('Statement', []):
            if statement.get('Action') == '*':
                overpermissive.append(policy['PolicyName'])
                break
    
    return overpermissive

if __name__ == "__main__":
    print("IAM Hardening Report")
    print("=" * 50)
    
    print("\nUsers without MFA:")
    for user in enforce_mfa():
        print(f"  - {user}")
    
    print("\nOld access keys (>90 days):")
    for key in rotate_old_keys():
        print(f"  - {key['user']}: {key['age_days']} days")
    
    print("\nOverpermissive policies:")
    for policy in find_overpermissive_policies():
        print(f"  - {policy}")

Example Prompts

  • "Review my IAM policies for security issues"
  • "Find users without MFA enabled"
  • "Create a least privilege policy for S3 access"
  • "Identify overly permissive IAM roles"
  • "Generate an IAM hardening report"

Best Practices

  • Use AWS managed policies when possible
  • Implement policy versioning
  • Test policies in non-production first
  • Document policy purposes
  • Regular access reviews (quarterly)
  • Use IAM Access Analyzer
  • Implement SCPs for organization-wide controls

Kiro CLI Integration

kiro-cli chat "Use aws-iam-best-practices to review my IAM setup"
kiro-cli chat "Create a least privilege policy with aws-iam-best-practices"

Additional Resources

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.