runbooks 1.0.0__py3-none-any.whl → 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/WEIGHT_CONFIG_README.md +368 -0
  3. runbooks/cfat/app.ts +27 -19
  4. runbooks/cfat/assessment/runner.py +6 -5
  5. runbooks/cfat/tests/test_weight_configuration.ts +449 -0
  6. runbooks/cfat/weight_config.ts +574 -0
  7. runbooks/cloudops/models.py +20 -14
  8. runbooks/common/__init__.py +26 -9
  9. runbooks/common/aws_pricing.py +1070 -105
  10. runbooks/common/aws_pricing_api.py +276 -44
  11. runbooks/common/date_utils.py +115 -0
  12. runbooks/common/dry_run_examples.py +587 -0
  13. runbooks/common/dry_run_framework.py +520 -0
  14. runbooks/common/enhanced_exception_handler.py +10 -7
  15. runbooks/common/mcp_cost_explorer_integration.py +5 -4
  16. runbooks/common/memory_optimization.py +533 -0
  17. runbooks/common/performance_optimization_engine.py +1153 -0
  18. runbooks/common/profile_utils.py +86 -118
  19. runbooks/common/rich_utils.py +3 -3
  20. runbooks/common/sre_performance_suite.py +574 -0
  21. runbooks/finops/business_case_config.py +314 -0
  22. runbooks/finops/cost_processor.py +19 -4
  23. runbooks/finops/dashboard_runner.py +47 -28
  24. runbooks/finops/ebs_cost_optimizer.py +1 -1
  25. runbooks/finops/ebs_optimizer.py +56 -9
  26. runbooks/finops/embedded_mcp_validator.py +642 -36
  27. runbooks/finops/enhanced_trend_visualization.py +7 -2
  28. runbooks/finops/executive_export.py +789 -0
  29. runbooks/finops/finops_dashboard.py +6 -5
  30. runbooks/finops/finops_scenarios.py +34 -27
  31. runbooks/finops/iam_guidance.py +6 -1
  32. runbooks/finops/nat_gateway_optimizer.py +46 -27
  33. runbooks/finops/notebook_utils.py +1 -1
  34. runbooks/finops/schemas.py +73 -58
  35. runbooks/finops/single_dashboard.py +20 -4
  36. runbooks/finops/tests/test_integration.py +3 -1
  37. runbooks/finops/vpc_cleanup_exporter.py +2 -1
  38. runbooks/finops/vpc_cleanup_optimizer.py +22 -29
  39. runbooks/inventory/core/collector.py +51 -28
  40. runbooks/inventory/discovery.md +197 -247
  41. runbooks/inventory/inventory_modules.py +2 -2
  42. runbooks/inventory/list_ec2_instances.py +3 -3
  43. runbooks/inventory/models/account.py +5 -3
  44. runbooks/inventory/models/inventory.py +1 -1
  45. runbooks/inventory/models/resource.py +5 -3
  46. runbooks/inventory/organizations_discovery.py +102 -13
  47. runbooks/inventory/unified_validation_engine.py +2 -15
  48. runbooks/main.py +255 -92
  49. runbooks/operate/base.py +9 -6
  50. runbooks/operate/deployment_framework.py +5 -4
  51. runbooks/operate/deployment_validator.py +6 -5
  52. runbooks/operate/mcp_integration.py +6 -5
  53. runbooks/operate/networking_cost_heatmap.py +17 -13
  54. runbooks/operate/vpc_operations.py +82 -13
  55. runbooks/remediation/base.py +3 -1
  56. runbooks/remediation/commons.py +5 -5
  57. runbooks/remediation/commvault_ec2_analysis.py +66 -18
  58. runbooks/remediation/config/accounts_example.json +31 -0
  59. runbooks/remediation/multi_account.py +120 -7
  60. runbooks/remediation/remediation_cli.py +710 -0
  61. runbooks/remediation/universal_account_discovery.py +377 -0
  62. runbooks/remediation/workspaces_list.py +2 -2
  63. runbooks/security/compliance_automation_engine.py +99 -20
  64. runbooks/security/config/__init__.py +24 -0
  65. runbooks/security/config/compliance_config.py +255 -0
  66. runbooks/security/config/compliance_weights_example.json +22 -0
  67. runbooks/security/config_template_generator.py +500 -0
  68. runbooks/security/security_cli.py +377 -0
  69. runbooks/validation/cli.py +8 -7
  70. runbooks/validation/comprehensive_2way_validator.py +26 -15
  71. runbooks/validation/mcp_validator.py +62 -8
  72. runbooks/vpc/config.py +49 -15
  73. runbooks/vpc/cross_account_session.py +5 -1
  74. runbooks/vpc/heatmap_engine.py +438 -59
  75. runbooks/vpc/mcp_no_eni_validator.py +115 -36
  76. runbooks/vpc/performance_optimized_analyzer.py +546 -0
  77. runbooks/vpc/runbooks_adapter.py +33 -12
  78. runbooks/vpc/tests/conftest.py +4 -2
  79. runbooks/vpc/tests/test_cost_engine.py +3 -1
  80. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/METADATA +1 -1
  81. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/RECORD +85 -79
  82. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  83. runbooks/finops/runbooks.security.report_generator.log +0 -0
  84. runbooks/finops/runbooks.security.run_script.log +0 -0
  85. runbooks/finops/runbooks.security.security_export.log +0 -0
  86. runbooks/finops/tests/results_test_finops_dashboard.xml +0 -1
  87. runbooks/inventory/artifacts/scale-optimize-status.txt +0 -12
  88. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  89. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  90. runbooks/inventory/runbooks.security.run_script.log +0 -0
  91. runbooks/inventory/runbooks.security.security_export.log +0 -0
  92. runbooks/vpc/runbooks.inventory.organizations_discovery.log +0 -0
  93. runbooks/vpc/runbooks.security.report_generator.log +0 -0
  94. runbooks/vpc/runbooks.security.run_script.log +0 -0
  95. runbooks/vpc/runbooks.security.security_export.log +0 -0
  96. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/WHEEL +0 -0
  97. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/entry_points.txt +0 -0
  98. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/licenses/LICENSE +0 -0
  99. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/top_level.txt +0 -0
