Mohammad Ali

Business & Digital Consultant

IT & Cloud Consultant

Career Growth Mentor

Sales & Strategy Advisor

0

No products in the cart.

Mohammad Ali

Business & Digital Consultant

IT & Cloud Consultant

Career Growth Mentor

Sales & Strategy Advisor

Blog Post

Automating AWS IAM User Access Reviews with Python and Boto3

September 9, 2026 Uncategorized

Introduction

Manually reviewing AWS IAM (Identity and Access Management) user permissions is essential for cloud security, but quickly becomes tedious, error-prone, and time-consuming as your environment grows. Over time, privilege creep sets in, potentially exposing sensitive resources to unnecessary risk. Automating AWS IAM access reviews helps teams maintain least-privilege policies, uncover unused permissions, and remain compliant without burning out security or DevOps teams.

This tutorial demonstrates how to automate AWS IAM user access reviews using Python and Boto3. You will build a practical script that identifies inactive users or permissions, generates actionable reports, and lays the foundation for improving IAM security posture with minimal manual effort.

What Is IAM Access Review Automation?

An IAM access review evaluates which users have access to which AWS resources, determining whether permissions are appropriate, necessary, or stale. Automating this process with AWS IAM automation enables regular, consistent reviews without manual oversight, reducing the risk of privilege creep and improving security.

With Python Boto3 IAM review scripts, you can systematically collect user activity data, compare it against assigned permissions, and highlight unused accounts or privileges for remediation. Integrating such automation into your regular IAM security audit routine is a DevSecOps best practice.

Prerequisites

  • Basic familiarity with Python (3.7+ recommended)
  • pip (Python package installer)
  • AWS CLI configured with sufficient permissions (at least iam:ListUsers, iam:ListAttachedUserPolicies, iam:ListAccessKeys, iam:GetUser, iam:GenerateServiceLastAccessedDetails), and optionally iam:ListGroupsForUser
  • An AWS account with IAM users set up
  • Access to a terminal/command shell

Step-by-Step Implementation

1. Install the Required Python Packages

Ensure you have Boto3 installed—the official AWS SDK for Python:

pip install boto3

If you haven’t configured your AWS credentials yet, do so using:

aws configure

This sets up your credentials and default region for Boto3.

2. Review the Automation Workflow

The script will:

  1. List all IAM users in your AWS account.
  2. Retrieve each user’s attached managed and inline policies.
  3. Obtain the last authentication and access key usage data.
  4. Optionally, use AWS’s generate_service_last_accessed_details API to discover service-level usage per user.
  5. Identify users and permissions that are inactive for a specified threshold (e.g., 90 days).
  6. Generate a CSV or printed report highlighting inactive users, unused policies, and potential actions.

3. Sample Python Script for Automated IAM Access Review

The following script walks through the process described above. It checks for users who haven’t logged in or used their access keys within a defined period (e.g., 90 days), as well as runs AWS’s service last-accessed APIs to identify unused permissions.

import boto3
from datetime import datetime, timezone, timedelta
import csv

# Change this to your review period threshold (e.g., 90 days)
INACTIVE_DAYS_THRESHOLD = 90

iam = boto3.client('iam')

def list_iam_users():
    paginator = iam.get_paginator('list_users')
    users = []
    for page in paginator.paginate():
        users.extend(page['Users'])
    return users

def get_last_console_login(username):
    try:
        response = iam.get_user(UserName=username)
        return response['User'].get('PasswordLastUsed')
    except Exception:
        return None

def list_user_access_keys(username):
    response = iam.list_access_keys(UserName=username)
    return response['AccessKeyMetadata']

def get_access_key_last_used(access_key_id):
    response = iam.get_access_key_last_used(AccessKeyId=access_key_id)
    return response['AccessKeyLastUsed'].get('LastUsedDate')

def list_attached_policies(username):
    response = iam.list_attached_user_policies(UserName=username)
    return response['AttachedPolicies']

def list_inline_policies(username):
    response = iam.list_user_policies(UserName=username)
    return response['PolicyNames']

def generate_service_last_accessed(username):
    job = iam.generate_service_last_accessed_details(Arn=f"arn:aws:iam::YOUR_ACCOUNT_ID:user/{username}")
    job_id = job['JobId']
    while True:
        status = iam.get_service_last_accessed_details(JobId=job_id)
        if status['JobStatus'] == 'COMPLETED':
            return status['ServicesLastAccessed']
        elif status['JobStatus'] == 'FAILED':
            return []
        # wait before polling again to avoid excessive API calls
        import time
        time.sleep(2)

