Skip to main content

Overview

The tool generates two CSV files with timestamps in the current working directory:

IAM Audit Report

iam_audit_report_YYYYMMDD_HHMMSS.csvComplete inventory of IAM users, access keys, MFA status, and console access

CloudTrail Events

cloudtrail_events_YYYYMMDD_HHMMSS.csvRecent IAM user and access key lifecycle events for remediation tracking

IAM Audit Report CSV

File Structure

Filename: iam_audit_report_YYYYMMDD_HHMMSS.csv
Source: iam_audit.py:176-183
Each row represents one access key for an IAM user. Users with multiple access keys will appear in multiple rows.

CSV Fields

The IAM audit report contains 11 columns extracted from the get_iam_users_with_keys function (iam_audit.py:70-82):
account_id
string
AWS account ID (12 digits)Example: 123456789012
account_name
string
Human-readable account name from AWS OrganizationsExample: Production, Staging, Development
username
string
IAM user nameExample: admin, [email protected], ci-deploy-user
password_status
string
Console password configuration statusValues:
  • Configurada - User has console access enabled
  • No configurada - User does not have console access
Source: iam_audit.py:54-58 (calls GetLoginProfile API)
password_last_used
string
Last time the user signed into the AWS Management ConsoleExample: 2026-03-01 14:23:15+00:00
If never used: Nunca
Source: iam_audit.py:75 (from User.PasswordLastUsed attribute)
access_key_id
string
Access key IDExample: AKIAIOSFODNN7EXAMPLE
This is sensitive information. Protect these CSV files accordingly.
status
string
Access key statusValues:
  • Active - Key is currently active and can be used
  • Inactive - Key is disabled
Source: iam_audit.py:77 (from AccessKeyMetadata.Status)
created_date
string
Date and time when the access key was createdExample: 2023-08-15 10:32:45+00:00Source: iam_audit.py:78 (from AccessKeyMetadata.CreateDate)
Keys older than 90 days should be rotated per AWS security best practices.
last_used_date
string
Last time the access key was used to make an AWS API requestExample: 2026-03-04 08:15:30+00:00
If never used: Nunca utilizada
Source: iam_audit.py:79 (from GetAccessKeyLastUsed API)
service_name
string
AWS service where the access key was last usedExample: s3, ec2, sts, dynamodb
If never used: N/A
Source: iam_audit.py:80 (from GetAccessKeyLastUsed.ServiceName)
mfa_status
string
Multi-factor authentication device status for the userValues:
  • Virtual - Virtual MFA device (authenticator app)
  • Hardware - Hardware MFA device
  • None - No MFA device configured
Source: iam_audit.py:45-52 (from ListMFADevices API)
Users with console access (password_status: Configurada) and mfa_status: None are high-risk.

Example CSV Output

account_id,account_name,username,password_status,password_last_used,access_key_id,status,created_date,last_used_date,service_name,mfa_status
123456789012,Production,admin,Configurada,2026-03-04 09:15:22+00:00,AKIAIOSFODNN7EXAMPLE,Active,2023-08-15 10:32:45+00:00,2026-03-04 08:15:30+00:00,s3,Virtual
123456789012,Production,ci-deploy,No configurada,Nunca,AKIAI44QH8DHBEXAMPLE,Active,2024-01-20 14:22:10+00:00,2026-03-05 07:45:12+00:00,sts,None
234567890123,Staging,developer,Configurada,2026-02-28 16:45:33+00:00,AKIAIOSFODNN8EXAMPLE,Inactive,2022-11-03 08:12:55+00:00,Nunca utilizada,N/A,None

CloudTrail Events CSV

File Structure

Filename: cloudtrail_events_YYYYMMDD_HHMMSS.csv
Source: iam_audit.py:200-208
Each row represents one CloudTrail event related to IAM user or access key lifecycle.

CSV Fields

The CloudTrail events report contains 6 columns extracted from the get_cloudtrail_events function (iam_audit.py:109-116):
eventTime
string
Timestamp when the event occurredExample: 2026-02-28 15:32:10+00:00
eventName
string
Name of the IAM API actionTracked Events:
  • CreateUser
  • DeleteUser
  • CreateAccessKey
  • DeleteAccessKey
  • DeleteLoginProfile
Source: iam_audit.py:88 (hardcoded list)
username
string
IAM principal who performed the actionExample: [email protected], AWSControlTowerExecution
If unavailable: N/A
Source: iam_audit.py:112 (from Event.Username)
account_id
string
AWS account ID where the event occurredExample: 123456789012
account_name
string
Human-readable account nameExample: Production, Staging
resources
string
Comma-separated list of affected resource namesExample: AKIAIOSFODNN7EXAMPLE, contractor-tempSource: iam_audit.py:207 (converted from array to comma-separated string)

Example CSV Output

eventTime,eventName,username,account_id,account_name,resources
2026-02-28 15:32:10+00:00,CreateAccessKey,[email protected],123456789012,Production,AKIAIOSFODNN7EXAMPLE
2026-03-01 09:15:43+00:00,DeleteUser,security-admin,234567890123,Staging,contractor-temp
2026-03-02 11:22:35+00:00,DeleteAccessKey,automated-rotation,123456789012,Production,AKIAI44QH8DHBEXAMPLE