@@ -610,18 +610,9 @@ class VPCCleanupOptimizer:
610
610
 
611
611
  print_header("🌐 Real-Time NO-ENI VPC Discovery", "MCP-Validated VPC Cleanup Analysis")
612
612
 
613
- # Configure enterprise profiles for MCP validation - Universal compatibility
614
- from runbooks.common.profile_utils import get_enterprise_profile_mapping
615
- enterprise_profiles = get_enterprise_profile_mapping()
616
-
617
- # Override with current profile if available
618
- current_profile_type = self._determine_profile_type(self.profile)
619
- if current_profile_type:
620
- enterprise_profiles[current_profile_type] = self.profile
621
-
622
- # Initialize MCP validator for dynamic discovery
613
+ # Initialize MCP validator with universal profile support
623
614
  print_info("🔧 Initializing dynamic MCP validator...")
624
- mcp_validator = NOENIVPCMCPValidator(enterprise_profiles)
615
+ mcp_validator = NOENIVPCMCPValidator(user_profile=self.profile)
625
616
 
626
617
  # Perform dynamic discovery across all accounts
627
618
  print_info("🚀 Starting real-time discovery across all AWS accounts...")
@@ -641,7 +632,7 @@ class VPCCleanupOptimizer:
641
632
  # Get detailed VPC information for each NO-ENI VPC
642
633
  try:
643
634
  # Use appropriate session for this account
644
- session = self._get_session_for_account(target.account_id, enterprise_profiles)
635
+ session = self._get_session_for_account(target.account_id)
645
636
  ec2_client = session.client('ec2', region_name=target.region)
646
637
 
647
638
  # Get VPC details
@@ -708,26 +699,28 @@ class VPCCleanupOptimizer:
708
699
  return 'CENTRALISED_OPS'
709
700
  return None
710
701
 
711
- def _get_session_for_account(self, account_id: str, enterprise_profiles: Dict[str, str]) -> boto3.Session:
712
- """Get appropriate session for accessing a specific account."""
702
+ def _get_session_for_account(self, account_id: str) -> boto3.Session:
703
+ """Get appropriate session for accessing a specific account using universal profile management."""
704
+ from runbooks.common.profile_utils import get_profile_for_operation
705
+
713
706
  # In enterprise setup, would assume role here
714
- # For now, return session with best available profile
707
+ # For now, return session with best available profile using three-tier priority system
715
708
 
