Skip to content

Commit

Permalink
restore footers
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 2, 2026
1 parent 4e21bee commit b092738
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import glob
from collections import Counter

__version__ = "1.0.9"
# --- VERSIONING ---
__version__ = "1.1.0"

def find_latest_file(pattern):
files = glob.glob(pattern)
Expand All @@ -19,13 +20,9 @@ def main():
parser.add_argument("--central-bucket-regex", default=".*", help="Regex for central bucket")
args = parser.parse_args()

input_file = args.input
input_file = args.input or find_latest_file("audit_results.check_cloudtrail.*.json")
if not input_file:
input_file = find_latest_file("audit_results.check_cloudtrail.*.json")
if not input_file:
print("Error: No input file provided and no recent check_cloudtrail JSON found.")
sys.exit(1)
print(f"Auto-discovered latest input: {input_file}")
print("Error: No input file found."); sys.exit(1)

try:
with open(input_file, 'r') as f:
Expand All @@ -35,17 +32,16 @@ def main():

org_id = data[0].get("org_id", "Unknown") if data else "Unknown"

print("-" * 160)
print(f"CLOUDTRAIL ASSESSMENT REPORT | Organization: {org_id}")
print("-" * 160)
print(f"{'Account ID':<15} | {'OU Path':<25} | {'Global Summary':<25} | {'Active/Stopped':<15} | {'Security Issues'}")
print("-" * 160)
print("-" * 180)
print(f"CLOUDTRAIL ASSESSMENT REPORT | Org ID: {org_id} | Input: {os.path.basename(input_file)}")
print("-" * 180)
print(f"{'Account ID':<15} | {'OU Path':<30} | {'Global Summary':<25} | {'Active/Stopped':<15} | {'Security Issues'}")
print("-" * 180)

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,
"local_trails_total": 0, "local_trails_by_reg": {},
"logging_active": 0, "logging_stopped": 0
}

Expand Down Expand Up @@ -73,6 +69,7 @@ def main():
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)
if not re.match(args.central_bucket_regex, bucket):
issues.append(f"Non-Central:{bucket}")

Expand All @@ -83,12 +80,30 @@ def main():
retention = val.get("cw_logs_retention_days", "Never Expire")
retention_distribution[retention] += 1

print(f"{acc_id:<15} | {ou_path[:25]:<25} | {summary:<25} | {f'{acc_active} ON / {acc_stopped} OFF':<15} | {', '.join(issues) if issues else 'COMPLIANT'}")
sns = val.get("sns_topic")
if sns and sns != "N/A": stats["sns_topics"].add(sns)
if val.get("kms_key_id") != "SSE-S3": stats["kms_cmk_count"] += 1
else: stats["sse_s3_count"] += 1

print("-" * 160)
print(f"FOOTPRINT SUMMARY | Org ID: {org_id}")
print(f" S3 Storage: {stats['s3_bytes'] / (1024**3):.2f} GB | CW Storage: {stats['cw_bytes'] / (1024**3):.2f} GB")
print("-" * 160)
print(f"{acc_id:<15} | {ou_path[:30]:<30} | {summary:<25} | {f'{acc_active} ON / {acc_stopped} OFF':<15} | {', '.join(issues) if issues else 'COMPLIANT'}")

# RESTORED SUMMARY SECTION
s3_gb = stats["s3_bytes"] / (1024**3)
cw_gb = stats["cw_bytes"] / (1024**3)

print("-" * 180)
print(f"ORGANIZATION CLOUDTRAIL FOOTPRINT SUMMARY | Org ID: {org_id}")
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'])} unique buckets")
print(f" CloudWatch Logs: {cw_gb:.2f} GB | {len(stats['cw_group_arns'])} unique log groups")
print(f" Log Group Retention Distribution:")
sorted_ret = sorted(retention_distribution.keys(), key=lambda x: (0, x) if isinstance(x, int) else (1, str(x)))
for period in sorted_ret:
label = f"{period} days" if isinstance(period, int) else str(period)
print(f" - {label:<15}: {retention_distribution[period]} group(s)")
print(f" Encryption: {stats['kms_cmk_count']} KMS CMK | {stats['sse_s3_count']} SSE-S3")
print(f" Notifications: {len(stats['sns_topics'])} unique SNS Topics")
print("-" * 180)

if __name__ == "__main__":
main()
67 changes: 34 additions & 33 deletions local-app/python-tools/cross-organization/assess_check_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,22 @@
import glob