Time Range

CloudTrail events are queried from February 18, 2026 to the current date. This is hardcoded at iam_audit.py:137. To change the time range, modify the source code.

Analyzing the Reports

Using Excel

1

Open the CSV file

Open the IAM audit report in Excel or Google Sheets
2

Apply filters

Enable AutoFilter on the header row
3

Sort by created_date

Identify the oldest access keys
4

Filter by mfa_status = None

Find users without MFA

Using Python (pandas)

import pandas as pd
from datetime import datetime, timedelta

# Load the IAM audit report
df = pd.read_csv('iam_audit_report_20260305_143022.csv')

# Convert date strings to datetime
df['created_date'] = pd.to_datetime(df['created_date'])
df['last_used_date'] = pd.to_datetime(df['last_used_date'], errors='coerce')

# Find keys older than 90 days
old_threshold = datetime.now() - timedelta(days=90)
old_keys = df[df['created_date'] < old_threshold]

print(f"Access keys older than 90 days: {len(old_keys)}")

# Find users with console access but no MFA
no_mfa = df[(df['password_status'] == 'Configurada') & (df['mfa_status'] == 'None')]
print(f"Users with console access and no MFA: {len(no_mfa['username'].unique())}")

# Find keys never used
never_used = df[df['last_used_date'] == 'Nunca utilizada']
print(f"Access keys never used: {len(never_used)}")

Using AWS Athena

1

Upload CSV to S3

aws s3 cp iam_audit_report_*.csv s3://your-bucket/iam-audit/
2

Create Athena table

CREATE EXTERNAL TABLE iam_audit (
  account_id STRING,
  account_name STRING,
  username STRING,
  password_status STRING,
  password_last_used STRING,
  access_key_id STRING,
  status STRING,
  created_date STRING,
  last_used_date STRING,
  service_name STRING,
  mfa_status STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 's3://your-bucket/iam-audit/'
TBLPROPERTIES ('skip.header.line.count'='1');
3

Query the data

SELECT account_name, username, created_date
FROM iam_audit
WHERE status = 'Active'
  AND CAST(created_date AS DATE) < DATE_ADD('day', -90, CURRENT_DATE)
ORDER BY created_date;

Common Security Patterns to Look For

High-Risk Findings

Query:
SELECT account_name, username, access_key_id, created_date
FROM iam_audit
WHERE status = 'Active'
  AND DATEDIFF(CURRENT_DATE, CAST(created_date AS DATE)) > 90
ORDER BY created_date;
Risk: Long-lived credentials increase the window for compromise.Remediation: Implement automated key rotation or migrate to temporary credentials (IAM roles).
Query:
SELECT DISTINCT account_name, username
FROM iam_audit
WHERE password_status = 'Configurada'
  AND mfa_status = 'None';
Risk: Credentials can be compromised without MFA protection.Remediation: Enforce MFA via IAM policies or SCPs.
Query:
SELECT account_name, username, access_key_id, created_date
FROM iam_audit
WHERE last_used_date = 'Nunca utilizada'
  AND status = 'Active';
Risk: Unused keys may have been created for testing and forgotten.Remediation: Delete unused keys after 30 days.
Query:
SELECT DISTINCT account_name, username
FROM iam_audit
WHERE password_status = 'Configurada'
  AND password_last_used = 'Nunca';
Risk: Dormant accounts that may not be monitored.Remediation: Review and delete or disable unused accounts.

Compliance Checks

ControlQuery
AWS CIS 1.4: Ensure access keys are rotated every 90 daysFilter created_date older than 90 days
AWS CIS 1.2: Ensure MFA is enabled for all IAM users with console passwordFilter password_status = 'Configurada' AND mfa_status = 'None'
NIST 800-53 IA-5: Credential managementIdentify keys not used in 45 days

Tracking Remediation with CloudTrail Events

Use the CloudTrail events CSV to track key deletions and user removals:
import pandas as pd

# Load CloudTrail events
ct = pd.read_csv('cloudtrail_events_20260305_143022.csv')

# Count remediation actions
remediations = ct[ct['eventName'].isin(['DeleteAccessKey', 'DeleteUser'])]
print(f"Remediation actions since Feb 18: {len(remediations)}")

# Group by account
by_account = remediations.groupby('account_name')['eventName'].value_counts()
print(by_account)
Example Output:
Remediation actions since Feb 18: 8

account_name  eventName       
Production    DeleteAccessKey    5
              DeleteUser         1
Staging       DeleteAccessKey    2

Data Retention and Security

These CSV files contain sensitive information:
  • Access key IDs (can be used to identify credentials)
  • Account IDs and names
  • User names and login patterns
Recommendations:
  1. Store reports in encrypted S3 buckets
  2. Restrict access with IAM policies
  3. Delete local copies after analysis
  4. Do not commit CSV files to version control

Next Steps

Running an Audit

Run another audit to track progress

Setup Guide

Configure audit roles in more accounts

Build docs developers (and LLMs) love