Skip to content

Commit

Permalink
add other details
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 2, 2026
1 parent 77ba4d3 commit b9bce26
Showing 1 changed file with 51 additions and 51 deletions.
102 changes: 51 additions & 51 deletions local-app/python-tools/cross-organization/check_cloudtrail.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,30 @@
import time
from datetime import datetime, timedelta

__version__ = "1.0.0"
__version__ = "1.1.0"

def get_s3_metrics(session, bucket_name, region):
"""Fetches bucket storage metrics from CloudWatch."""
cw = session.client('cloudwatch', region_name=region)
metrics = {"bucket_size_bytes": 0, "object_count": 0}
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']
)
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']
)
if r2['Datapoints']: metrics["object_count"] = int(r2['Datapoints'][-1]['Average'])
# Check both Standard and All Storage types for full visibility
for metric, key in [('BucketSizeBytes', 'bucket_size_bytes'), ('NumberOfObjects', 'object_count')]:
r = cw.get_metric_statistics(
Namespace='AWS/S3', MetricName=metric,
Dimensions=[{'Name': 'BucketName', 'Value': bucket_name}, {'Name': 'StorageType', 'Value': 'StandardStorage' if key == 'bucket_size_bytes' else 'AllStorageTypes'}],
StartTime=start, EndTime=end, Period=86400, Statistics=['Average']
)
if r['Datapoints']: metrics[key] = int(r['Datapoints'][-1]['Average'])
except: pass
return metrics

def get_log_group_details(session, group_arn, region):
"""Fetches retention and size for CloudWatch Log Groups."""
if not group_arn: return {}
logs = session.client('logs', region_name=region)
group_name = group_arn.split(':')[-2] # Extract name from ARN
try:
logs = session.client('logs', region_name=region)
group_name = group_arn.split(':')[-2] if ':' in group_arn else group_arn
resp = logs.describe_log_groups(logGroupNamePrefix=group_name)
for g in resp.get('logGroups', []):
if g['logGroupName'] == group_name:
Expand All @@ -50,42 +43,49 @@ def account_task(account_session, account_id, account_name, region):

try:
results["alias"] = account_session.client('iam').list_account_aliases().get('AccountAliases', ["N/A"])[0]
ct = account_session.client('cloudtrail', region_name=region)

# describe_trails retrieves trails for the current region (+ shadow trails if multi-region)
trails = ct.describe_trails(includeShadowTrails=True).get('trailList', [])
ec2 = account_session.client('ec2', region_name=region)
enabled_regions = [r['RegionName'] for r in ec2.describe_regions()['Regions']]

for trail in trails:
trail_name = trail['Name']
status = ct.get_trail_status(Name=trail_name)

is_org = trail.get('IsOrganizationTrail', False)
if is_org: org_trail_count += 1
else: local_trail_count += 1

trail_data = {
"trail_name": trail_name,
"is_logging": str(status.get('IsLogging', False)),
"is_org_trail": str(is_org),
"is_multi_region": str(trail.get('IsMultiRegionTrail', False)),
"log_file_validation": str(trail.get('LogFileValidationEnabled', False)),
"s3_bucket": trail.get('S3BucketName', 'N/A'),
"kms_key_id": trail.get('KmsKeyId', 'SSE-S3 (Default)'),
"sns_topic": trail.get('SNSTopicName', 'N/A'),
"home_region": trail.get('HomeRegion', 'N/A')
}

# Fetch CloudWatch Logs Integration
if 'CloudWatchLogsLogGroupArn' in trail:
trail_data.update(get_log_group_details(account_session, trail['CloudWatchLogsLogGroupArn'], region))

# Fetch S3 Metrics (if bucket is in the same account)
if trail_data['s3_bucket'] != 'N/A':
trail_data.update(get_s3_metrics(account_session, trail_data['s3_bucket'], region))
found_trail_arns = set()

for reg in enabled_regions:
ct = account_session.client('cloudtrail', region_name=reg)
# describe_trails with IncludeShadowTrails=True is powerful but we loop to catch
# trails that might be single-region in non-standard regions.
trails = ct.describe_trails(includeShadowTrails=True).get('trailList', [])

results["data"][f"trail:{trail_name}"] = trail_data
for trail in trails:
t_arn = trail['TrailARN']
if t_arn in found_trail_arns: continue
found_trail_arns.add(t_arn)

status = ct.get_trail_status(Name=t_arn)
is_org = trail.get('IsOrganizationTrail', False)
if is_org: org_trail_count += 1
else: local_trail_count += 1

t_name = trail['Name']
t_data = {
"trail_name": t_name,
"home_region": trail.get('HomeRegion', reg),
"is_logging": str(status.get('IsLogging', False)),
"is_org_trail": str(is_org),
"is_multi_region": str(trail.get('IsMultiRegionTrail', False)),
"s3_bucket": trail.get('S3BucketName', 'N/A'),
"log_file_validation": str(trail.get('LogFileValidationEnabled', False)),
"kms_key_id": trail.get('KmsKeyId', 'SSE-S3'),
"check_elapsed_sec": 0 # Placeholder for loop timing if needed
}

if 'CloudWatchLogsLogGroupArn' in trail:
t_data.update(get_log_group_details(account_session, trail['CloudWatchLogsLogGroupArn'], reg))

if t_data['s3_bucket'] != 'N/A':
t_data.update(get_s3_metrics(account_session, t_data['s3_bucket'], reg))

results["data"][f"trail:{t_name}:{t_data['home_region']}"] = t_data

# Add Per-Account Summary
# Summary Logic
summary = "OK"
if org_trail_count > 0 and local_trail_count > 0:
summary = f"DUPLICATIVE/{local_trail_count}_LOCAL_WITH_ORG"
Expand Down

0 comments on commit b9bce26

Please sign in to comment.