def main():
    now = datetime.now(timezone.utc)
    inactive_users = []
    summary_report = []

    users = list_iam_users()
    print(f"Found {len(users)} IAM users.")

    for user in users:
        username = user['UserName']
        user_arn = user['Arn']
        created_date = user['CreateDate']
        last_console = get_last_console_login(username)
        access_keys = list_user_access_keys(username)

        latest_activity = created_date

        if last_console:
            latest_activity = max(latest_activity, last_console)
        for access_key in access_keys:
            last_used = get_access_key_last_used(access_key['AccessKeyId'])
            if last_used:
                latest_activity = max(latest_activity, last_used)

        days_inactive = (now - latest_activity).days

        # For deeper policy usage check (optional but provides more granularity)
        # services_last_accessed = generate_service_last_accessed(username)
        # services_unused = [s for s in services_last_accessed if not s.get('LastAuthenticated')]
        
        if days_inactive > INACTIVE_DAYS_THRESHOLD:
            inactive_users.append(username)
            summary_report.append({
                'UserName': username,
                'Arn': user_arn,
                'Created': created_date,
                'LastActivity': latest_activity,
                'DaysInactive': days_inactive,
                'AccessKeys': ','.join([k['AccessKeyId'] for k in access_keys])
            })

    # Write findings to CSV for further action
    fieldnames = ['UserName', 'Arn', 'Created', 'LastActivity', 'DaysInactive', 'AccessKeys']
    with open('iam_inactive_users_report.csv', 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        for row in summary_report:
            writer.writerow(row)

    print(f"\nInactive users (>{INACTIVE_DAYS_THRESHOLD} days):")
    for user in inactive_users:
        print(user)
    print("\nReport written to iam_inactive_users_report.csv")

if __name__ == '__main__':
    main()

4. Explanation of Key Script Parts

  • list_iam_users: Retrieves all IAM users in your AWS account using pagination.
  • get_last_console_login: Gets the last time the user signed in to the AWS console.
  • list_user_access_keys and get_access_key_last_used: Checks when programmatic access was last used.
  • generate_service_last_accessed: (Optional; advanced) Retrieves the last time the user accessed each AWS service, helping identify unused services/permissions. You’ll need to fill in YOUR_ACCOUNT_ID and handle API call quotas.
  • Report generation: The script saves a CSV file containing users who have been inactive longer than your defined threshold.

5. Customization and Enhancements

  • Change INACTIVE_DAYS_THRESHOLD to match your organization’s policy (30, 60, 90 days, etc.)
  • Integrate email/SNS notifications for suspect users or unused access keys
  • Add logic to disable, delete, or quarantine users programmatically (ensure you follow production safety best-practices and backups!)
  • Embed this process into a periodic cron job or CI/CD pipeline for truly hands-off AWS IAM automation

Security and Production Considerations

  • Never auto-delete or disable users without approval: Always review reports before taking irreversible actions.
  • Control script permissions: Run this script only with a secure, audit-friendly IAM user or role. Avoid overly-broad permissions.
  • Monitor API call limits: Some AWS APIs (especially generate_service_last_accessed_details) have call rate restrictions. Batch or stagger requests in larger environments.
  • Store your reports securely: Generated IAM review reports may contain sensitive information.
  • Regularly rotate access keys: Script findings may highlight stale or unused access keys. Implement key rotation policies for better security hygiene.

Troubleshooting and Common Mistakes

  • Missing permissions: If your script fails with AccessDenied errors, ensure your IAM role/user has necessary permissions.
  • Account limits: The service access analysis API has quotas—avoid running too many jobs in parallel in large AWS environments.
  • Timezone confusion: All AWS times are UTC; compare them appropriately to prevent mislabeling users as inactive.
  • Incorrect account ID: When constructing ARNs for generate_service_last_accessed_details, ensure you specify the correct AWS account ID.
  • Incomplete automation: Always review auto-generated reports and verify before making access changes to avoid accidental lockout or privilege loss.

Next Steps and Further Automation

  • Integrate this Python Boto3 IAM review script with your ticketing or SOAR platform to streamline IAM security audit workflows.
  • Extend automation to also review group memberships and attached policies, not just user-level resources.
  • Schedule monthly or quarterly reviews and follow up with regular compliance checks.
  • Explore AWS-native solutions like IAM credential reports for complementary capabilities.

Conclusion

Automating AWS IAM access reviews with Python and Boto3 saves time, reduces human error, and ensures your AWS environment remains secure, compliant, and manageable. By regularly auditing user activity and permissions, engineering teams can confidently implement least-privilege access, spot dormant accounts, and proactively remediate security risks—without tedious spreadsheet exports and manual tracking.

For more DevSecOps tutorials focused on practical, real-world automation, explore other articles on MohammadAli.tech.

Write a comment