From 6ecd0e23f084a6f7b92f9132b6b6225dbc3a92f0 Mon Sep 17 00:00:00 2001 From: badra001 Date: Fri, 2 Jan 2026 15:58:29 -0500 Subject: [PATCH] update --- .../cross-organization/check_cloudtrail.py | 44 +++++-------------- .../cross-organization/check_config.py | 36 +++++++++++---- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/local-app/python-tools/cross-organization/check_cloudtrail.py b/local-app/python-tools/cross-organization/check_cloudtrail.py index 98f7962b..1ef918a6 100644 --- a/local-app/python-tools/cross-organization/check_cloudtrail.py +++ b/local-app/python-tools/cross-organization/check_cloudtrail.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta # --- VERSIONING --- -__version__ = "1.1.8" +__version__ = "1.1.11" def get_s3_metrics(session, bucket_name, region): cw = session.client('cloudwatch', region_name=region) @@ -23,44 +23,35 @@ def get_s3_metrics(session, bucket_name, region): return metrics def get_log_group_details(session, group_arn, region): - """RESTORED: Queries CloudWatch for specific log group metadata.""" if not group_arn: return {} try: - # Extract name from ARN (arn:aws:logs:region:acc:log-group:NAME:*) - # CloudWatch names can contain colons, but usually are the last part group_name = group_arn.split(':log-group:')[-1].replace(':*', '') - logs = session.client('logs', region_name=region) - # describe_log_groups returns a list; we filter by prefix for efficiency resp = logs.describe_log_groups(logGroupNamePrefix=group_name) - for g in resp.get('logGroups', []): if g['logGroupName'] == group_name: return { "cw_logs_retention_days": g.get('retentionInDays', 'Never Expire'), "cw_logs_size_bytes": g.get('storedBytes', 0) } - except Exception: - pass + except: pass return {} def account_task(account_session, account_id, account_name, region): results = {"alias": "N/A", "data": {}} org_trail_count, local_trail_count = 0, 0 - try: + sts = account_session.client('sts') + partition = sts.get_caller_identity()['Arn'].split(':')[1] results["alias"] = account_session.client('iam').list_account_aliases().get('AccountAliases', ["N/A"])[0] ec2 = account_session.client('ec2', region_name=region) enabled_regions = [r['RegionName'] for r in ec2.describe_regions()['Regions']] - total_reg_count = len(enabled_regions) seen_arns = set() for reg in enabled_regions: results["data"][reg] = {} ct = account_session.client('cloudtrail', region_name=reg) - try: - trails = ct.describe_trails(includeShadowTrails=True).get('trailList', []) - except: continue + trails = ct.describe_trails(includeShadowTrails=True).get('trailList', []) for trail in trails: t_arn = trail['TrailARN'] @@ -68,44 +59,31 @@ def account_task(account_session, account_id, account_name, region): seen_arns.add(t_arn) status = ct.get_trail_status(Name=t_arn) - is_org = trail.get('IsOrganizationTrail', False) is_multi = trail.get('IsMultiRegionTrail', False) + is_org = trail.get('IsOrganizationTrail', False) if is_org: org_trail_count += 1 else: local_trail_count += 1 t_data = { "resource": t_arn, + "partition": partition, "trail_name": trail['Name'], - "trail_arn": t_arn, - "home_region": trail.get('HomeRegion'), - "shadow_region_count": (total_reg_count - 1) if is_multi else 0, "is_logging": str(status.get('IsLogging', False)), - "is_org_trail": str(is_org), - "is_multi_region": str(is_multi), + "home_region": trail.get('HomeRegion'), + "shadow_region_count": (len(enabled_regions) - 1) if is_multi else 0, "s3_bucket": trail.get('S3BucketName', 'N/A'), - "log_file_validation": str(trail.get('LogFileValidationEnabled', False)), - "sns_topic": trail.get('SnsTopicARN', 'N/A'), "kms_key_id": trail.get('KmsKeyId', 'SSE-S3'), "cw_logs_arn": trail.get('CloudWatchLogsLogGroupArn', 'N/A') } - # RE-INTEGRATED CLOUDWATCH CHECK if t_data["cw_logs_arn"] != 'N/A': t_data.update(get_log_group_details(account_session, t_data["cw_logs_arn"], reg)) - if t_data['s3_bucket'] != 'N/A': t_data.update(get_s3_metrics(account_session, t_data['s3_bucket'], reg)) results["data"][f"{reg}:{t_arn}"] = t_data - results["data"]["account_summary"] = { - "_summary": f"ORG:{org_trail_count}|LOCAL:{local_trail_count}", - "enabled_regions": total_reg_count, - "total_unique_trails": len(seen_arns) - } - - except Exception as e: - results["error"] = str(e) - + results["data"]["account_summary"] = {"_summary": f"ORG:{org_trail_count}|LOCAL:{local_trail_count}"} + except Exception as e: results["error"] = str(e) return results diff --git a/local-app/python-tools/cross-organization/check_config.py b/local-app/python-tools/cross-organization/check_config.py index fb75fa6b..da2423ba 100644 --- a/local-app/python-tools/cross-organization/check_config.py +++ b/local-app/python-tools/cross-organization/check_config.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta # --- VERSIONING --- -__version__ = "1.1.0" +__version__ = "1.1.1" def get_s3_metrics(session, bucket_name, region): cw = session.client('cloudwatch', region_name=region) @@ -11,9 +11,20 @@ def get_s3_metrics(session, bucket_name, region): end = datetime.utcnow() start = end - timedelta(days=2) try: - r1 = cw.get_metric_statistics(Namespace='AWS/S3', MetricName='BucketSizeBytes', Dimensions=[{'Name': 'BucketName', 'Value': bucket_name}, {'Name': 'StorageType', 'Value': 'StandardStorage'}], StartTime=start, EndTime=end, Period=86400, Statistics=['Average']) + # Get Bucket Size + r1 = cw.get_metric_statistics( + Namespace='AWS/S3', MetricName='BucketSizeBytes', + Dimensions=[{'Name': 'BucketName', 'Value': bucket_name}, {'Name': 'StorageType', 'Value': 'StandardStorage'}], + StartTime=start, EndTime=end, Period=86400, Statistics=['Average'] + ) if r1['Datapoints']: metrics["bucket_size_bytes"] = int(r1['Datapoints'][-1]['Average']) - r2 = cw.get_metric_statistics(Namespace='AWS/S3', MetricName='NumberOfObjects', Dimensions=[{'Name': 'BucketName', 'Value': bucket_name}, {'Name': 'StorageType', 'Value': 'AllStorageTypes'}], StartTime=start, EndTime=end, Period=86400, Statistics=['Average']) + + # Get Object Count + r2 = cw.get_metric_statistics( + Namespace='AWS/S3', MetricName='NumberOfObjects', + Dimensions=[{'Name': 'BucketName', 'Value': bucket_name}, {'Name': 'StorageType', 'Value': 'AllStorageTypes'}], + StartTime=start, EndTime=end, Period=86400, Statistics=['Average'] + ) if r2['Datapoints']: metrics["object_count"] = int(r2['Datapoints'][-1]['Average']) except: pass return metrics @@ -23,8 +34,12 @@ def account_task(account_session, account_id, account_name, region): global_count = 0 try: results["alias"] = account_session.client('iam').list_account_aliases().get('AccountAliases', ["N/A"])[0] - regions = [r['RegionName'] for r in account_session.client('ec2', region_name=region).describe_regions()['Regions']] + ec2 = account_session.client('ec2', region_name=region) + regions = [r['RegionName'] for r in ec2.describe_regions()['Regions']] + sts = account_session.client('sts') + partition = sts.get_caller_identity()['Arn'].split(':')[1] + for reg in regions: reg_start = time.perf_counter() config = account_session.client('config', region_name=reg) @@ -35,17 +50,22 @@ def account_task(account_session, account_id, account_name, region): if is_global: global_count += 1 reg_data = { - "resource": "config", # New Resource Field + "resource": "config", + "partition": partition, "recorder_status": "ON" if recorders else "OFF", "global_recording": str(is_global) } + if channels: bucket = channels[0].get('s3BucketName', 'N/A') - reg_data.update({"s3_bucket": bucket, "delivery_freq": channels[0].get('configSnapshotDeliveryProperties', {}).get('deliveryFrequency', 'N/A')}) - if bucket != "N/A": reg_data.update(get_s3_metrics(account_session, bucket, reg)) + reg_data.update({ + "s3_bucket": bucket, + "delivery_freq": channels[0].get('configSnapshotDeliveryProperties', {}).get('deliveryFrequency', 'N/A') + }) + if bucket != "N/A": + reg_data.update(get_s3_metrics(account_session, bucket, reg)) reg_data["check_elapsed_sec"] = round(time.perf_counter() - reg_start, 3) - # Region field is now strictly the region name results["data"][reg] = reg_data summary_val = f"OK/1" if global_count == 1 else f"MULTIPLE/{global_count}" if global_count > 1 else "NONE/0"