diff --git a/local-app/python-tools/cross-organization/check_config.py b/local-app/python-tools/cross-organization/check_config.py index 2395b4e8..14b28f55 100644 --- a/local-app/python-tools/cross-organization/check_config.py +++ b/local-app/python-tools/cross-organization/check_config.py @@ -1,13 +1,14 @@ import boto3 +import time from datetime import datetime, timedelta -__version__ = "1.0.5" +__version__ = "1.0.6" 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) # Config metrics are once per day + start = end - timedelta(days=2) try: for metric_name in ['BucketSizeBytes', 'NumberOfObjects']: @@ -37,6 +38,9 @@ def account_task(account_session, account_id, account_name, region): regions = [r['RegionName'] for r in ec2.describe_regions()['Regions']] for reg in regions: + # START REGIONAL TIMER + reg_start = time.perf_counter() + config = account_session.client('config', region_name=reg) channels = config.describe_delivery_channels().get('DeliveryChannels', []) recorders = config.describe_configuration_recorders().get('ConfigurationRecorders', []) @@ -56,6 +60,12 @@ def account_task(account_session, account_id, account_name, region): if bucket != "N/A": reg_data.update(get_s3_metrics(account_session, bucket, reg)) + # END REGIONAL TIMER + reg_elapsed = time.perf_counter() - reg_start + reg_data["check_elapsed_sec"] = round(reg_elapsed, 3) + results["data"][reg] = reg_data - except Exception as e: results["error"] = str(e) + + except Exception as e: + results["error"] = str(e) return results diff --git a/local-app/python-tools/cross-organization/org_runner.py b/local-app/python-tools/cross-organization/org_runner.py index d1fc45e2..4d75a51f 100755 --- a/local-app/python-tools/cross-organization/org_runner.py +++ b/local-app/python-tools/cross-organization/org_runner.py @@ -12,7 +12,7 @@ from pathlib import Path # --- VERSIONING --- -__version__ = "1.3.0" +__version__ = "1.4.0" class OrgTaskRunner: def __init__(self, args): @@ -31,19 +31,27 @@ def run(self): sts = session.client('sts') org = session.client('organizations') - # Load tasks + # Load tasks and capture versions tasks = [] + task_versions = [] if self.args.enable_checks: sys.path.append(os.getcwd()) for m in self.args.enable_checks: - module = importlib.import_module(m.replace('.py', '')) + module_name = m.replace('.py', '') + module = importlib.import_module(module_name) + v = getattr(module, '__version__', 'Unknown') tasks.append(getattr(module, 'account_task')) + task_versions.append(f"{module_name} (v{v})") paginator = org.get_paginator('list_accounts') all_accounts = [acc for page in paginator.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()) - self.log_print(f"--- Starting Org Run v{__version__} ---") + # HEADER WITH VERSIONS + self.log_print("-" * 100) + self.log_print(f"AWS ORG TASK RUNNER - v{__version__}") + self.log_print(f"Enabled Checks: {', '.join(task_versions) if task_versions else 'None'}") + self.log_print("-" * 100) for i, acc in enumerate(all_accounts, 1): acc_id, acc_name = acc['Id'], acc['Name'] @@ -59,6 +67,8 @@ def run(self): ) account_data = {"account_id": acc_id, "account_name": acc_name, "checks": {}} + + # EXECUTION WITH TIMING for t_func in tasks: res = t_func(m_sess, acc_id, acc_name, self.args.region) account_data["alias"] = res.get("alias") @@ -69,6 +79,7 @@ def run(self): except Exception as e: self.log_print(f"[{i}/{len(all_accounts)}] FAILED {acc_name}: {e}") + # EXPORTS (JSON & CSV) if self.args.output: ds = datetime.now().strftime("%Y-%m-%dT%H%M%S") base = self.args.output if self.args.output != 'DEFAULT' else "audit_results"