ThoughtRoutes Logo
Back to all posts
Guides / Tutorial

Analyzing AWS IAM Users: Access Key and Password Age

Vishnu KS

Vishnu KS

Author

Published on February 23, 2026
Analyzing AWS IAM Users: Access Key and Password Age


Introduction

Security is a top priority in cloud computing, and managing IAM (Identity and Access Management) users effectively is crucial. In this blog post, we’ll explore a Python script designed to generate a report on AWS IAM users, detailing information about their access keys and password age. This report can be invaluable for security audits, ensuring the timely rotation of credentials and identifying potential vulnerabilities.

Prerequisites

Before we delve into the script, make sure you have the following:

  • AWS account with appropriate IAM permissions
  • Python installed on your machine
  • Boto3 library installed (pip install boto3)

The Python Script
import boto3
from datetime import datetime, timedelta, timezone
import csv
from io import StringIO
import time

def strfdelta(tdelta, fmt):
    d = {"days": tdelta.days}
    d["hours"], rem = divmod(tdelta.seconds, 3600)
    d["minutes"], d["seconds"] = divmod(rem, 60)
    return fmt.format(**d)

def get_access_key_age(access_key_last_rotated):
    current_time = datetime.now(timezone.utc)
    age = current_time - access_key_last_rotated
    return strfdelta(age, "{days} days, {hours:02}:{minutes:02}:{seconds:02}")

def calculate_password_age(password_last_changed):
    try:
        # Convert the timestamp string to a datetime object (offset-aware)
        last_changed_time = datetime.strptime(password_last_changed, "%Y-%m-%dT%H:%M:%S%z")

        # Make current_time offset-aware using UTC timezone
        current_time = datetime.utcnow().replace(tzinfo=timezone.utc)

        # Calculate the password age in days
        age_in_days = (current_time - last_changed_time).total_seconds() / (60 * 60 * 24)

        return age_in_days
    except ValueError as e:
        # Handle the case where the timestamp format is unexpected
        print(f"Error parsing timestamp: {e}")
        return None

def get_password_last_used(password_last_used):
    if password_last_used:
        return f"Password Last Used: {password_last_used}"
    else:
        return "Password Last Used: Never"

def generate_iam_report():
    # Create an IAM client
    iam_client = boto3.client('iam')

    # Get IAM users
    response = iam_client.list_users()
    users = response['Users']

    # Generate IAM credentials report
    response = iam_client.generate_credential_report()

    # Sleep and retry until the report is generated (maximum 60 seconds)
    max_retries = 6
    for _ in range(max_retries):
        try:
            # Download IAM credentials report
            report_response = iam_client.get_credential_report()
            break
        except iam_client.exceptions.ReportInProgressException:
            time.sleep(10)
    else:
        return {
            'statusCode': 500,
            'body': 'Timed out waiting for credential report to be generated.'
        }

    # Loop through each IAM user
    for user in users:
        user_name = user['UserName']

        # Get IAM access keys for the user
        access_keys = iam_client.list_access_keys(UserName=user_name)['AccessKeyMetadata']

        # Get information about the user's password
        password_last_used = user.get('PasswordLastUsed', None)

        # Print information about each access key and password
        for access_key in access_keys:
            access_key_id = access_key['AccessKeyId']
            access_key_last_rotated = access_key['CreateDate']

            # Calculate access key age
            access_key_age = get_access_key_age(access_key_last_rotated)

            # Parse the CSV report to get the password_last_changed field
            password_last_changed = None
            for row in csv.reader(StringIO(report_response['Content'].decode('utf-8'))):
                if row[0] == user_name:
                    password_last_changed = row[5] if len(row) > 5 else None  # Assuming the column index for 'password_last_changed'
                    break

            if password_last_changed and password_last_changed != 'N/A':
                password_age_days = calculate_password_age(password_last_changed)
                print(f"IAM User: {user_name}")
                print(f"Access Key ID: {access_key_id}")
                print(f"Access Key Age: {access_key_age}")
                print(f"Password Age (in days): {password_age_days}")
                print(get_password_last_used(password_last_used))
                print("")
            else:
                print(f"IAM User: {user_name}")
                print(f"Access Key ID: {access_key_id}")
                print(f"Access Key Age: {access_key_age}")
                print("Password Age: N/A (Not Available)")
                print(get_password_last_used(password_last_used))
                print("")

if __name__ == "__main__":
    generate_iam_report()

Conclusion

This Python script simplifies the process of generating an IAM report, providing insights into access key age and password information. Regularly running this script can help you maintain a secure AWS environment by ensuring that access keys are rotated promptly, and password policies are adhered to.

Feel free to adapt and extend the script to suit your specific IAM reporting needs. AWS provides a powerful set of IAM tools, and understanding user credentials is fundamental to maintaining a robust security posture in the cloud.