-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
132 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | | ||
|
|
36 changes: 36 additions & 0 deletions
36
local-app/python-tools/test-cross-organization/config_check.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |