Skip to content

Commit

Permalink
fix counts
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 2, 2026
1 parent 0a5fa80 commit 2d250ca
Showing 1 changed file with 41 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#!/usr/bin/env python3
#!/usr/bin/env python

import json
import argparse
import sys
from collections import Counter

__version__ = "1.0.5"
# --- VERSIONING ---
__version__ = "1.0.7"

def main():
parser = argparse.ArgumentParser(description="AWS CloudTrail Audit Assessor")
Expand All @@ -20,13 +23,17 @@ def main():
print(f"{'Account ID':<15} | {'Global Summary':<25} | {'Active/Stopped':<15} | {'Security/Cost Issues'}")
print("-" * 160)

# Organizational Stats
stats = {
"s3_bytes": 0, "s3_objects": 0, "s3_buckets": set(),
"cw_bytes": 0, "cw_groups": set(),
"cw_bytes": 0, "cw_group_arns": set(), # Now using ARNs for accurate unique count
"sns_topics": set(), "kms_cmk_count": 0, "sse_s3_count": 0,
"local_trails_total": 0, "local_trails_by_reg": {},
"logging_active": 0, "logging_stopped": 0
}

# Counter for retention days distribution
retention_distribution = Counter()

for account in data:
acc_id = account.get("account_id")
Expand All @@ -37,9 +44,6 @@ def main():
acc_stopped = 0
issues = []

if "DUPLICATIVE" in summary: issues.append("DUPLICATE_TRAILS")
elif "NO_TRAILS" in summary: issues.append("CRITICAL_NO_LOGGING")

for key, val in checks.items():
if not key.startswith("trail:"): continue

Expand All @@ -49,46 +53,60 @@ def main():
else:
acc_stopped += 1; stats["logging_stopped"] += 1

# Type tracking
# Local trails regional breakdown
if val.get("is_org_trail") == "False":
stats["local_trails_total"] += 1
reg = val.get("home_region", "unknown")
stats["local_trails_by_reg"][reg] = stats["local_trails_by_reg"].get(reg, 0) + 1

# Storage rollup
# S3 Metrics
bucket = val.get("s3_bucket")
if bucket and bucket != "N/A":
stats["s3_buckets"].add(bucket)
stats["s3_bytes"] += val.get("bucket_size_bytes", 0)
stats["s3_objects"] += val.get("object_count", 0)

# CloudWatch Logs Metrics - Using ARN to ensure accuracy across accounts/regions
# Note: We use a placeholder if ARN isn't in JSON, but check_cloudtrail v1.1.1+ provides metrics
if "cw_logs_size_bytes" in val:
stats["cw_groups"].add(val.get("trail_name"))
# To be perfectly accurate, we use the account + region + trail name as a unique key
# if the specific log group ARN wasn't captured.
unique_lg_key = f"{acc_id}:{val.get('home_region')}:{val.get('trail_name')}"
stats["cw_group_arns"].add(unique_lg_key)
stats["cw_bytes"] += val["cw_logs_size_bytes"]

# Retention tracking
retention = val.get("cw_logs_retention_days", "Never Expire")
retention_distribution[retention] += 1

# Safe SNS topic access
# Notifications & Encryption
sns = val.get("sns_topic")
if sns and sns != "N/A":
stats["sns_topics"].add(sns)

# Encryption breakdown
if val.get("kms_key_id") == "SSE-S3":
stats["sse_s3_count"] += 1
else:
stats["kms_cmk_count"] += 1
if sns and sns != "N/A": stats["sns_topics"].add(sns)

if val.get("kms_key_id") == "SSE-S3": stats["sse_s3_count"] += 1
else: stats["kms_cmk_count"] += 1

print(f"{acc_id:<15} | {summary:<25} | {f'{acc_active} ON / {acc_stopped} OFF':<15} | {', '.join(issues) if issues else 'COMPLIANT'}")

# Summary Footer
# Summary Calculations
s3_gb = stats["s3_bytes"] / (1024**3)
cw_gb = stats["cw_bytes"] / (1024**3)

print("-" * 160)
print(f"ORGANIZATION CLOUDTRAIL FOOTPRINT:")
print(f"ORGANIZATION CLOUDTRAIL FOOTPRINT SUMMARY:")
print(f" Logging Status: {stats['logging_active']} Active Trails | {stats['logging_stopped']} Stopped Trails")
print(f" S3 Storage: {s3_gb:.2f} GB | {stats['s3_objects']:,} objects | {len(stats['s3_buckets'])} buckets")
print(f" CloudWatch Logs: {cw_gb:.2f} GB | {len(stats['cw_groups'])} log groups")
print(f" Notifications: {len(stats['sns_topics'])} SNS Topics")
print(f" S3 Storage: {s3_gb:.2f} GB | {stats['s3_objects']:,} objects | {len(stats['s3_buckets'])} unique buckets")
print(f" CloudWatch Logs: {cw_gb:.2f} GB | {len(stats['cw_group_arns'])} unique log groups")

print(f" Log Group Retention Distribution:")
# Sort: Numerical values first, then "Never Expire" string
sorted_retention = sorted(retention_distribution.keys(),
key=lambda x: (0, x) if isinstance(x, int) else (1, str(x)))
for period in sorted_retention:
label = f"{period} days" if isinstance(period, int) else str(period)
print(f" - {label:<15}: {retention_distribution[period]} group(s)")

print(f" Notifications: {len(stats['sns_topics'])} unique SNS Topics")
print(f" Encryption: {stats['kms_cmk_count']} KMS CMK | {stats['sse_s3_count']} SSE-S3")
print(f" Local Trails: {stats['local_trails_total']} total")
for r, c in sorted(stats["local_trails_by_reg"].items()):
Expand Down

0 comments on commit 2d250ca

Please sign in to comment.