716
- # Priority order for account access
717
- profile_priority = ['MANAGEMENT', 'CENTRALISED_OPS', 'BILLING']
709
+ # Try different operation types in priority order
710
+ profile_types = ['management', 'operational', 'billing']
718
711
 
719
- for profile_type in profile_priority:
720
- if profile_type in enterprise_profiles:
721
- try:
722
- session = boto3.Session(profile_name=enterprise_profiles[profile_type])
723
- # Verify access
724
- sts_client = session.client('sts')
725
- identity = sts_client.get_caller_identity()
726
-
727
- if identity['Account'] == account_id:
728
- return session
729
- except Exception:
730
- continue
712
+ for profile_type in profile_types:
713
+ try:
714
+ profile_name = get_profile_for_operation(profile_type, self.profile)
715
+ session = boto3.Session(profile_name=profile_name)
716
+ # Verify access
717
+ sts_client = session.client('sts')
718
+ identity = sts_client.get_caller_identity()
719
+
720
+ if identity['Account'] == account_id:
721
+ return session
722
+ except Exception:
723
+ continue
731
724
 
732
725
  # Fallback to current session
733
726
  return self.session
@@ -42,12 +42,13 @@ try:
42
42
  ENHANCED_PROFILES_AVAILABLE = True
43
43
  except ImportError:
44
44
  ENHANCED_PROFILES_AVAILABLE = False
45
- # Fallback profile definitions
45
+ # Fallback profile definitions with universal environment support
46
+ import os
46
47
  ENTERPRISE_PROFILES = {
47
- "BILLING_PROFILE": "ams-admin-Billing-ReadOnlyAccess-909135376185",
48
- "MANAGEMENT_PROFILE": "ams-admin-ReadOnlyAccess-909135376185",
49
- "CENTRALISED_OPS_PROFILE": "ams-centralised-ops-ReadOnlyAccess-335083429030",
50
- "SINGLE_ACCOUNT_PROFILE": "ams-shared-services-non-prod-ReadOnlyAccess-499201730520",
48
+ "BILLING_PROFILE": os.getenv("BILLING_PROFILE", "default-billing-profile"),
49
+ "MANAGEMENT_PROFILE": os.getenv("MANAGEMENT_PROFILE", "default-management-profile"),
50
+ "CENTRALISED_OPS_PROFILE": os.getenv("CENTRALISED_OPS_PROFILE", "default-ops-profile"),
51
+ "SINGLE_ACCOUNT_PROFILE": os.getenv("SINGLE_AWS_PROFILE", "default-single-profile"),
51
52
  }
52
53
 
53
54
 
@@ -141,28 +142,35 @@ class EnhancedInventoryCollector(CloudFoundationsBase):
141
142
 
142
143
  Strategic Alignment: "Do one thing and do it well"
143
144
  - Single profile override pattern: --profile takes precedence
144
- - Simple fallback to environment variables when no --profile specified
145
- - No hardcoded profile mappings - dynamic based on user input
145
+ - Universal AWS environment compatibility: works with ANY profile configuration
146
+ - Graceful fallback system for discovery across different AWS setups
146
147
 
147
148
  Returns:
148
149
  str: The active profile to use for all operations
149
150
  """
150
- # Primary profile determination: user --profile parameter takes absolute precedence
151
+ # PRIMARY: User --profile parameter takes absolute precedence (Universal Compatibility)
151
152
  if self.profile:
152
- print_info(f"Using user-specified profile: {self.profile}")
153
- logger.info("Profile override via --profile parameter - enterprise priority system")
153
+ print_info(f"✅ Universal AWS Compatibility: Using user-specified profile '{self.profile}'")
154
+ logger.info("Profile override via --profile parameter - universal environment support")
154
155
  return self.profile
155
156
 
156
- # Fallback to environment variables when no --profile specified
157
+ # SECONDARY: Environment variable fallback with intelligent prioritization
158
+ # Priority order: Management > Billing > Operations > Default (Organizations discovery preference)
157
159
  env_profile = (
158
160
  os.getenv("MANAGEMENT_PROFILE") or
159
161
  os.getenv("BILLING_PROFILE") or
160
162
  os.getenv("CENTRALISED_OPS_PROFILE") or
163
+ os.getenv("SINGLE_AWS_PROFILE") or
161
164
  "default"
162
165
  )
163
166
 
164
- print_info(f"No --profile specified, using environment fallback: {env_profile}")
165
- logger.info("Using environment variable fallback profile")
167
+ if env_profile != "default":
168
+ print_info(f"✅ Universal AWS Compatibility: Using environment profile '{env_profile}'")
169
+ logger.info(f"Environment variable profile selected: {env_profile}")
170
+ else:
171
+ print_info("✅ Universal AWS Compatibility: Using 'default' profile - works with any AWS CLI configuration")
172
+ logger.info("Using default profile - universal compatibility mode")
173
+
166
174
  return env_profile
167
175
 
168
176
  def _initialize_collectors(self) -> Dict[str, str]:
@@ -226,21 +234,19 @@ class EnhancedInventoryCollector(CloudFoundationsBase):
226
234
 
227
235
  def get_organization_accounts(self) -> List[str]:
228
236
  """
