From 5dbe9503aa7fa0d437d5740e8803b1d74ade9f1f Mon Sep 17 00:00:00 2001 From: badra001 Date: Fri, 2 Jan 2026 09:18:14 -0500 Subject: [PATCH] restore headers --- .../cross-organization/org_runner.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/local-app/python-tools/cross-organization/org_runner.py b/local-app/python-tools/cross-organization/org_runner.py index 4ef6fafb..ef98fc50 100755 --- a/local-app/python-tools/cross-organization/org_runner.py +++ b/local-app/python-tools/cross-organization/org_runner.py @@ -18,7 +18,7 @@ def tqdm(iterable, **kwargs): return iterable # --- VERSIONING --- -__version__ = "1.6.1" +__version__ = "1.6.2" class OrgTaskRunner: def __init__(self, args): @@ -28,9 +28,6 @@ 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-'): @@ -83,22 +80,35 @@ def process_account(self, acc, partition, tasks): def run(self): self.start_time = time.perf_counter() session = boto3.Session(profile_name=self.args.profile, region_name=self.args.region) + org_client = session.client('organizations') partition = session.client('sts').get_caller_identity()['Arn'].split(':')[1] - tasks, check_names = [], [] + # Load tasks & build dynamic metadata + tasks, check_info = [], [] if self.args.enable_checks: sys.path.append(os.getcwd()) for m in self.args.enable_checks: - module = importlib.import_module(m.replace('.py', '')) + mod_name = m.replace('.py', '') + module = importlib.import_module(mod_name) tasks.append(getattr(module, 'account_task')) - check_names.append(m.replace('.py', '')) + v = getattr(module, '__version__', '?.?.?') + check_info.append(f"{mod_name} (v{v})") - all_accounts = [acc for page in session.client('organizations').get_paginator('list_accounts').paginate() - for acc in page['Accounts'] if acc['Status'] == 'ACTIVE'] + check_suffix = ".".join([m.replace('.py', '') for m in (self.args.enable_checks or [])]) or "connectivity" - print("-" * 60) - print(f"AWS ORG TASK RUNNER - v{__version__} | Runners: {self.args.max_workers}") - print("-" * 60) + # Gather accounts and count them before processing + all_accounts = [acc for page in org_client.get_paginator('list_accounts').paginate() + for acc in page['Accounts'] if acc['Status'] == 'ACTIVE'] + all_accounts.sort(key=lambda x: x['Name' if self.args.sort == 'name' else 'Id'].lower()) + + # RESTORED HEADER + print("-" * 100) + print(f"AWS ORG TASK RUNNER - v{__version__}") + print(f"Target Role: {self.args.role_name}") + print(f"Runners: {self.args.max_workers}") + print(f"Enabled Checks: {', '.join(check_info) if check_info else 'None'}") + print(f"Accounts Found: {len(all_accounts)}") + print("-" * 100) with ThreadPoolExecutor(max_workers=self.args.max_workers) as executor: futures = {executor.submit(self.process_account, acc, partition, tasks): acc for acc in all_accounts} @@ -110,7 +120,8 @@ def run(self): if self.args.output: ds = datetime.now().strftime("%Y%m%dT%H%M%S") - # 1. ACCOUNT BASELINE (Metadata Only) + + # 1. ACCOUNT BASELINE acc_base = f"audit_results.account.{ds}" 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: @@ -119,10 +130,10 @@ def run(self): 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 (Data + Account Context) - for cn in check_names: + # 2. CHECK SPECIFIC FILES + for cn in [m.replace('.py', '') for m in (self.args.enable_checks or [])]: chk_base = f"audit_results.{cn}.{ds}" - # Save CSV (Long Format) + # Save CSV with open(f"{chk_base}.csv", 'w', newline='') as f: w = csv.writer(f) w.writerow(["account_id", "account_alias", "region", "field_name", "field_value"]) @@ -130,7 +141,7 @@ def run(self): for reg, fields in res["checks"].items(): for k, v in fields.items(): w.writerow([res["metadata"]["account_id"], res["metadata"]["alias"], reg, k, v]) - # Save JSON (Hierarchical) + # Save JSON 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)