# --- VERSIONING ---
__version__ = "1.0.5"
__version__ = "1.0.6"

def find_latest_file(pattern):
"""Searches for the most recent file matching the pattern."""
files = glob.glob(pattern)
if not files:
return None
return max(files, key=os.path.getctime)
return max(files, key=os.path.getctime) if files else None

def main():
parser = argparse.ArgumentParser(description=f"AWS Config Audit Assessor v{__version__}")
# Made --input optional to support auto-discovery
parser.add_argument("--input", help="JSON audit file (default: latest audit_results.check_config.*.json)")
# Default regex set to '-org-'
parser.add_argument("--central-bucket-regex", default="-org-", help="Regex for corporate S3 standards")
parser.add_argument("--central-bucket-regex", default=".*-org-.*", help="Regex for corporate S3 standards")
args = parser.parse_args()

input_file = args.input
input_file = args.input or find_latest_file("audit_results.check_config.*.json")
if not input_file:
input_file = find_latest_file("audit_results.check_config.*.json")
if not input_file:
print("Error: No input file provided and no recent check_config JSON found.")
sys.exit(1)
print(f"Auto-discovered latest input: {input_file}")
print("Error: No input file found."); sys.exit(1)

try:
with open(input_file, 'r') as f:
Expand All @@ -41,14 +33,16 @@ def main():

org_id = data[0].get("org_id", "Unknown") if data else "Unknown"

print("-" * 125)
print("-" * 140)
print(f"AWS CONFIG ASSESSMENT | Org ID: {org_id} | Pattern: {args.central_bucket_regex}")
print("-" * 125)
print(f"{'Account ID':<15} | {'OU Path':<25} | {'Global Status':<12} | {'S3 Compliance'}")
print("-" * 125)
print("-" * 140)
print(f"{'Account ID':<15} | {'OU Path':<30} | {'Global Status':<12} | {'S3 Compliance'}")
print("-" * 140)

total_stats = {"objects": 0, "size_bytes": 0, "non_central_buckets": 0, "accounts": len(data)}
unique_non_central = set()
stats = {
"objects": 0, "size_bytes": 0, "non_central_buckets": set(),
"total_recorders": 0, "accounts": len(data)
}

for account in data:
acc_id = account.get("account_id")
Expand All @@ -59,25 +53,32 @@ def main():
s3_issues = []
for reg, reg_data in checks.items():
if reg == "account_summary": continue

if reg_data.get("recorder_status") == "ON":
stats["total_recorders"] += 1

bucket = reg_data.get("s3_bucket", "N/A")
total_stats["objects"] += reg_data.get("object_count", 0)
total_stats["size_bytes"] += reg_data.get("bucket_size_bytes", 0)
stats["objects"] += reg_data.get("object_count", 0)
stats["size_bytes"] += reg_data.get("bucket_size_bytes", 0)

if bucket != "N/A":
if not re.match(args.central_bucket_regex, bucket):
s3_issues.append(f"{reg}:{bucket}")
unique_non_central.add(bucket)
if bucket != "N/A" and not re.match(args.central_bucket_regex, bucket):
s3_issues.append(bucket)
stats["non_central_buckets"].add(bucket)

s3_status = "NON_COMPLIANT" if s3_issues else "COMPLIANT"
print(f"{acc_id:<15} | {ou_path[:25]:<25} | {summary:<12} | {s3_status}")
print(f"{acc_id:<15} | {ou_path[:30]:<30} | {summary:<12} | {s3_status}")

size_gb = total_stats["size_bytes"] / (1024**3)
print("-" * 125)
print(f"SUMMARY (CONFIG) | Org ID: {org_id}")
print(f" Total S3 Storage: {size_gb:.2f} GB")
print(f" Non-Central Buckets: {len(unique_non_central)}")
print("-" * 125)
# RESTORED SUMMARY SECTION
size_gb = stats["size_bytes"] / (1024**3)
print("-" * 140)
print(f"ORGANIZATION FOOTPRINT SUMMARY (CONFIG) | Org ID: {org_id}")
print(f" Active Recorders: {stats['total_recorders']}")
print(f" Total S3 Objects: {stats['objects']:,}")
print(f" Total S3 Storage: {size_gb:.2f} GB")
print(f" Non-Central Buckets: {len(stats['non_central_buckets'])}")
for b in sorted(stats["non_central_buckets"]):
print(f" - {b}")
print("-" * 140)

if __name__ == "__main__":
main()

0 comments on commit b092738

Please sign in to comment.