runbooks 1.0.0__py3-none-any.whl → 1.0.1__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 (77) 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/common/__init__.py +26 -9
  8. runbooks/common/aws_pricing.py +1070 -105
  9. runbooks/common/date_utils.py +115 -0
  10. runbooks/common/enhanced_exception_handler.py +10 -7
  11. runbooks/common/mcp_cost_explorer_integration.py +5 -4
  12. runbooks/common/profile_utils.py +76 -115
  13. runbooks/common/rich_utils.py +3 -3
  14. runbooks/finops/dashboard_runner.py +47 -28
  15. runbooks/finops/ebs_optimizer.py +56 -9
  16. runbooks/finops/enhanced_trend_visualization.py +7 -2
  17. runbooks/finops/finops_dashboard.py +6 -5
  18. runbooks/finops/iam_guidance.py +6 -1
  19. runbooks/finops/nat_gateway_optimizer.py +46 -27
  20. runbooks/finops/tests/test_integration.py +3 -1
  21. runbooks/finops/vpc_cleanup_optimizer.py +22 -29
  22. runbooks/inventory/core/collector.py +51 -28
  23. runbooks/inventory/discovery.md +197 -247
  24. runbooks/inventory/inventory_modules.py +2 -2
  25. runbooks/inventory/list_ec2_instances.py +3 -3
  26. runbooks/inventory/organizations_discovery.py +13 -8
  27. runbooks/inventory/unified_validation_engine.py +2 -15
  28. runbooks/main.py +74 -32
  29. runbooks/operate/base.py +9 -6
  30. runbooks/operate/deployment_framework.py +5 -4
  31. runbooks/operate/deployment_validator.py +6 -5
  32. runbooks/operate/mcp_integration.py +6 -5
  33. runbooks/operate/networking_cost_heatmap.py +17 -13
  34. runbooks/operate/vpc_operations.py +52 -12
  35. runbooks/remediation/base.py +3 -1
  36. runbooks/remediation/commons.py +5 -5
  37. runbooks/remediation/commvault_ec2_analysis.py +66 -18
  38. runbooks/remediation/config/accounts_example.json +31 -0
  39. runbooks/remediation/multi_account.py +120 -7
  40. runbooks/remediation/remediation_cli.py +710 -0
  41. runbooks/remediation/universal_account_discovery.py +377 -0
  42. runbooks/security/compliance_automation_engine.py +99 -20
  43. runbooks/security/config/__init__.py +24 -0
  44. runbooks/security/config/compliance_config.py +255 -0
  45. runbooks/security/config/compliance_weights_example.json +22 -0
  46. runbooks/security/config_template_generator.py +500 -0
  47. runbooks/security/security_cli.py +377 -0
  48. runbooks/validation/cli.py +8 -7
  49. runbooks/validation/comprehensive_2way_validator.py +26 -15
  50. runbooks/validation/mcp_validator.py +62 -8
  51. runbooks/vpc/config.py +32 -7
  52. runbooks/vpc/cross_account_session.py +5 -1
  53. runbooks/vpc/heatmap_engine.py +21 -14
  54. runbooks/vpc/mcp_no_eni_validator.py +115 -36
  55. runbooks/vpc/runbooks_adapter.py +33 -12
  56. runbooks/vpc/tests/conftest.py +4 -2
  57. runbooks/vpc/tests/test_cost_engine.py +3 -1
  58. {runbooks-1.0.0.dist-info → runbooks-1.0.1.dist-info}/METADATA +1 -1
  59. {runbooks-1.0.0.dist-info → runbooks-1.0.1.dist-info}/RECORD +63 -65
  60. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  61. runbooks/finops/runbooks.security.report_generator.log +0 -0
  62. runbooks/finops/runbooks.security.run_script.log +0 -0
  63. runbooks/finops/runbooks.security.security_export.log +0 -0
  64. runbooks/finops/tests/results_test_finops_dashboard.xml +0 -1
  65. runbooks/inventory/artifacts/scale-optimize-status.txt +0 -12
  66. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  67. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  68. runbooks/inventory/runbooks.security.run_script.log +0 -0
  69. runbooks/inventory/runbooks.security.security_export.log +0 -0
  70. runbooks/vpc/runbooks.inventory.organizations_discovery.log +0 -0
  71. runbooks/vpc/runbooks.security.report_generator.log +0 -0
  72. runbooks/vpc/runbooks.security.run_script.log +0 -0
  73. runbooks/vpc/runbooks.security.security_export.log +0 -0
  74. {runbooks-1.0.0.dist-info → runbooks-1.0.1.dist-info}/WHEEL +0 -0
  75. {runbooks-1.0.0.dist-info → runbooks-1.0.1.dist-info}/entry_points.txt +0 -0
  76. {runbooks-1.0.0.dist-info → runbooks-1.0.1.dist-info}/licenses/LICENSE +0 -0
  77. {runbooks-1.0.0.dist-info → runbooks-1.0.1.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@ Networking Cost Heat Map Engine - Advanced heat map generation with all required
3
3
  """
4
4
 
5
5
  import logging
6
+ import os
6
7
  from dataclasses import dataclass, field
7
8
  from datetime import datetime, timedelta
8
9
  from typing import Any, Dict, List, Optional, Tuple
@@ -181,7 +182,8 @@ class NetworkingCostHeatMapEngine:
181
182
  """Generate detailed single account heat map"""
182
183
  logger.info("Generating single account heat map")
183
184
 
184
- account_id = "499201730520" # Default single account
185
+ # Use environment-driven account ID for universal compatibility
186
+ account_id = os.getenv("AWS_ACCOUNT_ID", "123456789012")
185
187
 
186
188
  # Create cost distribution matrix
187
189
  heat_map_matrix = np.zeros((len(self.config.regions), len(NETWORKING_SERVICES)))
@@ -231,21 +233,23 @@ class NetworkingCostHeatMapEngine:
231
233
  """Generate multi-account aggregated heat map"""
232
234
  logger.info("Generating multi-account heat map (60 accounts)")
233
235
 
234
- num_accounts = 60
236
+ # Environment-driven account configuration for universal compatibility
237
+ num_accounts = int(os.getenv("AWS_TOTAL_ACCOUNTS", "60"))
235
238
 
236
- # Account categories
239
+ # Account categories with dynamic environment configuration
237
240
  account_categories = {
238
- "production": {"count": 15, "cost_multiplier": 5.0},
239
- "staging": {"count": 15, "cost_multiplier": 2.0},
240
- "development": {"count": 20, "cost_multiplier": 1.0},
241
- "sandbox": {"count": 10, "cost_multiplier": 0.3},
241
+ "production": {"count": int(os.getenv("AWS_PROD_ACCOUNTS", "15")), "cost_multiplier": float(os.getenv("PROD_COST_MULTIPLIER", "5.0"))},
242
+ "staging": {"count": int(os.getenv("AWS_STAGING_ACCOUNTS", "15")), "cost_multiplier": float(os.getenv("STAGING_COST_MULTIPLIER", "2.0"))},
243
+ "development": {"count": int(os.getenv("AWS_DEV_ACCOUNTS", "20")), "cost_multiplier": float(os.getenv("DEV_COST_MULTIPLIER", "1.0"))},
244
+ "sandbox": {"count": int(os.getenv("AWS_SANDBOX_ACCOUNTS", "10")), "cost_multiplier": float(os.getenv("SANDBOX_COST_MULTIPLIER", "0.3"))},
242
245
  }
243
246
 
244
247
  # Generate aggregated matrix
245
248
  aggregated_matrix = np.zeros((len(self.config.regions), len(NETWORKING_SERVICES)))
246
249
  account_breakdown = []
247
250
 
248
- account_id = 100000000000
251
+ # Dynamic base account ID from environment for universal compatibility
252
+ account_id = int(os.getenv("AWS_BASE_ACCOUNT_ID", "100000000000"))
249
253
 
250
254
  for category, details in account_categories.items():
251
255
  for i in range(details["count"]):
@@ -300,8 +304,8 @@ class NetworkingCostHeatMapEngine:
300
304
  time_series_data = {}
301
305
 
302
306
  for period_name, days in periods.items():
303
- # Dynamic base daily cost from environment variable - NO hardcoded defaults
304
- base_daily_cost = get_required_env_float('VPC_BASE_DAILY_COST')
307
+ # Dynamic base daily cost from environment variable with fallback
308
+ base_daily_cost = float(os.getenv('VPC_BASE_DAILY_COST', '10.0'))
305
309
 
306
310
  if period_name == "forecast_90_days":
307
311
  # Forecast with growth trend
@@ -528,11 +532,14 @@ class NetworkingCostHeatMapEngine:
528
532
  for service_idx, service_key in enumerate(NETWORKING_SERVICES.keys()):
529
533
  for region_idx in range(len(self.config.regions)):
530
534
  if service_key == "nat_gateway" and region_idx < pattern["nat_gateways"]:
531
- matrix[region_idx, service_idx] = 45.0 * multiplier
535
+ base_nat_cost = float(os.getenv("NAT_GATEWAY_MONTHLY_COST", "45.0"))
536
+ matrix[region_idx, service_idx] = base_nat_cost * multiplier
532
537
  elif service_key == "transit_gateway" and pattern["transit_gateway"] and region_idx == 0:
533
- matrix[region_idx, service_idx] = 36.5 * multiplier
538
+ base_tgw_cost = float(os.getenv("TRANSIT_GATEWAY_MONTHLY_COST", "36.5"))
539
+ matrix[region_idx, service_idx] = base_tgw_cost * multiplier
534
540
  elif service_key == "vpc_endpoint" and region_idx < pattern["vpc_endpoints"]:
535
- matrix[region_idx, service_idx] = 10.0 * multiplier
541
+ base_endpoint_cost = float(os.getenv("VPC_ENDPOINT_MONTHLY_COST", "10.0"))
542
+ matrix[region_idx, service_idx] = base_endpoint_cost * multiplier
536
543
 
537
544
  return matrix
538
545
 
@@ -657,7 +664,7 @@ class NetworkingCostHeatMapEngine:
657
664
  "total_monthly_spend": heat_maps["single_account_heat_map"]["total_monthly_cost"],
658
665
  "total_accounts": 1,
659
666
  "account_data": {
660
- "499201730520": {"monthly_cost": heat_maps["single_account_heat_map"]["total_monthly_cost"]}
667
+ os.getenv("AWS_ACCOUNT_ID", "123456789012"): {"monthly_cost": heat_maps["single_account_heat_map"]["total_monthly_cost"]}
661
668
  },
662
669
  }
663
670
  }
@@ -338,31 +338,39 @@ class NOENIVPCMCPValidator:
338
338
  - ≥99.5% accuracy scoring
339
339
  """
340
340
 
341
- def __init__(self, profiles: Dict[str, str], console: Console = None):
341
+ def __init__(self, user_profile: Optional[str] = None, console: Console = None):
342
342
  """
343
- Initialize NO-ENI VPC MCP validator.
343
+ Initialize NO-ENI VPC MCP validator with universal profile support.
344
344
 
345
345
  Args:
346
- profiles: Dictionary mapping profile types to profile names
347
- {'MANAGEMENT': 'profile1', 'BILLING': 'profile2', 'CENTRALISED_OPS': 'profile3'}
346
+ user_profile: User-specified profile (from --profile parameter)
348
347
  console: Rich console for output
349
348
  """
350
- self.profiles = profiles
349
+ # Import universal profile management
350
+ from ..common.profile_utils import (
351
+ get_profile_for_operation,
352
+ get_available_profiles_for_validation
353
+ )
354
+
355
+ self.user_profile = user_profile
351
356
  self.console = console or Console()
352
357
  self.validation_cache: Dict[str, Any] = {}
353
358
  self.cache_ttl = 300 # 5 minutes cache TTL
354
359
  self.accuracy_threshold = 99.5 # Enterprise accuracy target
355
360
 
356
- # Initialize MCP interfaces for each profile
361
+ # Universal profile detection - NO HARDCODED PROFILES
362
+ self.profiles = self._detect_universal_profiles()
363
+
364
+ # Initialize MCP interfaces for each detected profile
357
365
  self.mcp_interfaces = {}
358
- for profile_type, profile_name in profiles.items():
366
+ for profile_type, profile_name in self.profiles.items():
359
367
  try:
360
368
  self.mcp_interfaces[profile_type] = MCPServerInterface(profile_name, self.console)
361
369
  print_success(f"MCP interface initialized for {profile_type}: {profile_name}")
362
370
  except Exception as e:
363
371
  print_error(f"Failed to initialize MCP interface for {profile_type}: {e}")
364
372
 
365
- print_header("NO-ENI VPC MCP Validator", "Enterprise Cross-Validation Framework")
373
+ print_header("NO-ENI VPC MCP Validator", "Universal Profile Architecture")
366
374
  print_info(f"Initialized with {len(self.mcp_interfaces)} profile interfaces")
367
375
 
368
376
  # Initialize Organizations discovery engine for dynamic account discovery
@@ -378,7 +386,49 @@ class NOENIVPCMCPValidator:
378
386
  print_success("Organizations discovery engine initialized for dynamic account discovery")
379
387
  except Exception as e:
380
388
  print_warning(f"Organizations discovery initialization failed: {e}")
381
- print_info("Will use static profile-based discovery instead")
389
+ print_info("Will use profile-based discovery instead")
390
+
391
+ def _detect_universal_profiles(self) -> Dict[str, str]:
392
+ """
393
+ Detect available profiles using universal three-tier priority system.
394
+
395
+ Returns:
396
+ Dictionary mapping profile types to actual profile names
397
+ """
398
+ from ..common.profile_utils import get_profile_for_operation
399
+
400
+ detected_profiles = {}
401
+
402
+ # Universal profile detection - supports any AWS configuration
403
+ profile_types = ['management', 'billing', 'operational']
404
+
405
+ for profile_type in profile_types:
406
+ try:
407
+ profile_name = get_profile_for_operation(profile_type, self.user_profile)
408
+ # Convert to uppercase for compatibility with existing code
409
+ profile_key = profile_type.upper()
410
+ if profile_type == 'operational':
411
+ profile_key = 'CENTRALISED_OPS'
412
+
413
+ detected_profiles[profile_key] = profile_name
414
+ print_info(f"Detected {profile_key} profile: {profile_name}")
415
+
416
+ except Exception as e:
417
+ print_warning(f"Could not detect profile for {profile_type}: {e}")
418
+
419
+ # Ensure we have at least one profile for validation
420
+ if not detected_profiles:
421
+ import boto3
422
+ available_profiles = boto3.Session().available_profiles
423
+ if available_profiles:
424
+ fallback_profile = available_profiles[0]
425
+ detected_profiles['MANAGEMENT'] = fallback_profile
426
+ print_warning(f"Using fallback profile for validation: {fallback_profile}")
427
+ else:
428
+ detected_profiles['MANAGEMENT'] = 'default'
429
+ print_warning("Using 'default' profile as last resort")
430
+
431
+ return detected_profiles
382
432
 
383
433
  async def validate_no_eni_vpcs_comprehensive(self, region: str = 'ap-southeast-2') -> ValidationEvidence:
384
434
  """
@@ -551,7 +601,9 @@ class NOENIVPCMCPValidator:
551
601
  # Check for SSO token issues specifically
552
602
  if 'does not exist' in error_msg or 'KeyError' in error_msg or 'JSONDecodeError' in error_msg:
553
603
  print_warning("🔐 AWS SSO token issue detected")
554
- print_info("💡 Fix: Run 'aws sso login --profile ams-admin-ReadOnlyAccess-909135376185'")
604
+ import os
605
+ management_profile = os.getenv("MANAGEMENT_PROFILE", "your-management-profile")
606
+ print_info(f"💡 Fix: Run 'aws sso login --profile {management_profile}'")
555
607
 
556
608
  print_warning(f"Organizations discovery failed: {error_msg}")
557
609
  print_info("🔄 Falling back to single profile mode")
@@ -1226,12 +1278,28 @@ were used - all results reflect actual AWS infrastructure state.
1226
1278
  consistency_panel = self._create_consistency_panel(evidence.cross_profile_consistency)
1227
1279
  self.console.print(consistency_panel)
1228
1280
 
1229
- # Expected Results Validation
1230
- expected_results = {
1231
- 'MANAGEMENT_PROFILE': {'account': '909135376185', 'expected_no_eni': 1},
1232
- 'BILLING_PROFILE': {'account': '909135376185', 'expected_no_eni': 1},
1233
- 'CENTRALISED_OPS_PROFILE': {'account': '335083429030', 'expected_no_eni': 1}
1234
- }
1281
+ # Universal Account Validation - works with ANY AWS setup
1282
+ # Get actual account IDs from sessions instead of hardcoded values
1283
+ discovered_accounts = set()
1284
+ for candidate in evidence.vpc_candidates:
1285
+ discovered_accounts.add(candidate.account_id)
1286
+
1287
+ # Create dynamic expected results based on discovered accounts
1288
+ expected_results = {}
1289
+ for profile_type in self.profiles:
1290
+ # Get account ID for this profile type
1291
+ try:
1292
+ mcp_interface = self.mcp_interfaces.get(profile_type)
1293
+ if mcp_interface:
1294
+ sts_client = mcp_interface.session.client('sts')
1295
+ identity = sts_client.get_caller_identity()
1296
+ account_id = identity['Account']
1297
+ expected_results[profile_type] = {
1298
+ 'account': account_id,
1299
+ 'expected_no_eni': 'any' # Universal - accept any valid result
1300
+ }
1301
+ except Exception:
1302
+ pass # Skip profiles that can't be validated
1235
1303
 
1236
1304
  validation_status = self._validate_against_expected_results(evidence, expected_results)
1237
1305
 
@@ -1276,7 +1344,7 @@ were used - all results reflect actual AWS infrastructure state.
1276
1344
  )
1277
1345
 
1278
1346
  def _validate_against_expected_results(self, evidence: ValidationEvidence, expected: Dict[str, Any]) -> str:
1279
- """Validate results against expected enterprise profile outcomes."""
1347
+ """Validate results against dynamic profile outcomes (universal compatibility)."""
1280
1348
 
1281
1349
  validation_results = []
1282
1350
  overall_passed = True
@@ -1295,19 +1363,31 @@ were used - all results reflect actual AWS infrastructure state.
1295
1363
  account_candidates = [c for c in actual_candidates if c.account_id == expected_account]
1296
1364
  actual_count = len(account_candidates)
1297
1365
 
1298
- status = "✅ PASSED" if actual_count == expected_count else "❌ FAILED"
1299
- if actual_count != expected_count:
1300
- overall_passed = False
1366
+ # Universal validation - accept any valid result for 'any' expectation
1367
+ if expected_count == 'any':
1368
+ status = "✅ VALIDATED"
1369
+ validation_summary = f"Found {actual_count} NO-ENI VPCs"
1370
+ else:
1371
+ status = "✅ PASSED" if actual_count == expected_count else "❌ FAILED"
1372
+ if actual_count != expected_count and expected_count != 'any':
1373
+ overall_passed = False
1374
+ validation_summary = f"Expected: {expected_count}, Found: {actual_count}"
1301
1375
 
1302
1376
  validation_results.append(
1303
1377
  f"[bold {self._get_profile_color(profile_type)}]{profile_type}[/bold {self._get_profile_color(profile_type)}]: "
1304
- f"Account {expected_account} → Expected: {expected_count}, Found: {actual_count} {status}"
1378
+ f"Account {expected_account} → {validation_summary} {status}"
1305
1379
  )
1306
1380
 
1307
- # Overall validation status
1308
- overall_status = "✅ ALL VALIDATIONS PASSED" if overall_passed else "⚠️ VALIDATION ISSUES DETECTED"
1381
+ # Overall validation status - more forgiving for universal compatibility
1382
+ if not expected:
1383
+ overall_status = "✅ UNIVERSAL COMPATIBILITY - NO SPECIFIC EXPECTATIONS"
1384
+ elif overall_passed:
1385
+ overall_status = "✅ ALL VALIDATIONS PASSED"
1386
+ else:
1387
+ overall_status = "⚠️ SOME VALIDATIONS REQUIRE REVIEW"
1388
+
1309
1389
  validation_results.append("")
1310
- validation_results.append(f"[bold {'green' if overall_passed else 'red'}]Overall Status: {overall_status}[/bold {'green' if overall_passed else 'red'}]")
1390
+ validation_results.append(f"[bold green]Overall Status: {overall_status}[/bold green]")
1311
1391
 
1312
1392
  return "\n".join(validation_results)
1313
1393
 
@@ -1484,20 +1564,13 @@ were used - all results reflect actual AWS infrastructure state.
1484
1564
 
1485
1565
 
1486
1566
  # CLI Entry Point for Testing
1487
- async def main():
1567
+ async def main(user_profile: Optional[str] = None):
1488
1568
  """CLI entry point for NO-ENI VPC MCP validation with dynamic discovery."""
1489
1569
 
1490
- # Enterprise profile configuration
1491
- enterprise_profiles = {
1492
- 'MANAGEMENT': 'ams-admin-ReadOnlyAccess-909135376185',
1493
- 'BILLING': 'ams-admin-Billing-ReadOnlyAccess-909135376185',
1494
- 'CENTRALISED_OPS': 'ams-centralised-ops-ReadOnlyAccess-335083429030'
1495
- }
1570
+ print_header("🎯 NO-ENI VPC Dynamic Discovery", "Universal Profile Architecture")
1496
1571
 
1497
- print_header("🎯 NO-ENI VPC Dynamic Discovery", "Real-Time MCP Validation")
1498
-
1499
- # Initialize validator
1500
- validator = NOENIVPCMCPValidator(enterprise_profiles)
1572
+ # Initialize validator with universal profile detection
1573
+ validator = NOENIVPCMCPValidator(user_profile)
1501
1574
 
1502
1575
  # Run dynamic discovery across all accounts
1503
1576
  print_info("🌐 Starting dynamic NO-ENI VPC discovery across all AWS accounts...")
@@ -1548,4 +1621,10 @@ async def main():
1548
1621
 
1549
1622
 
1550
1623
  if __name__ == "__main__":
1551
- asyncio.run(main())
1624
+ import argparse
1625
+
1626
+ parser = argparse.ArgumentParser(description="NO-ENI VPC MCP Validation with Universal Profile Support")
1627
+ parser.add_argument('--profile', help='AWS profile to use (overrides environment variables)')
1628
+ args = parser.parse_args()
1629
+
1630
+ asyncio.run(main(args.profile))
@@ -18,6 +18,7 @@ import boto3
18
18
  from botocore.exceptions import ClientError
19
19
 
20
20
  from runbooks.common.rich_utils import console, print_success, print_warning, print_error
21
+ from runbooks.common.profile_utils import create_operational_session, validate_profile_access
21
22
  from .vpc_cleanup_integration import VPCCleanupFramework
22
23
  from .cleanup_wrapper import VPCCleanupCLI
23
24
  from .networking_wrapper import VPCNetworkingWrapper
@@ -33,18 +34,29 @@ class RunbooksAdapter:
33
34
  Provides backward compatibility while leveraging existing VPC infrastructure.
34
35
  """
35
36
 
36
- def __init__(self, profile: str, region: str = "us-east-1"):
37
+ def __init__(self, profile: Optional[str] = None, region: str = "us-east-1"):
37
38
  """
38
- Initialize RunbooksAdapter with enterprise VPC framework integration.
39
+ Initialize RunbooksAdapter with universal AWS profile support.
39
40
 
40
41
  Args:
41
- profile: AWS profile for operations
42
+ profile: AWS profile for operations (uses universal profile selection if None)
42
43
  region: AWS region
43
44
  """
44
- self.profile = profile or None
45
+ self.user_profile = profile
45
46
  self.region = region
46
47
  self.have_runbooks = self._detect_runbooks_availability()
47
48
 
49
+ # Universal profile selection - works with ANY AWS setup
50
+ if profile:
51
+ # Validate user-specified profile
52
+ if not validate_profile_access(profile, "VPC operations"):
53
+ print_warning(f"Profile '{profile}' validation failed, using universal fallback")
54
+ self.profile = None
55
+ else:
56
+ self.profile = profile
57
+ else:
58
+ self.profile = None
59
+
48
60
  # Initialize enterprise VPC components
49
61
  self.vpc_wrapper = None
50
62
  self.cleanup_framework = None
@@ -64,16 +76,25 @@ class RunbooksAdapter:
64
76
  return False
65
77
 
66
78
  def _initialize_components(self):
67
- """Initialize runbooks components and boto3 session."""
68
- # Initialize boto3 session for fallback
69
- if boto3:
70
- session_args = {}
79
+ """Initialize runbooks components and boto3 session with universal profile support."""
80
+ # Initialize boto3 session using universal profile management
81
+ try:
71
82
  if self.profile:
72
- session_args['profile_name'] = self.profile
83
+ # Use operational session for VPC operations
84
+ self.session = create_operational_session(profile=self.profile)
85
+ print_success(f"Universal profile session created: {self.profile}")
86
+ else:
87
+ # Fallback to universal profile selection
88
+ self.session = create_operational_session(profile=None)
89
+ print_success("Universal fallback session created")
90
+ except Exception as e:
91
+ print_warning(f"Universal session creation failed: {e}")
92
+ # Final fallback to basic boto3 session
73
93
  try:
74
- self.session = boto3.session.Session(region_name=self.region, **session_args)
75
- except Exception as e:
76
- print_warning(f"Boto3 session creation failed: {e}")
94
+ self.session = boto3.Session()
95
+ print_warning("Using basic boto3 session as final fallback")
96
+ except Exception as e2:
97
+ print_error(f"All session creation methods failed: {e2}")
77
98
  self.session = None
78
99
 
79
100
  if not self.have_runbooks:
@@ -31,6 +31,8 @@ from runbooks.vpc.networking_wrapper import VPCNetworkingWrapper
31
31
 
32
32
  @pytest.fixture(scope="session")
33
33
  def aws_credentials():
34
+ # Dynamic test period for consistent test data
35
+ test_period = get_test_date_period(30)
34
36
  """Mock AWS credentials for VPC testing."""
35
37
  os.environ["AWS_ACCESS_KEY_ID"] = "testing"
36
38
  os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"
@@ -276,7 +278,7 @@ def mock_cost_explorer_responses():
276
278
  "vpc_costs": {
277
279
  "ResultsByTime": [
278
280
  {
279
- "TimePeriod": {"Start": "2024-01-01", "End": "2024-01-31"},
281
+ "TimePeriod": {"Start": test_period["Start"], "End": test_period["End"]},
280
282
  "Total": {"BlendedCost": {"Amount": "145.67", "Unit": "USD"}},
281
283
  }
282
284
  ]
@@ -284,7 +286,7 @@ def mock_cost_explorer_responses():
284
286
  "nat_gateway_costs": {
285
287
  "ResultsByTime": [
286
288
  {
287
- "TimePeriod": {"Start": "2024-01-01", "End": "2024-01-31"},
289
+ "TimePeriod": {"Start": test_period["Start"], "End": test_period["End"]},
288
290
  "Total": {"BlendedCost": {"Amount": "89.32", "Unit": "USD"}},
289
291
  }
290
292
  ]
@@ -20,6 +20,8 @@ class TestNetworkingCostEngine:
20
20
  """Test Networking Cost Engine functionality."""
21
21
 
22
22
  def test_initialization_default(self):
23
+ # Dynamic test period for consistent test data
24
+ test_period = get_test_date_period(30)
23
25
  """Test cost engine initialization with defaults."""
24
26
  engine = NetworkingCostEngine()
25
27
 
@@ -455,7 +457,7 @@ class TestNetworkingCostEngine:
455
457
  mock_cost_explorer.get_cost_and_usage.return_value = {
456
458
  "ResultsByTime": [
457
459
  {
458
- "TimePeriod": {"Start": "2024-01-01", "End": "2024-01-31"},
460
+ "TimePeriod": {"Start": test_period["Start"], "End": test_period["End"]},
459
461
  "Total": {"BlendedCost": {"Amount": "123.45", "Unit": "USD"}},
460
462
  }
461
463
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runbooks
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: CloudOps Automation Toolkit with Enhanced Cloud Foundations Assessment for DevOps and SRE teams.
5
5
  Author-email: Maintainers <nnthanh101@gmail.com>
6
6
  License-Expression: Apache-2.0