diff --git a/local-app/python-tools/cross-organization/assess_check_cloudtrail.py b/local-app/python-tools/cross-organization/assess_check_cloudtrail.py index 30a82b3f..568ee6d5 100755 --- a/local-app/python-tools/cross-organization/assess_check_cloudtrail.py +++ b/local-app/python-tools/cross-organization/assess_check_cloudtrail.py @@ -15,7 +15,7 @@ def find_latest_file(pattern): return max(files, key=os.path.getctime) if files else None def main(): - parser = argparse.ArgumentParser(description="AWS CloudTrail Audit Assessor - ARN Edition") + parser = argparse.ArgumentParser(description="AWS CloudTrail Audit Assessor") parser.add_argument("--input", help="JSON file (default: latest audit_results.check_cloudtrail.*.json)") args = parser.parse_args() @@ -30,51 +30,45 @@ def main(): print(f"Error: {e}"); sys.exit(1) org_id = data[0].get("org_id", "Unknown") if data else "Unknown" + account_count = len(data) - # EXPANDED COLUMN WIDTHS - # Account(15) | OU(30) | Global(25) | Active(15) | Resource(100) - report_width = 230 + report_width = 240 print("-" * report_width) print(f"CLOUDTRAIL ARN ASSESSMENT REPORT | Org ID: {org_id} | Input: {os.path.basename(input_file)}") print("-" * report_width) - print(f"{'Account ID':<15} | {'OU Path':<30} | {'Global Summary':<25} | {'Active/Stopped':<15} | {'Resource (ARN)'}") + # Added Idx Column to Header + print(f"{'Idx':<5} | {'Account ID':<15} | {'OU Path':<35} | {'Global Summary':<25} | {'Active/Stopped':<15} | {'Resource (ARN)'}") print("-" * report_width) stats = { "s3_bytes": 0, "s3_objects": 0, "s3_buckets": set(), "cw_bytes": 0, "cw_group_arns": set(), - "sns_topics": set(), "kms_cmk_count": 0, "sse_s3_count": 0, "logging_active": 0, "logging_stopped": 0, - "total_shadow_regions": 0, - "total_home_regions": 0 + "total_shadow_regions": 0, "total_home_regions": 0 } retention_distribution = Counter() - for account in data: + # Added enumerate to track Index + for idx, account in enumerate(data, 1): acc_id = account.get("account_id") ou_path = account.get("ou_path", "Root") checks = account.get("data", {}) summary = checks.get("account_summary", {}).get("_summary", "UNKNOWN") - acc_active, acc_stopped = 0, 0 - - # Since one account can have multiple trails, we iterate them for key, val in checks.items(): if key == "account_summary": continue if ":" not in key: continue - + + trail_arn = val.get("resource") stats["total_home_regions"] += 1 stats["total_shadow_regions"] += int(val.get("shadow_region_count", 0)) - trail_arn = val.get("resource") # Now contains the full ARN - if val.get("is_logging") == "True": - acc_active += 1; stats["logging_active"] += 1 + stats["logging_active"] += 1 else: - acc_stopped += 1; stats["logging_stopped"] += 1 + stats["logging_stopped"] += 1 - # Metric Aggregation bucket = val.get("s3_bucket") if bucket and bucket != "N/A": stats["s3_buckets"].add(bucket) @@ -86,20 +80,17 @@ def main(): stats["cw_bytes"] += val["cw_logs_size_bytes"] retention_distribution[val.get("cw_logs_retention_days", "Never Expire")] += 1 - # Print row for each trail found - print(f"{acc_id:<15} | {ou_path[:30]:<30} | {summary:<25} | {val.get('is_logging'):<15} | {trail_arn:<100}") + # Print row with Index + print(f"{idx:<5} | {acc_id:<15} | {ou_path[:35]:<35} | {summary:<25} | {val.get('is_logging'):<15} | {trail_arn:<100}") - # FOOTPRINT SUMMARY s3_gb, cw_gb = stats["s3_bytes"] / (1024**3), stats["cw_bytes"] / (1024**3) print("-" * report_width) - print(f"ORGANIZATION FOOTPRINT SUMMARY | Org ID: {org_id}") - print(f" Logging Status: {stats['logging_active']} Active | {stats['logging_stopped']} Stopped") - print(f" S3 Storage: {s3_gb:.2f} GB | {stats['s3_objects']:,} objects") - print(f" CloudWatch Logs: {cw_gb:.2f} GB | {len(stats['cw_group_arns'])} unique groups") - print(f" Primary (Home) Trails: {stats['total_home_regions']}") - print(f" Shadow Regions Covered: {stats['total_shadow_regions']}") - print(f" Total Regional Presence: {stats['total_home_regions'] + stats['total_shadow_regions']}") + print(f"ORGANIZATION CLOUDTRAIL FOOTPRINT SUMMARY | Org ID: {org_id}") + print(f" Total Accounts Found: {account_count}") # Added Account Count + print(f" Primary (Home) Trails: {stats['total_home_regions']}") + print(f" Shadow Regions Covered: {stats['total_shadow_regions']}") + print(f" S3 Storage Total: {s3_gb:.2f} GB") print("-" * report_width) if __name__ == "__main__": diff --git a/local-app/python-tools/cross-organization/assess_check_config.py b/local-app/python-tools/cross-organization/assess_check_config.py index 0d4ebbf6..b218d814 100755 --- a/local-app/python-tools/cross-organization/assess_check_config.py +++ b/local-app/python-tools/cross-organization/assess_check_config.py @@ -8,10 +8,9 @@ import glob # --- VERSIONING --- -__version__ = "1.0.10" +__version__ = "1.0.11" def find_latest_file(pattern): - """Searches for the most recent file matching the pattern.""" files = glob.glob(pattern) return max(files, key=os.path.getctime) if files else None @@ -32,12 +31,14 @@ def main(): print(f"Error reading {input_file}: {e}"); sys.exit(1) org_id = data[0].get("org_id", "Unknown") if data else "Unknown" + account_count = len(data) - print("-" * 140) + print("-" * 150) print(f"AWS CONFIG ASSESSMENT | Org ID: {org_id} | Pattern: {args.central_bucket_regex}") - print("-" * 140) - print(f"{'Account ID':<15} | {'OU Path':<30} | {'Global Status':<12} | {'S3 Compliance'}") - print("-" * 140) + print("-" * 150) + # Added Index Column to Header + print(f"{'Idx':<5} | {'Account ID':<15} | {'OU Path':<35} | {'Global Status':<12} | {'S3 Compliance'}") + print("-" * 150) stats = { "objects": 0, "size_bytes": 0, @@ -45,11 +46,11 @@ def main(): "config_count_central": 0, "config_count_non_central": 0, "unique_central_buckets": set(), - "unique_non_central_buckets": set(), - "accounts": len(data) + "unique_non_central_buckets": set() } - for account in data: + # Added enumerate to track Index + for idx, account in enumerate(data, 1): acc_id = account.get("account_id") ou_path = account.get("ou_path", "Root") checks = account.get("data", {}) @@ -58,8 +59,6 @@ def main(): s3_issues = [] for reg, reg_data in checks.items(): if reg == "account_summary": continue - - # Logic update: Verify resource type if reg_data.get("resource") != "config": continue if reg_data.get("recorder_status") == "ON": @@ -79,16 +78,18 @@ def main(): s3_issues.append(bucket) s3_status = "NON_COMPLIANT" if s3_issues else "COMPLIANT" - print(f"{acc_id:<15} | {ou_path[:30]:<30} | {summary:<12} | {s3_status}") + # Print with Index + print(f"{idx:<5} | {acc_id:<15} | {ou_path[:35]:<35} | {summary:<12} | {s3_status}") size_gb = stats["size_bytes"] / (1024**3) - print("-" * 140) + print("-" * 150) print(f"ORGANIZATION FOOTPRINT SUMMARY (CONFIG) | Org ID: {org_id}") + print(f" Total Accounts Found: {account_count}") # Added Account Count print(f" Active Recorders Found: {stats['total_recorders']}") print(f" Total S3 Storage: {size_gb:.2f} GB") print(f" Configs using Central: {stats['config_count_central']}") print(f" Configs using Non-Central:{stats['config_count_non_central']}") - print("-" * 140) + print("-" * 150) if __name__ == "__main__": main()