Skip to content

Commit

Permalink
restore cw log details
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 2, 2026
1 parent 0ce7760 commit 5847e40
Showing 1 changed file with 49 additions and 23 deletions.
72 changes: 49 additions & 23 deletions local-app/python-tools/cross-organization/check_cloudtrail.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,41 @@
from datetime import datetime, timedelta

# --- VERSIONING ---
__version__ = "1.1.7"
__version__ = "1.1.8"

def get_s3_metrics(session, bucket_name, region):
cw = session.client('cloudwatch', region_name=region)
metrics = {"bucket_size_bytes": 0, "object_count": 0}
end = datetime.utcnow()
start = end - timedelta(days=2)
try:
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):
"""Restored: Fetches CloudWatch Log Group retention and size."""
if not group_arn: return {}
try:
# Extract name from ARN (arn:aws:logs:region:account:log-group:name)
group_name = group_arn.split(':')[-1]
logs = session.client('logs', region_name=region)
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: pass
return {}

def account_task(account_session, account_id, account_name, region):
results = {"alias": "N/A", "data": {}}
Expand All @@ -14,60 +48,52 @@ def account_task(account_session, account_id, account_name, region):
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)

# Track ARNs globally across the account scan to prevent duplicates
seen_arns = set()

for reg in enabled_regions:
# Always ensure the region key exists in the JSON so it's not "empty"
results["data"][reg] = {}

ct = account_session.client('cloudtrail', region_name=reg)
try:
# Include shadow trails to see multi-region/org trails from any region
trails = ct.describe_trails(includeShadowTrails=True).get('trailList', [])
except:
continue # Skip regions where CloudTrail might be restricted
except: continue

for trail in trails:
t_arn = trail['TrailARN']

# DUPLICATION PROTECTION:
# Only process the trail once. If it's multi-region, we'll
# likely see it in the first region we scan.
if t_arn in seen_arns:
continue
if t_arn in seen_arns: continue
seen_arns.add(t_arn)

home_reg = trail.get('HomeRegion')
is_multi = trail.get('IsMultiRegionTrail', False)
status = ct.get_trail_status(Name=t_arn)
is_org = trail.get('IsOrganizationTrail', False)
is_multi = trail.get('IsMultiRegionTrail', False)

if is_org: org_trail_count += 1
else: local_trail_count += 1

shadow_count = (total_reg_count - 1) if is_multi else 0

t_data = {
"resource": t_arn,
"trail_name": trail['Name'],
"trail_arn": t_arn,
"home_region": home_reg,
"shadow_region_count": shadow_count,
"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),
"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')
"kms_key_id": trail.get('KmsKeyId', 'SSE-S3'),
"cw_logs_arn": trail.get('CloudWatchLogsLogGroupArn', 'N/A') # Restored Field
}

# Place the trail data into the region it was discovered in
# Retrieve restored CloudWatch details
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

# Final account summary ensures the file is never "empty"
results["data"]["account_summary"] = {
"_summary": f"ORG:{org_trail_count}|LOCAL:{local_trail_count}",
"enabled_regions": total_reg_count,
Expand Down

0 comments on commit 5847e40

Please sign in to comment.