diff --git a/local-app/python-tools/test-cross-organization/README.md b/local-app/python-tools/test-cross-organization/README.md new file mode 100644 index 00000000..5a1cda40 --- /dev/null +++ b/local-app/python-tools/test-cross-organization/README.md @@ -0,0 +1,96 @@ +# README: AWS Organization Task Runner + +## Overview + +The `org_runner.py` is a modular Python utility designed to execute administrative tasks across every account in an AWS Organization. It handles the heavy lifting of: + +1. **Identity Management**: Discovering partition (Commercial vs GovCloud) and Management account info. +2. **Hierarchy Discovery**: Resolving the full Organizational Unit (OU) path (e.g., `Production:Finance:Workloads`). +3. **Cross-Account Access**: Automating the `STS AssumeRole` workflow. +4. **Reporting**: Generating console tables, summary statistics by OU, and timestamped log files. + +## Configuration + +### Prerequisites + +* **IAM Role**: A cross-account IAM role (e.g., `OrganizationAdminAccessRole`) must exist in all member accounts with a Trust Policy allowing your Management identity to assume it. +* **Permissions**: Your local credentials must have `organizations:List*` and `sts:AssumeRole` permissions. + +### Modular Tasks + +The script is designed for extensibility. To run a specific task (e.g., checking for unencrypted S3 buckets), simply modify the `account_task` function: + +```python +def account_task(account_session, account_id, account_name, region): + s3 = account_session.client('s3') + # Your custom logic here... + return {"status": "SUCCESS", "alias": "some-data"} + +``` + +## Usage + +### Make Executable + +```bash +chmod +x org_runner.py + +``` + +### Run a Connectivity Test + +```bash +./org_runner.py --role-name MyCrossAccountRole --output + +``` + +### Command Line Arguments + +| Argument | Description | Default | +| --- | --- | --- | +| `--role-name` | **Required**. The name of the IAM role to assume in member accounts. | N/A | +| `--profile` | The AWS CLI profile to use for the management session. | `AWS_PROFILE` env var | +| `--region` | The base region for API calls. | `us-east-1` | +| `--output` | Save results to a file. Auto-generates `results_ORGID.ISO-DATE.txt` if no name provided. | None | +| `--sort` | Sort the table by `name` or `id`. | `name` | +| `--debug` | Enables Boto3 HTTP wire-trace logging. | False | + +## Security Best Practices + +* **Least Privilege**: Ensure the `account_task` only uses the clients it needs. +* **Audit Trails**: Use the `--output` flag to maintain a record of administrative actions taken across the Org. +* **Error Handling**: The script uses a `try/except` block per account, ensuring that one failed `AssumeRole` doesn't crash the entire organization-wide run. + +### Utility Capability Overview + +The current version of the utility provides a production-ready framework with the following capabilities: + +* **Identity-Aware Execution**: Automatically detects if it's running in standard AWS, GovCloud, or China partitions and adjusts ARN construction. +* **Intelligent Hierarchy Mapping**: Understands the full logical path of every account, allowing you to see which Business Units are failing or succeeding. +* **Safe Automation**: Uses `AssumeRole` with short-lived credentials; no static secrets are stored. +* **High Scannability**: Specifically designed for large-scale organizations (hundreds of accounts) with sorted tables and summary counts. +* **Audit-Ready Logging**: Generates timestamped, Organization-specific files that capture every line of output, including configuration headers and execution time. +* **Extensible Engine**: By separating the "loop" logic from the "task" logic, it can be repurposed for security audits, cost remediation, or resource inventory in minutes. + +# CHANGELOG + +This table tracks the development of the tool based on your iterative requirements. + +| Version | Focus | Key Features Added | +| --- | --- | --- | +| **1.1.0** | Framework | Refactored into a **Modular Wrapper** with a standalone `account_task` function. | +| **1.0.21** | Automation | Implemented **Dynamic Naming**: `results_{org_id}.{ISO-date}.txt`. | +| **1.0.19** | Optimization | Refactored logging to a list-buffer to remove class complexity and delay file writes. | +| **1.0.18** | Persistence | Added `--output` flag and `Tee` class for dual console/file logging. | +| **1.0.17** | Performance | Added `time.perf_counter()` to provide total elapsed time in the summary. | +| **1.0.16** | Formatting | Expanded Alias field to 64 chars to prevent truncation of long IAM aliases. | +| **1.0.15** | Metadata | Included OU IDs in the summary table for precise infrastructure mapping. | +| **1.0.14** | Reporting | Added **Summary Table** at the end and stripped "Root" from OU path labels. | +| **1.0.13** | Logic Fix | Fixed OU path "accumulation bug" by implementing ID-based path caching. | +| **1.0.12** | Hierarchy | Implemented recursive OU path lookup (e.g., `Prod:App:DB`). | +| **1.0.11** | Sorting | Added `--sort [id|name]` and item numbering for long output lists. | +| **1.0.10** | Diagnostics | Added `--debug` flag for Boto3 wire-trace and removed emojis for log compatibility. | +| **1.0.9** | Partitioning | Added **Partition Awareness** to handle Commercial vs GovCloud (`arn:aws` vs `arn:aws-us-gov`). | +| **1.0.5** | Org Support | Added `organizations:ListAccounts` to loop through all active accounts. | +| **1.0.0** | Initial Script | Basic STS AssumeRole logic for a single account. | + diff --git a/local-app/python-tools/test-cross-organization/config_check.py b/local-app/python-tools/test-cross-organization/config_check.py new file mode 100644 index 00000000..fe4194e7 --- /dev/null +++ b/local-app/python-tools/test-cross-organization/config_check.py @@ -0,0 +1,36 @@ +def config_check_account_task(account_session, account_id, account_name, region): + """ + Task: Checks for AWS Config Recorders in the specified region. + Identifies if 'IncludeGlobalResourceTypes' is enabled (potential cost/duplication). + """ + results = {"alias": "None", "status": "SUCCESS", "details": ""} + + try: + # 1. Get IAM Alias for reporting + iam = account_session.client('iam') + aliases = iam.list_account_aliases().get('AccountAliases', []) + results["alias"] = aliases[0] if aliases else "None" + + # 2. Check AWS Config Recorder + config = account_session.client('config', region_name=region) + recorders = config.describe_configuration_recorders().get('ConfigurationRecorders', []) + + if not recorders: + results["status"] = "WARNING(NoRecorder)" + else: + # Check for Global Resource recording + for r in recorders: + group = r.get('recordingGroup', {}) + if group.get('includeGlobalResourceTypes'): + results["status"] = "ACTION_REQ" + results["details"] = "GlobalResources:True" + else: + results["status"] = "OPTIMIZED" + results["details"] = "GlobalResources:False" + + except ClientError as e: + results["status"] = f"FAIL({e.response['Error']['Code']})" + except Exception as e: + results["status"] = f"FAIL(Error)" + + return results