229
- Get list of accounts in AWS Organization.
237
+ Get list of accounts in AWS Organization with universal compatibility.
230
238
 
231
239
  Strategic Alignment: "Do one thing and do it well"
232
- - Single responsibility: discover organization structure
233
- - Uses active profile for Organizations API access
234
- - Enables multi-account inventory operations
240
+ - Universal AWS environment compatibility: works with ANY Organizations setup
241
+ - Intelligent fallback system: Organizations standalone account detection
242
+ - Graceful handling of different permission scenarios
235
243
  """
236
244
  try:
237
- # Use management profile for Organizations operations
238
- # Use single active profile for all operations following --profile pattern
239
- management_profile = self.active_profile
240
- management_session = create_management_session(profile=management_profile)
245
+ # Use active profile for Organizations operations (Universal Compatibility)
246
+ management_session = create_management_session(profile=self.active_profile)
241
247
  organizations_client = management_session.client("organizations")
242
248
 
243
- print_info("Discovering organization accounts with management profile...")
249
+ print_info(f"🔍 Universal Discovery: Attempting Organizations API with profile '{self.active_profile}'...")
244
250
  response = self._make_aws_call(organizations_client.list_accounts)
245
251
 
246
252
  accounts = []
@@ -248,14 +254,31 @@ class EnhancedInventoryCollector(CloudFoundationsBase):
248
254
  if account["Status"] == "ACTIVE":
249
255
  accounts.append(account["Id"])
250
256
 
251
- print_success(f"Found {len(accounts)} active accounts in organization")
252
- logger.info(f"Found {len(accounts)} active accounts using management profile: {management_profile}")
253
- return accounts
257
+ if accounts:
258
+ print_success(f"✅ Organizations Discovery: Found {len(accounts)} active accounts in organization")
259
+ logger.info(f"Organizations discovery successful: {len(accounts)} accounts with profile {self.active_profile}")
260
+ return accounts
261
+ else:
262
+ print_warning("⚠️ Organizations Discovery: No active accounts found in organization")
263
+ return [self.get_account_id()]
254
264
 
255
265
  except Exception as e:
256
- print_warning(f"Could not list organization accounts: {e}")
257
- logger.warning(f"Organization discovery failed, falling back to current account: {e}")
258
- # Fallback to current account
266
+ # Enhanced error messages for different AWS environment scenarios
267
+ error_message = str(e).lower()
268
+
269
+ if "accessdenied" in error_message or "unauthorized" in error_message:
270
+ print_warning(f"⚠️ Universal Compatibility: Profile '{self.active_profile}' lacks Organizations permissions")
271
+ print_info("💡 Single Account Mode: Continuing with current account (universal compatibility)")
272
+ elif "organizationsnotinuse" in error_message:
273
+ print_info(f"ℹ️ Standalone Account: Profile '{self.active_profile}' not in an AWS Organization")
274
+ print_info("💡 Single Account Mode: Continuing with current account")
275
+ else:
276
+ print_warning(f"⚠️ Organizations Discovery Failed: {e}")
277
+ print_info("💡 Fallback Mode: Continuing with current account for universal compatibility")
278
+
279
+ logger.warning(f"Organization discovery failed, graceful fallback: {e}")
280
+
281
+ # Universal fallback: always return current account for single-account operations
259
282
  return [self.get_account_id()]
260
283
 
261
284
  def get_current_account_id(self) -> str: