Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 2, 2026
1 parent 68975d3 commit 6ecd0e2
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 41 deletions.
44 changes: 11 additions & 33 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,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)
Expand All @@ -23,89 +23,67 @@ 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']
if t_arn in seen_arns: continue
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
36 changes: 28 additions & 8 deletions local-app/python-tools/cross-organization/check_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,28 @@
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)
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'])
# 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
Expand All @@ -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)
Expand All @@ -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"
Expand Down

0 comments on commit 6ecd0e2

Please sign in to comment.