Skip to content

Commit

Permalink
home region/shadow region fix
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 2, 2026
1 parent ecd8750 commit 0ce7760
Showing 1 changed file with 24 additions and 27 deletions.
51 changes: 24 additions & 27 deletions local-app/python-tools/cross-organization/check_cloudtrail.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
from datetime import datetime, timedelta

# --- VERSIONING ---
__version__ = "1.1.6"

# ... (get_s3_metrics and get_log_group_details remain same) ...
__version__ = "1.1.7"

def account_task(account_session, account_id, account_name, region):
results = {"alias": "N/A", "data": {}}
Expand All @@ -17,64 +15,63 @@ def account_task(account_session, account_id, account_name, region):
enabled_regions = [r['RegionName'] for r in ec2.describe_regions()['Regions']]
total_reg_count = len(enabled_regions)

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

for reg in enabled_regions:
reg_start = time.perf_counter()
ct = account_session.client('cloudtrail', region_name=reg)
# Always ensure the region key exists in the JSON so it's not "empty"
results["data"][reg] = {}

# We must use includeShadowTrails=True to discover trails managed elsewhere
trails = ct.describe_trails(includeShadowTrails=True).get('trailList', [])
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

for trail in trails:
t_arn = trail['TrailARN']
home_reg = trail.get('HomeRegion')
is_multi = trail.get('IsMultiRegionTrail', False)

# Logic: Only process the trail in its Home Region to prevent duplication.
if reg != home_reg:
# 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 found_trail_arns: continue
found_trail_arns.add(t_arn)
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)

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

# Calculate Shadow Regions:
# If multi-region, it covers all enabled regions minus the home region.
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, # New field
"shadow_region_count": shadow_count,
"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'),
"check_elapsed_sec": round(time.perf_counter() - reg_start, 3)
"kms_key_id": trail.get('KmsKeyId', 'SSE-S3')
}

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))

# Place the trail data into the region it was discovered in
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
"enabled_regions": total_reg_count,
"total_unique_trails": len(seen_arns)
}

except Exception as e:
Expand Down

0 comments on commit 0ce7760

Please sign in to comment.