diff --git a/local-app/python-tools/gfl-resource-actions/.github/copilot-instructions.md b/local-app/python-tools/gfl-resource-actions/.github/copilot-instructions.md new file mode 100644 index 00000000..60c99ddd --- /dev/null +++ b/local-app/python-tools/gfl-resource-actions/.github/copilot-instructions.md @@ -0,0 +1,113 @@ +# GitHub Copilot Instructions: AWS Resource Management Tool + +This document provides instructions for AI assistants working on the AWS Resource Management Tool codebase. + +## Project Overview + +This Python tool discovers, starts, and stops AWS resources across multiple accounts (primarily in GovCloud environments). The primary purpose is cost management by automatically managing resource states. + +## Code Architecture + +### Package Structure +``` +aws_resource_management/ # Main package +├── __init__.py +├── aws_utils.py # AWS credential/authentication utilities +├── cli.py # Command-line interface +├── core.py # Main business logic (ResourceManager) +├── discovery.py # Resource discovery logic +├── managers/ # Resource-specific managers +│ ├── base.py # Base resource manager class +│ ├── ec2.py # EC2-specific implementation +│ ├── eks.py # EKS-specific implementation +│ ├── emr.py # EMR-specific implementation +│ └── rds.py # RDS-specific implementation +└── reporting.py # Reporting utilities +``` + +### Key Classes and Design Patterns + +1. `ResourceManager` (in `core.py`): Main orchestrator class that handles cross-account operations + - Uses ThreadPoolExecutor for parallel account processing + - Delegates resource-specific operations to appropriate managers + +2. Resource Managers (in `managers/`): + - All extend the `ResourceManager` base class + - Implement `start()` and `stop()` methods specific to their resource type + - Handle AWS API interactions for their specific service + +3. Credential Management (in `aws_utils.py`): + - Uses caching to minimize API calls + - Handles AWS SSO profiles and role assumption + - Automatic partition detection (AWS commercial, GovCloud, China) + +## Implementation Details + +### AWS Authentication Flow +1. First attempt to use AWS SSO profiles from `~/.aws/config` +2. Fall back to role assumption with `OrganizationAccountAccessRole` or `AWSControlTowerExecution` +3. Credential information is cached to reduce API calls + +### AWS API Interaction Patterns +1. Always use pagination handling for AWS API responses +2. Always use try/except blocks when making AWS API calls +3. Always check for error codes like "AuthFailure" and handle them gracefully +4. Use region detection and filtering to minimize API calls + +### CLI Commands +The tool exposes a CLI through `aws-resource-mgmt` with these main options: +- `--start`/`--stop` to specify action +- `--resource-type` to select resource type (ec2, rds, eks, emr, all) +- `--region`/`--exclude-region` to specify regions +- `--account`/`--exclude-account` to specify accounts +- `--dry-run` to simulate without making changes + +## Example Tasks and Prompts + +### Adding New Resource Type Support +When asked to add support for a new AWS service (e.g., Lambda): + +1. Create a new manager class in `aws_resource_management/managers/lambda.py` +2. Implement discovery function in `discovery.py` +3. Add to `RESOURCE_TYPES` list in `core.py` +4. Update CLI choices in `cli.py` + +### Fixing Authentication Issues +For authentication issues, check: +1. `aws_utils.py` credential handling functions +2. Account and profile lookup logic +3. Role assumption and cache handling + +### Optimizing Performance +For performance optimization: +1. Look at caching mechanisms in `aws_utils.py` +2. Check thread pool configuration in `core.py` +3. Review pagination handling in API calls + +## Gotchas and Important Notes + +1. **AWS Partition Handling**: The code must work across AWS partitions (commercial, GovCloud, and China) - always use partition detection. + +2. **Error Handling**: Multiple layers of error handling are implemented - avoid removing or bypassing these checks. + +3. **Caching**: Most credential and region lookup operations use caching - maintain this pattern. + +4. **Import Structure**: Maintain correct imports to avoid circular dependencies. + +5. **Pagination**: Always handle pagination in AWS API responses. + +6. **Credentials**: Never hardcode credentials or suggest hardcoded credential solutions. + +7. **Type Annotations**: All functions should have proper type annotations. + +## Common Refactoring Strategies + +1. When refactoring, maintain the class hierarchy and delegation pattern. + +2. Use common utilities from `aws_utils.py` rather than reimplementing functionality. + +3. Maintain consistent error handling and logging patterns. + +4. Keep CLI argument parsing in `cli.py` and business logic in `core.py`. + +5. Follow existing pagination and caching patterns when adding new API interactions. diff --git a/local-app/python-tools/gfl-resource-actions/.gitignore b/local-app/python-tools/gfl-resource-actions/.gitignore index db01cfc1..d47e967f 100644 --- a/local-app/python-tools/gfl-resource-actions/.gitignore +++ b/local-app/python-tools/gfl-resource-actions/.gitignore @@ -1,55 +1,42 @@ -# Byte-compiled / optimized / DLL files +# Python __pycache__/ *.py[cod] *$py.class - -# Distribution / packaging -dist/ +*.so +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ *.egg-info/ +.installed.cfg *.egg -# Virtual environments -venv/ +# Logs and data +*.log +logs/ +*.csv +*.db +*.sqlite3 + +# Environment variables +.env +.venv env/ +venv/ ENV/ -.env/ -.venv/ - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ -nosetests.xml -# IDE specific files +# IDE files .idea/ .vscode/ *.swp *.swo -.DS_Store - -# Project specific -config.local.py -credentials.json -*.log -logs/ -temp/ - -# AWS -.aws-sam/ -samconfig.toml -.chalice/ -.aws_credentials - -# Terraform -.terraform/ -*.tfstate -*.tfstate.backup -*.tfplan -.terraform.lock.hcl +*~ diff --git a/local-app/python-tools/gfl-resource-actions/CONTRIBUTING.md b/local-app/python-tools/gfl-resource-actions/CONTRIBUTING.md new file mode 100644 index 00000000..2f21b0de --- /dev/null +++ b/local-app/python-tools/gfl-resource-actions/CONTRIBUTING.md @@ -0,0 +1,118 @@ +# Contributing to AWS Resource Management Tool + +This guide provides essential information for developers who want to contribute to or maintain the AWS Resource Management Tool. + +## Project Overview + +The AWS Resource Management Tool is a Python application designed to discover, start, and stop AWS resources across multiple accounts, with special support for GovCloud environments. It helps manage costs by enabling automatic resource state management. + +## Code Structure +gfl-resource-actions/ +├── aws_resource_management/ # Main package +│ ├── init.py +│ ├── aws_utils.py # AWS authentication and utilities +│ ├── cli.py # Main CLI entry point +│ ├── config_manager.py # Configuration management +│ ├── core.py # Core business logic +│ ├── discovery.py # Resource discovery +│ ├── logging_setup.py # Logging configuration +│ ├── managers/ # Resource type managers +│ │ ├── init.py +│ │ ├── base.py # Base manager class +│ │ ├── ec2.py # EC2 resource manager +│ │ ├── eks.py # EKS resource manager +│ │ ├── emr.py # EMR resource manager +│ │ └── rds.py # RDS resource manager +│ └── reporting.py # Report generation +├── config.py # Global configuration +├── logging_utils.py # Logging utilities +├── Makefile # Build and development commands +├── README.md # Project documentation +└── setup.py # Package installation + + +## Key Components + +1. **ResourceManager** (`core.py`) - Main orchestrator for resource operations across accounts +2. **CLI** (`cli.py`) - Command-line interface for the tool +3. **Resource Managers** (`managers/*`) - Handle specific resource types (EC2, RDS, EKS, EMR) +4. **AWS Utils** (`aws_utils.py`) - Credential management and AWS API interaction + +## Development Workflow + +### Setting Up + +1. Clone the repository +2. Install dependencies: `make install-dev` +3. Install the package in development mode: `make install` + +### Common Tasks + +#### Running the Tool + +```bash +# Via the installed command +aws-resource-mgmt --stop --dry-run --resource-type all + +# Using the module directly +python -m aws_resource_management.cli --start --resource-type ec2 +``` + + +##### Adding a New Resource Type +1. Create a new manager class in aws_resource_management/managers/ +1. Extend the base ResourceManager class +1. Implement the required start() and stop() methods +1. Add the new resource type to RESOURCE_TYPES in core.py +1. Update CLI argument choices in cli.py + +###### Modifying Resource Discovery +1. Edit the appropriate discovery function in discovery.py to modify how resources are found. + +###### Testing Changes + +```bash +# Dry run (no actual changes) +make run-dry-run + +# Run specific test +pytest tests/test_specific_file.py -v +``` + +### Best Practices +1. Credentials Handling: Never hardcode credentials. Use AWS SSO or assume-role. +1. Error Handling: Always use proper try/except blocks when interacting with AWS APIs. +1. Logging: Use the established logging framework (logger from logging_setup). +1. Pagination: Always handle pagination in AWS API responses. +1. Caching: Use caching mechanisms for frequent API calls. + +#### Project Standards +1. Code Style: Use Black for formatting and isort for import ordering +1. Type Hints: Include type hints for all function parameters and return values +1. Documentation: Document all classes and functions with docstrings +1. Tests: Add tests for all new functionality +##### Command Reference +```bash +# Format code +make format + +# Run linter checks +make lint + +# Run tests +make test + +# Build package +make dist + +# Install development dependencies +make install-dev + +# Clean temporary files +make clean +``` + +##### Troubleshooting +1. Authentication Issues: Ensure AWS SSO is properly configured or the appropriate roles exist. +1. Import Errors: Check that you're using the correct module paths. +1. Missing Dependencies: Run make install-dev to install all requirements. diff --git a/local-app/python-tools/gfl-resource-actions/README.md b/local-app/python-tools/gfl-resource-actions/README.md index 083afbbf..306f380b 100644 --- a/local-app/python-tools/gfl-resource-actions/README.md +++ b/local-app/python-tools/gfl-resource-actions/README.md @@ -24,6 +24,21 @@ A collection of Python utilities for managing and interacting with AWS resources pip install boto3 ``` +## Command-Line Usage + +The package provides a command-line interface for managing AWS resources: + +```bash +# Stop all resources in dry-run mode +aws-resource-mgmt --stop --dry-run --resource-type all + +# Start EC2 instances +aws-resource-mgmt --start --resource-type ec2 + +# Stop RDS instances in specific regions +aws-resource-mgmt --stop --resource-type rds --region us-east-1 --region us-west-2 +``` + ## Usage ### Basic Usage diff --git a/local-app/python-tools/gfl-resource-actions/aws_resource_management/cli.py b/local-app/python-tools/gfl-resource-actions/aws_resource_management/cli.py index 7b964f6f..3e0a040c 100644 --- a/local-app/python-tools/gfl-resource-actions/aws_resource_management/cli.py +++ b/local-app/python-tools/gfl-resource-actions/aws_resource_management/cli.py @@ -1,20 +1,151 @@ +#!/usr/bin/env python3 """ -DEPRECATED: This module is deprecated in favor of cli_optimized.py. -The original CLI implementation had performance issues that caused multiple -account scans for each resource type. +CLI for AWS resource management tool. """ +import argparse +import logging import sys -import warnings +import traceback +from typing import Any, Dict, List, Optional -from aws_resource_management.cli_optimized import main +from aws_resource_management.config_manager import get_config +from aws_resource_management.core import ResourceManager +from aws_resource_management.logging_setup import setup_logging + +logger = setup_logging() +config = get_config() + + +def parse_args(): + """Parse command line arguments.""" + parser = argparse.ArgumentParser(description="AWS Resource Management CLI") + + # Action arguments + action_group = parser.add_mutually_exclusive_group(required=True) + action_group.add_argument("--stop", action="store_true", help="Stop resources") + action_group.add_argument("--start", action="store_true", help="Start resources") + + # Resource selection + parser.add_argument( + "--resource-type", + choices=["ecr", "ec2", "rds", "eks", "emr", "all"], + default="all", + help="Resource type to manage (default: all)", + ) + + # Account filtering + parser.add_argument("--account", help="Specific account ID to process") + parser.add_argument( + "--exclude-account", + action="append", + dest="exclude_accounts", + help="Account ID to exclude (can be used multiple times)", + ) + + # Region options + parser.add_argument( + "--region", + action="append", + dest="regions", + help="Regions to scan (can be used multiple times)", + ) + parser.add_argument( + "--exclude-region", + action="append", + dest="exclude_regions", + help="Regions to exclude (can be used multiple times)", + ) + parser.add_argument( + "--discover-regions", + action="store_true", + help="Automatically discover enabled regions", + ) + parser.add_argument( + "--partition", + choices=["aws", "aws-us-gov", "aws-cn"], + default="aws-us-gov", + help="AWS partition to operate in (default: aws-us-gov)", + ) + + # Authentication + parser.add_argument("--profile", help="AWS profile to use") + parser.add_argument( + "--use-profiles", + action="store_true", + help="Use AWS profiles for account discovery", + ) + + # Dry run + parser.add_argument( + "--dry-run", + action="store_true", + help="Show what would be done without making changes", + ) + + return parser.parse_args() + + +def main(): + """Main CLI entry point.""" + args = parse_args() + + try: + # Determine action + action = "stop" if args.stop else "start" + + # Determine resource types to process + exclude_resources = [] + if args.resource_type != "all": + # If specific resource type is selected, exclude all others + all_resource_types = ["ecr", "ec2", "rds", "eks", "emr"] + exclude_resources = [ + rt for rt in all_resource_types if rt != args.resource_type + ] + + logger.info( + f"Processing {args.resource_type} resources in {args.regions or 'all enabled'} regions for {action} action" + ) + + # Initialize resource manager + resource_manager = ResourceManager( + profile_name=args.profile, + use_profiles=args.use_profiles, + discover_regions=args.discover_regions, + partition=args.partition, + ) + + # Create account inclusion/exclusion list + exclude_accounts = args.exclude_accounts or [] + if args.account: + # If specific account is given, exclude all others by default + # But don't add the specified account to the exclusion list + logger.info(f"Processing only account {args.account}") + # We'll handle this by post-filtering the account list in resource_manager + + # Process accounts - single scan for all resource types + stats = resource_manager.process_accounts( + action=action, + regions=args.regions or [], + exclude_accounts=exclude_accounts, + exclude_resources=exclude_resources, + exclude_regions=args.exclude_regions or [], + dry_run=args.dry_run, + ) + + logger.info(f"Completed {action} action for {args.resource_type} resources") + + # Exit with success code + sys.exit(0) + + except KeyboardInterrupt: + logger.warning("Operation interrupted by user. Exiting gracefully...") + sys.exit(1) + except Exception as e: + logger.error(f"Unhandled exception: {str(e)}") + logger.debug(traceback.format_exc()) + sys.exit(1) -warnings.warn( - "The cli module is deprecated. Please use cli_optimized module instead.", - DeprecationWarning, - stacklevel=2, -) -# Redirect to the optimized CLI if __name__ == "__main__": - sys.exit(main()) + main() diff --git a/local-app/python-tools/gfl-resource-actions/aws_resource_management/cli_optimized.py b/local-app/python-tools/gfl-resource-actions/aws_resource_management/cli_optimized.py deleted file mode 100644 index f981bc8a..00000000 --- a/local-app/python-tools/gfl-resource-actions/aws_resource_management/cli_optimized.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python3 -""" -Optimized CLI for AWS resource management tool. -This version scans accounts only once for all resource types. -""" - -import argparse -import logging -import sys -import traceback -from typing import Any, Dict, List, Optional - -from aws_resource_management.config_manager import get_config -from aws_resource_management.core import ResourceManager -from aws_resource_management.logging_setup import setup_logging - -logger = setup_logging() -config = get_config() - - -def parse_args(): - """Parse command line arguments.""" - parser = argparse.ArgumentParser(description="AWS Resource Management CLI") - - # Action arguments - action_group = parser.add_mutually_exclusive_group(required=True) - action_group.add_argument("--stop", action="store_true", help="Stop resources") - action_group.add_argument("--start", action="store_true", help="Start resources") - - # Resource selection - parser.add_argument( - "--resource-type", - choices=["ecr", "ec2", "rds", "eks", "emr", "all"], - default="all", - help="Resource type to manage (default: all)", - ) - - # Account filtering - parser.add_argument("--account", help="Specific account ID to process") - parser.add_argument( - "--exclude-account", - action="append", - dest="exclude_accounts", - help="Account ID to exclude (can be used multiple times)", - ) - - # Region options - parser.add_argument( - "--region", - action="append", - dest="regions", - help="Regions to scan (can be used multiple times)", - ) - parser.add_argument( - "--exclude-region", - action="append", - dest="exclude_regions", - help="Regions to exclude (can be used multiple times)", - ) - parser.add_argument( - "--discover-regions", - action="store_true", - help="Automatically discover enabled regions", - ) - parser.add_argument( - "--partition", - choices=["aws", "aws-us-gov", "aws-cn"], - default="aws-us-gov", # Added missing comma - help="AWS partition to operate in (default: aws-us-gov)", - ) - - # Authentication - parser.add_argument("--profile", help="AWS profile to use") - parser.add_argument( - "--use-profiles", - action="store_true", - help="Use AWS profiles for account discovery", - ) - - # Dry run - parser.add_argument( - "--dry-run", - action="store_true", - help="Show what would be done without making changes", - ) - - return parser.parse_args() - - -def main(): - """Main CLI entry point.""" - args = parse_args() - - try: - # Determine action - action = "stop" if args.stop else "start" - - # Determine resource types to process - exclude_resources = [] - if args.resource_type != "all": - # If specific resource type is selected, exclude all others - all_resource_types = ["ecr", "ec2", "rds", "eks", "emr"] - exclude_resources = [ - rt for rt in all_resource_types if rt != args.resource_type - ] - - logger.info( - f"Processing {args.resource_type} resources in {args.regions or 'all enabled'} regions for {action} action" - ) - - # Initialize resource manager - resource_manager = ResourceManager( - profile_name=args.profile, - use_profiles=args.use_profiles, - discover_regions=args.discover_regions, - partition=args.partition, - ) - - # Create account inclusion/exclusion list - exclude_accounts = args.exclude_accounts or [] - if args.account: - # If specific account is given, exclude all others by default - # But don't add the specified account to the exclusion list - logger.info(f"Processing only account {args.account}") - # We'll handle this by post-filtering the account list in resource_manager - - # Process accounts - single scan for all resource types - stats = resource_manager.process_accounts( - action=action, - regions=args.regions or [], - exclude_accounts=exclude_accounts, - exclude_resources=exclude_resources, - exclude_regions=args.exclude_regions or [], - dry_run=args.dry_run, - ) - - logger.info(f"Completed {action} action for {args.resource_type} resources") - - # Exit with success code - sys.exit(0) - - except KeyboardInterrupt: - logger.warning("Operation interrupted by user. Exiting gracefully...") - sys.exit(1) - except Exception as e: - logger.error(f"Unhandled exception: {str(e)}") - logger.debug(traceback.format_exc()) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/local-app/python-tools/gfl-resource-actions/aws_resource_management/resource_controller.py b/local-app/python-tools/gfl-resource-actions/aws_resource_management/resource_controller.py deleted file mode 100644 index f40023b5..00000000 --- a/local-app/python-tools/gfl-resource-actions/aws_resource_management/resource_controller.py +++ /dev/null @@ -1,272 +0,0 @@ -""" -DEPRECATED: This module is deprecated in favor of core.py. -Resource controller module. -Handles the core business logic for managing AWS resources. -""" - -import warnings - -warnings.warn( - "The resource_controller module is deprecated. Please use the core module instead.", - DeprecationWarning, - stacklevel=2, -) - -import logging -import sys -from typing import Any, Dict, List, Optional, Union - -from aws_resource_management.aws_utils import ( - get_available_regions, # Changed from get_enabled_regions -) -from aws_resource_management.aws_utils import ( - get_account_list, - get_credentials, -) -from aws_resource_management.config_manager import get_config -from aws_resource_management.core import ResourceManager -from aws_resource_management.logging_setup import setup_logging -from aws_resource_management.managers.ec2 import EC2Manager -from aws_resource_management.managers.eks import EKSManager -from aws_resource_management.managers.emr import EMRManager -from aws_resource_management.managers.rds import RDSManager - -logger = setup_logging() -config = get_config() - - -class ResourceController: - """Controller class that orchestrates resource management across accounts.""" - - def __init__( - self, - profile_name: Optional[str] = None, - use_profiles: bool = False, - discover_regions: bool = False, - partition: Optional[str] = None, - ): - """ - Initialize the resource controller. - - Args: - profile_name: Optional AWS profile name to use for all operations - use_profiles: Whether to use AWS profiles from ~/.aws/config - discover_regions: Whether to automatically discover enabled regions - partition: AWS partition to operate in (aws, aws-us-gov, aws-cn) - """ - self.config = get_config() - self.resource_manager = ResourceManager( - profile_name=profile_name, - use_profiles=use_profiles, - discover_regions=discover_regions, - partition=partition, - ) - self.discover_regions = discover_regions - self.partition = partition - - def process_resources( - self, - action: str, - resource_type: str, - regions: List[str], - exclude_accounts: List[str] = None, - exclude_resources: List[str] = None, - exclude_regions: List[str] = None, - dry_run: bool = False, - ) -> Dict[str, Any]: - """ - Process resources of the specified type across accounts. - - Args: - action: Action to perform (stop or start) - resource_type: Type of resources to manage - regions: List of regions to scan - exclude_accounts: List of account IDs to exclude - exclude_resources: List of resource types to exclude - exclude_regions: List of regions to exclude - dry_run: Whether to perform a dry run - - Returns: - Statistics dictionary with results - """ - try: - # Validate action - if action not in ["start", "stop"]: - logger.error(f"Invalid action: {action}. Must be 'start' or 'stop'") - return {"error": f"Invalid action: {action}"} - - # Validate resource type - if resource_type not in ["ec2", "rds", "eks", "emr"]: - logger.error(f"Invalid resource type: {resource_type}") - return {"error": f"Invalid resource type: {resource_type}"} - - region_msg = ( - "all enabled regions" if self.discover_regions else ", ".join(regions) - ) - logger.info( - f"Processing {resource_type} resources in {region_msg} for {action} action" - ) - - # Process accounts through the resource manager - stats = self.resource_manager.process_accounts( - action=action, - regions=regions, - exclude_accounts=exclude_accounts, - exclude_resources=exclude_resources, - exclude_regions=exclude_regions, - dry_run=dry_run, - ) - - # Log summary - logger.info(f"Completed {action} action for {resource_type} resources") - logger.info(f"Accounts processed: {stats.get('accounts_processed', 0)}") - logger.info(f"Accounts skipped: {stats.get('accounts_skipped', 0)}") - logger.info(f"Resources processed: {stats.get('resources_processed', 0)}") - logger.info(f"Resources skipped: {stats.get('resources_skipped', 0)}") - logger.info(f"Regions processed: {stats.get('regions_processed', 0)}") - - if stats.get("errors"): - logger.warning(f"Encountered {len(stats.get('errors', []))} errors") - - return stats - - except KeyboardInterrupt: - # Propagate the keyboard interrupt to let the CLI handle it - logger.warning("Operation interrupted by user in resource controller") - raise - - def list_resources( - self, - resource_type: str, - regions: List[str], - exclude_accounts: List[str] = None, - exclude_regions: List[str] = None, - ) -> Dict[str, Any]: - """ - List resources of the specified type across accounts. - - Args: - resource_type: Type of resources to list - regions: List of regions to scan - exclude_accounts: List of account IDs to exclude - exclude_regions: List of regions to exclude - - Returns: - Dictionary containing the discovered resources - """ - logger.info(f"Listing {resource_type} resources in {', '.join(regions)}") - - # Get account list - accounts = get_account_list() - if not accounts: - logger.warning("No accounts found or error retrieving accounts") - return {"error": "No accounts found or error retrieving accounts"} - - results = {"resources": [], "accounts_processed": 0, "accounts_skipped": 0} - - # Process each account - for account in accounts: - account_id = account.get("account_id") - account_name = account.get("account_name") - - # Skip excluded accounts - if exclude_accounts and account_id in exclude_accounts: - logger.info(f"Skipping excluded account {account_id} ({account_name})") - results["accounts_skipped"] += 1 - continue - - try: - # Get credentials for the account - credentials = get_credentials(account_id) - if not credentials: - logger.warning( - f"Could not obtain credentials for account {account_id}" - ) - results["accounts_skipped"] += 1 - continue - - # Create the appropriate manager for the resource type - manager = self._create_resource_manager( - resource_type=resource_type, - credentials=credentials, - account_id=account_id, - account_name=account_name, - ) - - if not manager: - logger.error( - f"Failed to create resource manager for type {resource_type}" - ) - continue - - # Get resources for each region - for region in regions: - resources = manager.list_resources(region) - results["resources"].extend(resources) - - results["accounts_processed"] += 1 - - except Exception as e: - logger.error( - f"Error listing resources for account {account_id}: {str(e)}" - ) - - logger.info(f"Found {len(results['resources'])} {resource_type} resources") - return results - - def _create_resource_manager( - self, - resource_type: str, - credentials: Dict[str, str], - account_id: str, - account_name: Optional[str] = None, - ) -> Optional[Any]: - """ - Create the appropriate resource manager based on the resource type. - - Args: - resource_type: Type of resources to manage - credentials: AWS credentials - account_id: AWS account ID - account_name: AWS account name - - Returns: - Resource manager instance or None if invalid type - """ - if resource_type == "ec2": - return EC2Manager(credentials, account_id, account_name) - elif resource_type == "rds": - return RDSManager(credentials, account_id, account_name) - elif resource_type == "eks": - return EKSManager(credentials, account_id, account_name) - elif resource_type == "emr": - return EMRManager(credentials, account_id, account_name) - else: - return None - - def get_regions_for_account(self, account_id: str) -> List[str]: - """ - Get enabled regions for a specific account. - - Args: - account_id: AWS account ID - - Returns: - List of enabled region names - """ - try: - # Get credentials for the account - credentials = get_credentials(account_id) - if not credentials: - logger.warning(f"Could not obtain credentials for account {account_id}") - return [] - - # Get enabled regions - regions = get_available_regions( - credentials, partition=self.partition - ) # Changed from get_enabled_regions - return regions - - except Exception as e: - logger.error(f"Error getting regions for account {account_id}: {str(e)}") - return [] diff --git a/local-app/python-tools/gfl-resource-actions/main.py b/local-app/python-tools/gfl-resource-actions/main.py deleted file mode 100644 index a60ead0a..00000000 --- a/local-app/python-tools/gfl-resource-actions/main.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python3 -""" -Main entry point for AWS Resource Management tool. -""" - -import argparse -import concurrent.futures # Add missing import -import sys # Add missing import -from concurrent.futures import ThreadPoolExecutor - -import config -from aws_resource_management.aws_utils import ( - assume_role, - get_available_regions, - get_organization_accounts, -) -from discovery import get_account_resources -from logging_utils import setup_logging -from managers import EC2Manager, EKSManager, EMRManager, RDSManager - -logger = setup_logging() - - -def process_region(credentials, region, exclude_resources): - """Process resources in a single region.""" - try: - return get_account_resources(credentials, [region], exclude_resources) - except Exception as e: - logger.error(f"Error processing region {region}: {e}") - return { - "ec2_instances": [], - "rds_instances": [], - "eks_clusters": [], - "emr_clusters": [], - } - - -def main(): - # ...existing code until after args parsing... - - # Process each account - for account in accounts: - logger.info( - f"Processing account: {account['name']} ({account['id']}) in {len(regions)} regions" - ) - - credentials = assume_role(account["id"]) - if not credentials: - logger.warning( - f"Skipping account {account['id']} due to role assumption failure" - ) - continue - - # Process regions in parallel - all_resources = { - "ec2_instances": [], - "rds_instances": [], - "eks_clusters": [], - "emr_clusters": [], - } - - with ThreadPoolExecutor(max_workers=4) as executor: - future_to_region = { - executor.submit( - process_region, credentials, region, exclude_resources - ): region - for region in regions - } - - for future in concurrent.futures.as_completed(future_to_region): - region = future_to_region[future] - try: - region_resources = future.result() - for resource_type in all_resources: - all_resources[resource_type].extend( - region_resources[resource_type] - ) - except Exception as e: - logger.error(f"Error processing region {region}: {e}") - - # ...rest of existing code... - - -if __name__ == "__main__": - sys.exit(main())