Skip to content

Commit

Permalink
update assess script, split out files properly
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 2, 2026
1 parent bbc5eca commit 4690e66
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 32 deletions.
72 changes: 58 additions & 14 deletions local-app/python-tools/cross-organization/assess_check_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,72 @@
import json
import argparse
import re
import sys

__version__ = "1.0.0"
# --- VERSIONING ---
__version__ = "1.0.1"

def main():
parser = argparse.ArgumentParser()
parser.add_argument("--input", required=True, help="The JSON audit file")
parser.add_argument("--central-bucket-regex", required=True, help="Regex for central bucket")
parser = argparse.ArgumentParser(description=f"AWS Config Audit Assessor v{__version__}")
parser.add_argument("--input", required=True, help="The JSON audit file from check_config")
parser.add_argument("--central-bucket-regex", required=True, help="Regex to validate corporate S3 standards")
args = parser.parse_args()

with open(args.input, 'r') as f:
data = json.load(f)
try:
with open(args.input, 'r') as f:
data = json.load(f)
except Exception as e:
print(f"Error reading input file: {e}")
sys.exit(1)

print(f"{'Account ID':<15} | {'Region':<15} | {'Match':<6} | {'Bucket Name'}")
print("-" * 70)
print("-" * 110)
print(f"{'Account ID':<15} | {'Alias':<20} | {'Global Status':<12} | {'S3 Compliance'}")
print("-" * 110)

for acc in data:
for reg, checks in acc["checks"].items():
bucket = checks.get("s3_bucket", "N/A")
total_accounts = len(data)
non_compliant_s3 = 0
duplicate_globals = 0

for account in data:
acc_id = account.get("account_id", "Unknown")
alias = account.get("alias", "N/A")
checks = account.get("data", {})

# 1. Evaluate Global Recording Summary
# Look for the 'account_summary' key injected by check_config.py
summary_record = checks.get("account_summary", {})
global_status = summary_record.get("_summary", "MISSING")

if "MULTIPLE" in global_status:
duplicate_globals += 1

# 2. Evaluate S3 Regional Compliance
s3_issues = []
for reg, reg_data in checks.items():
if reg == "account_summary":
continue

bucket = reg_data.get("s3_bucket", "N/A")
if bucket != "N/A":
match = bool(re.match(args.central_bucket_regex, bucket))
if not match:
print(f"{acc['account_id']:<15} | {reg:<15} | {str(match):<6} | {bucket}")
is_valid = bool(re.match(args.central_bucket_regex, bucket))
if not is_valid:
s3_issues.append(f"{reg}:{bucket}")

if s3_issues:
non_compliant_s3 += 1
s3_str = ", ".join(s3_issues)
else:
s3_str = "✅ Compliant"

print(f"{acc_id:<15} | {alias[:20]:<20} | {global_status:<12} | {s3_str}")

# Final Summary Footer
print("-" * 110)
print(f"ASSESSMENT SUMMARY:")
print(f" Total Accounts Scanned: {total_accounts}")
print(f" Accounts with Duplicate Global Recording: {duplicate_globals}")
print(f" Accounts with Non-Compliant S3 Buckets: {non_compliant_s3}")
print("-" * 110)

if __name__ == "__main__":
main()
5 changes: 3 additions & 2 deletions local-app/python-tools/cross-organization/check_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import time
from datetime import datetime, timedelta

__version__ = "1.0.7"
__version__ = "1.0.8"

def get_s3_metrics(session, bucket_name, region):
cw = session.client('cloudwatch', region_name=region)
Expand Down Expand Up @@ -30,6 +30,7 @@ def account_task(account_session, account_id, account_name, region):
recorders = config.describe_configuration_recorders().get('ConfigurationRecorders', [])
channels = config.describe_delivery_channels().get('DeliveryChannels', [])

# Re-added Global Resource Check
is_global = any(r.get('recordingGroup', {}).get('includeGlobalResourceTypes') for r in recorders)
if is_global: global_count += 1

Expand All @@ -42,7 +43,7 @@ def account_task(account_session, account_id, account_name, region):
reg_data["check_elapsed_sec"] = round(time.perf_counter() - reg_start, 3)
results["data"][reg] = reg_data

# Add Per-Account Summary record (using 'global' as the region key for the CSV row)
# Per-Account Summary record
summary_val = f"OK/1" if global_count == 1 else f"MULTIPLE/{global_count}" if global_count > 1 else "NONE/0"
results["data"]["account_summary"] = {"_summary": summary_val}

Expand Down
43 changes: 27 additions & 16 deletions local-app/python-tools/cross-organization/org_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
def tqdm(iterable, **kwargs): return iterable

# --- VERSIONING ---
__version__ = "1.6.0"
__version__ = "1.6.1"

class OrgTaskRunner:
def __init__(self, args):
Expand All @@ -28,6 +28,9 @@ def __init__(self, args):
self.created_files = []
self.start_time = 0

def log_print(self, message=""):
self.output_log.append(message)

def get_ou_path(self, org_client, entity_id):
if entity_id in self.hierarchy_cache: return self.hierarchy_cache[entity_id]
if entity_id.startswith('r-'):
Expand All @@ -36,7 +39,8 @@ def get_ou_path(self, org_client, entity_id):
try:
parents = org_client.list_parents(ChildId=entity_id)['Parents']
p_id = parents[0]['Id'] if parents else None
ou_name = org_client.describe_organizational_unit(OrganizationalUnitId=entity_id)['OrganizationalUnit']['Name']
ou_desc = org_client.describe_organizational_unit(OrganizationalUnitId=entity_id)
ou_name = ou_desc['OrganizationalUnit']['Name']
p_path, _ = self.get_ou_path(org_client, p_id) if p_id else (None, None)
path = f"{p_path}:{ou_name}" if p_path else ou_name
self.hierarchy_cache[entity_id] = (path, entity_id)
Expand All @@ -53,11 +57,13 @@ def process_account(self, acc, partition, tasks):
ou_path, ou_id = self.get_ou_path(org, parents[0]['Id']) if parents else ("Orphaned", "N/A")
ou_path = ou_path if ou_path else "Root"

account_data = {
account_metadata = {
"account_id": acc_id, "account_name": acc_name, "alias": "N/A",
"ou_path": ou_path, "ou_id": ou_id, "checks": {}
"ou_path": ou_path, "ou_id": ou_id
}

account_results = {"metadata": account_metadata, "checks": {}}

try:
assumed = sts.assume_role(RoleArn=role_arn, RoleSessionName="OrgRunner")
m_sess = boto3.Session(
Expand All @@ -68,9 +74,9 @@ def process_account(self, acc, partition, tasks):
)
for t_func in tasks:
res = t_func(m_sess, acc_id, acc_name, self.args.region)
account_data["alias"] = res.get("alias", "N/A")
account_data["checks"].update(res.get("data", {}))
return account_data, None
account_results["metadata"]["alias"] = res.get("alias", "N/A")
account_results["checks"].update(res.get("data", {}))
return account_results, None
except Exception as e:
return None, f"FAILED {acc_name}: {str(e)}"

Expand Down Expand Up @@ -104,26 +110,31 @@ def run(self):

if self.args.output:
ds = datetime.now().strftime("%Y%m%dT%H%M%S")
# 1. ACCOUNT BASELINE FILES
# 1. ACCOUNT BASELINE (Metadata Only)
acc_base = f"audit_results.account.{ds}"
with open(f"{acc_base}.json", 'w') as f: json.dump(self.full_results, f, indent=2)
with open(f"{acc_base}.json", 'w') as f: json.dump([r['metadata'] for r in self.full_results], f, indent=2)
with open(f"{acc_base}.csv", 'w', newline='') as f:
w = csv.DictWriter(f, fieldnames=["account_id", "account_name", "alias", "ou_path", "ou_id"])
w.writeheader()
w.writerows([{k: v for k, v in acc.items() if k != 'checks'} for acc in self.full_results])
w.writerows([r['metadata'] for r in self.full_results])
self.created_files.extend([f"{acc_base}.json", f"{acc_base}.csv"])

# 2. CHECK SPECIFIC FILES
# 2. CHECK SPECIFIC FILES (Data + Account Context)
for cn in check_names:
chk_base = f"audit_results.{cn}.{ds}"
# Save CSV (Long Format)
with open(f"{chk_base}.csv", 'w', newline='') as f:
w = csv.writer(f)
w.writerow(["account_id", "region", "field_name", "field_value"])
for acc in self.full_results:
for reg, fields in acc["checks"].items():
w.writerow(["account_id", "account_alias", "region", "field_name", "field_value"])
for res in self.full_results:
for reg, fields in res["checks"].items():
for k, v in fields.items():
w.writerow([acc["account_id"], reg, k, v])
self.created_files.append(f"{chk_base}.csv")
w.writerow([res["metadata"]["account_id"], res["metadata"]["alias"], reg, k, v])
# Save JSON (Hierarchical)
with open(f"{chk_base}.json", 'w') as f:
json.dump([{"account_id": r["metadata"]["account_id"], "alias": r["metadata"]["alias"], "data": r["checks"]} for r in self.full_results], f, indent=2)

self.created_files.extend([f"{chk_base}.csv", f"{chk_base}.json"])

print(f"\nTime: {round(time.perf_counter() - self.start_time, 2)}s\nFiles Created:")
for f in self.created_files: print(f" - {f}")
Expand Down

0 comments on commit 4690e66

Please sign in to comment.