runbooks 0.9.6__py3-none-any.whl → 0.9.8__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 (57) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/_platform/__init__.py +19 -0
  3. runbooks/_platform/core/runbooks_wrapper.py +478 -0
  4. runbooks/cloudops/cost_optimizer.py +330 -0
  5. runbooks/cloudops/interfaces.py +3 -3
  6. runbooks/common/mcp_integration.py +174 -0
  7. runbooks/common/performance_monitor.py +4 -4
  8. runbooks/enterprise/__init__.py +18 -10
  9. runbooks/enterprise/security.py +708 -0
  10. runbooks/finops/README.md +1 -1
  11. runbooks/finops/automation_core.py +643 -0
  12. runbooks/finops/business_cases.py +414 -16
  13. runbooks/finops/cli.py +23 -0
  14. runbooks/finops/compute_cost_optimizer.py +865 -0
  15. runbooks/finops/ebs_cost_optimizer.py +718 -0
  16. runbooks/finops/ebs_optimizer.py +909 -0
  17. runbooks/finops/elastic_ip_optimizer.py +675 -0
  18. runbooks/finops/embedded_mcp_validator.py +330 -14
  19. runbooks/finops/enhanced_dashboard_runner.py +2 -1
  20. runbooks/finops/enterprise_wrappers.py +827 -0
  21. runbooks/finops/finops_dashboard.py +322 -11
  22. runbooks/finops/legacy_migration.py +730 -0
  23. runbooks/finops/nat_gateway_optimizer.py +1160 -0
  24. runbooks/finops/network_cost_optimizer.py +1387 -0
  25. runbooks/finops/notebook_utils.py +596 -0
  26. runbooks/finops/reservation_optimizer.py +956 -0
  27. runbooks/finops/single_dashboard.py +16 -16
  28. runbooks/finops/validation_framework.py +753 -0
  29. runbooks/finops/vpc_cleanup_optimizer.py +817 -0
  30. runbooks/finops/workspaces_analyzer.py +1 -1
  31. runbooks/inventory/__init__.py +7 -0
  32. runbooks/inventory/collectors/aws_networking.py +357 -6
  33. runbooks/inventory/mcp_vpc_validator.py +1091 -0
  34. runbooks/inventory/vpc_analyzer.py +1107 -0
  35. runbooks/inventory/vpc_architecture_validator.py +939 -0
  36. runbooks/inventory/vpc_dependency_analyzer.py +845 -0
  37. runbooks/main.py +487 -40
  38. runbooks/operate/vpc_operations.py +1485 -16
  39. runbooks/remediation/commvault_ec2_analysis.py +1 -1
  40. runbooks/remediation/dynamodb_optimize.py +2 -2
  41. runbooks/remediation/rds_instance_list.py +1 -1
  42. runbooks/remediation/rds_snapshot_list.py +1 -1
  43. runbooks/remediation/workspaces_list.py +2 -2
  44. runbooks/security/compliance_automation.py +2 -2
  45. runbooks/vpc/__init__.py +12 -0
  46. runbooks/vpc/cleanup_wrapper.py +757 -0
  47. runbooks/vpc/cost_engine.py +527 -3
  48. runbooks/vpc/networking_wrapper.py +29 -29
  49. runbooks/vpc/runbooks_adapter.py +479 -0
  50. runbooks/vpc/tests/test_config.py +2 -2
  51. runbooks/vpc/vpc_cleanup_integration.py +2629 -0
  52. {runbooks-0.9.6.dist-info → runbooks-0.9.8.dist-info}/METADATA +1 -1
  53. {runbooks-0.9.6.dist-info → runbooks-0.9.8.dist-info}/RECORD +57 -34
  54. {runbooks-0.9.6.dist-info → runbooks-0.9.8.dist-info}/WHEEL +0 -0
  55. {runbooks-0.9.6.dist-info → runbooks-0.9.8.dist-info}/entry_points.txt +0 -0
  56. {runbooks-0.9.6.dist-info → runbooks-0.9.8.dist-info}/licenses/LICENSE +0 -0
  57. {runbooks-0.9.6.dist-info → runbooks-0.9.8.dist-info}/top_level.txt +0 -0
runbooks/main.py CHANGED
@@ -5348,9 +5348,9 @@ def emergency_response(ctx, spike_threshold, target_savings, analysis_days, max_
5348
5348
 
5349
5349
  # Export reports if requested
5350
5350
  if export_reports:
5351
- exported = result.export_reports('/tmp/emergency-cost-reports')
5351
+ exported = result.export_reports('./tmp/emergency-cost-reports')
5352
5352
  if exported.get('json'):
5353
- print_success(f"📊 Executive reports exported to: /tmp/emergency-cost-reports")
5353
+ print_success(f"📊 Executive reports exported to: ./tmp/emergency-cost-reports")
5354
5354
 
5355
5355
  # Exit with success/failure status
5356
5356
  if result.success:
@@ -5549,7 +5549,7 @@ def cost_optimization():
5549
5549
  @click.option("--analyze", is_flag=True, help="Perform detailed cost analysis")
5550
5550
  @click.option("--calculate-savings", is_flag=True, help="Calculate cost savings for cleanup")
5551
5551
  @click.option("--unused-days", default=180, help="Days threshold for considering WorkSpace unused (JIRA FinOps-24)")
5552
- @click.option("--output-file", default="/tmp/workspaces_cost_analysis.csv", help="Output CSV file path")
5552
+ @click.option("--output-file", default="./tmp/workspaces_cost_analysis.csv", help="Output CSV file path")
5553
5553
  def workspaces(profile, region, dry_run, analyze, calculate_savings, unused_days, output_file):
5554
5554
  """
5555
5555
  FinOps-24: WorkSpaces cleanup analysis for $12,518 annual savings.
@@ -5598,7 +5598,7 @@ def workspaces(profile, region, dry_run, analyze, calculate_savings, unused_days
5598
5598
  @click.option("--older-than", default=90, help="Focus on snapshots older than X days (JIRA FinOps-23)")
5599
5599
  @click.option("--calculate-savings", is_flag=True, help="Calculate detailed cost savings analysis")
5600
5600
  @click.option("--analyze", is_flag=True, help="Perform comprehensive cost analysis")
5601
- @click.option("--output-file", default="/tmp/rds_snapshots_cost_analysis.csv", help="Output CSV file path")
5601
+ @click.option("--output-file", default="./tmp/rds_snapshots_cost_analysis.csv", help="Output CSV file path")
5602
5602
  def rds_snapshots(profile, region, dry_run, manual_only, older_than, calculate_savings, analyze, output_file):
5603
5603
  """
5604
5604
  FinOps-23: RDS manual snapshots analysis for $5K-24K annual savings.
@@ -5645,7 +5645,7 @@ def rds_snapshots(profile, region, dry_run, manual_only, older_than, calculate_s
5645
5645
  @common_aws_options
5646
5646
  @click.option("--account", default="637423383469", help="Commvault backup account ID (JIRA FinOps-25)")
5647
5647
  @click.option("--investigate-utilization", is_flag=True, help="Investigate EC2 utilization patterns")
5648
- @click.option("--output-file", default="/tmp/commvault_ec2_analysis.csv", help="Output CSV file path")
5648
+ @click.option("--output-file", default="./tmp/commvault_ec2_analysis.csv", help="Output CSV file path")
5649
5649
  def commvault_ec2(profile, region, dry_run, account, investigate_utilization, output_file):
5650
5650
  """
5651
5651
  FinOps-25: Commvault EC2 investigation for cost optimization.
@@ -5688,7 +5688,7 @@ def commvault_ec2(profile, region, dry_run, account, investigate_utilization, ou
5688
5688
  @cost_optimization.command()
5689
5689
  @common_aws_options
5690
5690
  @click.option("--all-scenarios", is_flag=True, help="Run all JIRA cost optimization scenarios")
5691
- @click.option("--output-dir", default="/tmp/cost_optimization_reports", help="Directory for all reports")
5691
+ @click.option("--output-dir", default="./tmp/cost_optimization_reports", help="Directory for all reports")
5692
5692
  def comprehensive_analysis(profile, region, dry_run, all_scenarios, output_dir):
5693
5693
  """
5694
5694
  Comprehensive analysis of all JIRA cost optimization scenarios.
@@ -5850,8 +5850,8 @@ def _parse_profiles_parameter(profiles_tuple):
5850
5850
  )
5851
5851
  @click.option(
5852
5852
  "--scenario",
5853
- type=click.Choice(["workspaces", "snapshots", "commvault"], case_sensitive=False),
5854
- help="Business scenario analysis: workspaces (FinOps-24: $13,020 savings), snapshots (FinOps-23: $119,700 savings), commvault (FinOps-25: investigation)"
5853
+ type=click.Choice(["workspaces", "snapshots", "commvault", "nat-gateway", "elastic-ip", "ebs", "vpc-cleanup"], case_sensitive=False),
5854
+ help="Business scenario analysis: workspaces (FinOps-24: $13,020 savings), snapshots (FinOps-23: $119,700 savings), commvault (FinOps-25: investigation), nat-gateway (FinOps-26: $8K-$12K potential), elastic-ip (FinOps-EIP: $3.65/month direct savings), ebs (FinOps-EBS: 15-20% storage optimization), vpc-cleanup (AWSO-05: $5,869.20 VPC cleanup savings)"
5855
5855
  )
5856
5856
  @click.pass_context
5857
5857
  def finops(
@@ -5891,10 +5891,14 @@ def finops(
5891
5891
  Comprehensive cost analysis supporting both UnblendedCost (technical)
5892
5892
  and AmortizedCost (financial) perspectives for enterprise reporting.
5893
5893
 
5894
- BUSINESS SCENARIOS ($132,720+ proven savings):
5894
+ BUSINESS SCENARIOS ($138,589+ proven savings):
5895
5895
  runbooks finops --scenario workspaces # FinOps-24: WorkSpaces cleanup ($13,020 annual)
5896
5896
  runbooks finops --scenario snapshots # FinOps-23: RDS snapshots ($119,700 annual)
5897
5897
  runbooks finops --scenario commvault # FinOps-25: EC2 investigation framework
5898
+ runbooks finops --scenario nat-gateway # FinOps-26: NAT Gateway optimization ($8K-$12K potential)
5899
+ runbooks finops --scenario elastic-ip # FinOps-EIP: Elastic IP cleanup ($3.65/month per EIP)
5900
+ runbooks finops --scenario ebs # FinOps-EBS: Storage optimization (15-20% cost reduction)
5901
+ runbooks finops --scenario vpc-cleanup # AWSO-05: VPC cleanup ($5,869.20 annual savings)
5898
5902
 
5899
5903
  GENERAL ANALYTICS:
5900
5904
  runbooks finops --audit --csv --report-name audit_report
@@ -5914,20 +5918,259 @@ def finops(
5914
5918
  print_info(f"Executing scenario: {scenario.upper()}")
5915
5919
 
5916
5920
  try:
5921
+ # Import CloudOps cost optimizer for enhanced JIRA scenario integration
5922
+ from runbooks.cloudops.cost_optimizer import CostOptimizer
5923
+ from runbooks.cloudops.models import ExecutionMode
5924
+ import asyncio
5925
+
5926
+ # Initialize CloudOps cost optimizer with enterprise patterns
5927
+ execution_mode = ExecutionMode.DRY_RUN if dry_run else ExecutionMode.EXECUTE
5928
+ # Ensure profile is a string, not a tuple
5929
+ profile_str = profile[0] if isinstance(profile, (tuple, list)) and profile else profile or "default"
5930
+ cost_optimizer = CostOptimizer(
5931
+ profile=profile_str,
5932
+ dry_run=dry_run,
5933
+ execution_mode=execution_mode
5934
+ )
5935
+
5917
5936
  if scenario.lower() == "workspaces":
5918
5937
  print_info("FinOps-24: WorkSpaces cleanup analysis ($13,020 annual savings - 104% target achievement)")
5919
- from runbooks.finops.finops_scenarios import analyze_finops_24_workspaces
5920
- results = analyze_finops_24_workspaces(profile)
5938
+ print_info("🚀 Enhanced with CloudOps enterprise integration")
5939
+
5940
+ # Use CloudOps cost optimizer for enterprise-grade analysis
5941
+ workspaces_result = asyncio.run(cost_optimizer.optimize_workspaces(
5942
+ usage_threshold_days=180, # 6 months as per JIRA requirement
5943
+ dry_run=dry_run
5944
+ ))
5945
+
5946
+ # Convert to legacy format for backward compatibility
5947
+ results = {
5948
+ "scenario": "FinOps-24",
5949
+ "business_case": "WorkSpaces Cleanup",
5950
+ "annual_savings": workspaces_result.annual_savings,
5951
+ "monthly_savings": workspaces_result.total_monthly_savings,
5952
+ "affected_resources": workspaces_result.affected_resources,
5953
+ "success": workspaces_result.success,
5954
+ "execution_mode": workspaces_result.execution_mode.value,
5955
+ "risk_level": workspaces_result.resource_impacts[0].risk_level.value if workspaces_result.resource_impacts else "LOW"
5956
+ }
5921
5957
 
5922
5958
  elif scenario.lower() == "snapshots":
5923
5959
  print_info("FinOps-23: RDS snapshots optimization ($119,700 annual savings - 498% target achievement)")
5924
- from runbooks.finops.finops_scenarios import analyze_finops_23_rds_snapshots
5925
- results = analyze_finops_23_rds_snapshots(profile)
5960
+ print_info("🚀 Enhanced with CloudOps enterprise integration")
5961
+
5962
+ # Use CloudOps cost optimizer for enterprise-grade analysis
5963
+ snapshots_result = asyncio.run(cost_optimizer.optimize_rds_snapshots(
5964
+ snapshot_age_threshold_days=90, # 3 months threshold
5965
+ dry_run=dry_run
5966
+ ))
5967
+
5968
+ # Convert to legacy format for backward compatibility
5969
+ results = {
5970
+ "scenario": "FinOps-23",
5971
+ "business_case": "RDS Snapshots Cleanup",
5972
+ "annual_savings": snapshots_result.annual_savings,
5973
+ "monthly_savings": snapshots_result.total_monthly_savings,
5974
+ "affected_resources": snapshots_result.affected_resources,
5975
+ "success": snapshots_result.success,
5976
+ "execution_mode": snapshots_result.execution_mode.value,
5977
+ "risk_level": snapshots_result.resource_impacts[0].risk_level.value if snapshots_result.resource_impacts else "MEDIUM"
5978
+ }
5926
5979
 
5927
5980
  elif scenario.lower() == "commvault":
5928
5981
  print_info("FinOps-25: Commvault EC2 investigation framework (Real AWS integration)")
5929
- from runbooks.finops.finops_scenarios import investigate_finops_25_commvault
5930
- results = investigate_finops_25_commvault(profile)
5982
+ print_info("🚀 Enhanced with CloudOps enterprise integration")
5983
+
5984
+ # Use CloudOps cost optimizer for enterprise-grade investigation
5985
+ commvault_result = asyncio.run(cost_optimizer.investigate_commvault_ec2(
5986
+ account_id="637423383469", # From JIRA specification
5987
+ dry_run=True # Always dry-run for investigations
5988
+ ))
5989
+
5990
+ # Convert to legacy format for backward compatibility
5991
+ results = {
5992
+ "scenario": "FinOps-25",
5993
+ "business_case": "Commvault EC2 Investigation",
5994
+ "annual_savings": commvault_result.annual_savings,
5995
+ "monthly_savings": commvault_result.total_monthly_savings,
5996
+ "affected_resources": commvault_result.affected_resources,
5997
+ "success": commvault_result.success,
5998
+ "execution_mode": commvault_result.execution_mode.value,
5999
+ "risk_level": commvault_result.resource_impacts[0].risk_level.value if commvault_result.resource_impacts else "HIGH",
6000
+ "investigation_status": "Framework Established"
6001
+ }
6002
+
6003
+ elif scenario.lower() == "nat-gateway":
6004
+ print_info("FinOps-26: NAT Gateway cost optimization ($8K-$12K potential annual savings)")
6005
+ print_info("🚀 Enterprise multi-region analysis with network dependency validation")
6006
+
6007
+ # Use dedicated NAT Gateway optimizer for specialized analysis
6008
+ from runbooks.finops.nat_gateway_optimizer import NATGatewayOptimizer
6009
+
6010
+ profile_str = profile[0] if isinstance(profile, (tuple, list)) and profile else profile or "default"
6011
+ nat_optimizer = NATGatewayOptimizer(
6012
+ profile_name=profile_str,
6013
+ regions=regions or ["us-east-1", "us-west-2", "eu-west-1"]
6014
+ )
6015
+
6016
+ nat_result = asyncio.run(nat_optimizer.analyze_nat_gateways(dry_run=dry_run))
6017
+
6018
+ # Convert to legacy format for backward compatibility
6019
+ results = {
6020
+ "scenario": "FinOps-26",
6021
+ "business_case": "NAT Gateway Cost Optimization",
6022
+ "annual_savings": nat_result.potential_annual_savings,
6023
+ "monthly_savings": nat_result.potential_monthly_savings,
6024
+ "total_nat_gateways": nat_result.total_nat_gateways,
6025
+ "analyzed_regions": nat_result.analyzed_regions,
6026
+ "current_annual_cost": nat_result.total_annual_cost,
6027
+ "execution_time": nat_result.execution_time_seconds,
6028
+ "mcp_validation_accuracy": nat_result.mcp_validation_accuracy,
6029
+ "success": True,
6030
+ "risk_level": "LOW", # READ-ONLY analysis
6031
+ "optimization_summary": {
6032
+ "decommission_candidates": len([r for r in nat_result.optimization_results if r.optimization_recommendation == "decommission"]),
6033
+ "investigation_candidates": len([r for r in nat_result.optimization_results if r.optimization_recommendation == "investigate"]),
6034
+ "retain_recommendations": len([r for r in nat_result.optimization_results if r.optimization_recommendation == "retain"])
6035
+ }
6036
+ }
6037
+
6038
+ elif scenario.lower() == "elastic-ip":
6039
+ print_info("FinOps-EIP: Elastic IP cost optimization ($3.65/month per unattached EIP)")
6040
+ print_info("🚀 Enterprise multi-region analysis with DNS dependency validation")
6041
+
6042
+ # Use dedicated Elastic IP optimizer for specialized analysis
6043
+ from runbooks.finops.elastic_ip_optimizer import ElasticIPOptimizer
6044
+
6045
+ profile_str = profile[0] if isinstance(profile, (tuple, list)) and profile else profile or "default"
6046
+ eip_optimizer = ElasticIPOptimizer(
6047
+ profile_name=profile_str,
6048
+ regions=regions or ["us-east-1", "us-west-2", "eu-west-1", "us-east-2"]
6049
+ )
6050
+
6051
+ eip_result = asyncio.run(eip_optimizer.analyze_elastic_ips(dry_run=dry_run))
6052
+
6053
+ # Convert to legacy format for backward compatibility
6054
+ results = {
6055
+ "scenario": "FinOps-EIP",
6056
+ "business_case": "Elastic IP Cost Optimization",
6057
+ "annual_savings": eip_result.potential_annual_savings,
6058
+ "monthly_savings": eip_result.potential_monthly_savings,
6059
+ "total_elastic_ips": eip_result.total_elastic_ips,
6060
+ "attached_elastic_ips": eip_result.attached_elastic_ips,
6061
+ "unattached_elastic_ips": eip_result.unattached_elastic_ips,
6062
+ "analyzed_regions": eip_result.analyzed_regions,
6063
+ "current_annual_cost": eip_result.total_annual_cost,
6064
+ "execution_time": eip_result.execution_time_seconds,
6065
+ "mcp_validation_accuracy": eip_result.mcp_validation_accuracy,
6066
+ "success": True,
6067
+ "risk_level": "LOW", # READ-ONLY analysis
6068
+ "optimization_summary": {
6069
+ "release_candidates": len([r for r in eip_result.optimization_results if r.optimization_recommendation == "release"]),
6070
+ "investigation_candidates": len([r for r in eip_result.optimization_results if r.optimization_recommendation == "investigate"]),
6071
+ "retain_recommendations": len([r for r in eip_result.optimization_results if r.optimization_recommendation == "retain"])
6072
+ }
6073
+ }
6074
+
6075
+ elif scenario.lower() == "ebs":
6076
+ print_info("FinOps-EBS: EBS Volume storage optimization (15-20% cost reduction potential)")
6077
+ print_info("🚀 Enterprise comprehensive analysis: GP2→GP3 + Usage + Orphaned cleanup")
6078
+
6079
+ # Use dedicated EBS optimizer for specialized analysis
6080
+ from runbooks.finops.ebs_optimizer import EBSOptimizer
6081
+
6082
+ profile_str = profile[0] if isinstance(profile, (tuple, list)) and profile else profile or "default"
6083
+ ebs_optimizer = EBSOptimizer(
6084
+ profile_name=profile_str,
6085
+ regions=regions or ["us-east-1", "us-west-2", "eu-west-1"]
6086
+ )
6087
+
6088
+ ebs_result = asyncio.run(ebs_optimizer.analyze_ebs_volumes(dry_run=dry_run))
6089
+
6090
+ # Convert to legacy format for backward compatibility
6091
+ results = {
6092
+ "scenario": "FinOps-EBS",
6093
+ "business_case": "EBS Volume Storage Optimization",
6094
+ "annual_savings": ebs_result.total_potential_annual_savings,
6095
+ "monthly_savings": ebs_result.total_potential_monthly_savings,
6096
+ "total_volumes": ebs_result.total_volumes,
6097
+ "gp2_volumes": ebs_result.gp2_volumes,
6098
+ "gp3_eligible_volumes": ebs_result.gp3_eligible_volumes,
6099
+ "low_usage_volumes": ebs_result.low_usage_volumes,
6100
+ "orphaned_volumes": ebs_result.orphaned_volumes,
6101
+ "analyzed_regions": ebs_result.analyzed_regions,
6102
+ "current_annual_cost": ebs_result.total_annual_cost,
6103
+ "execution_time": ebs_result.execution_time_seconds,
6104
+ "mcp_validation_accuracy": ebs_result.mcp_validation_accuracy,
6105
+ "success": True,
6106
+ "risk_level": "LOW", # READ-ONLY analysis
6107
+ "optimization_breakdown": {
6108
+ "gp3_conversion_savings": ebs_result.gp3_potential_annual_savings,
6109
+ "low_usage_savings": ebs_result.low_usage_potential_annual_savings,
6110
+ "orphaned_cleanup_savings": ebs_result.orphaned_potential_annual_savings
6111
+ },
6112
+ "optimization_summary": {
6113
+ "gp3_convert_candidates": len([r for r in ebs_result.optimization_results if r.optimization_recommendation == "gp3_convert"]),
6114
+ "usage_investigation_candidates": len([r for r in ebs_result.optimization_results if r.optimization_recommendation == "investigate_usage"]),
6115
+ "orphaned_cleanup_candidates": len([r for r in ebs_result.optimization_results if r.optimization_recommendation == "cleanup_orphaned"]),
6116
+ "retain_recommendations": len([r for r in ebs_result.optimization_results if r.optimization_recommendation == "retain"])
6117
+ }
6118
+ }
6119
+
6120
+ elif scenario.lower() == "vpc-cleanup":
6121
+ print_info("AWSO-05: VPC Cleanup cost optimization ($5,869.20 annual savings)")
6122
+ print_info("🚀 Enterprise three-bucket strategy with dependency validation")
6123
+
6124
+ # Use dedicated VPC Cleanup optimizer for AWSO-05 analysis
6125
+ from runbooks.finops.vpc_cleanup_optimizer import VPCCleanupOptimizer
6126
+
6127
+ profile_str = profile[0] if isinstance(profile, (tuple, list)) and profile else profile or "default"
6128
+ vpc_optimizer = VPCCleanupOptimizer(
6129
+ profile=profile_str
6130
+ )
6131
+
6132
+ vpc_result = vpc_optimizer.analyze_vpc_cleanup_opportunities()
6133
+
6134
+ # Convert to legacy format for backward compatibility
6135
+ results = {
6136
+ "scenario": "AWSO-05",
6137
+ "business_case": "VPC Cleanup Cost Optimization",
6138
+ "annual_savings": vpc_result.total_annual_savings,
6139
+ "monthly_savings": vpc_result.total_annual_savings / 12,
6140
+ "total_vpcs_analyzed": vpc_result.total_vpcs_analyzed,
6141
+ "bucket_1_internal": len(vpc_result.bucket_1_internal),
6142
+ "bucket_2_external": len(vpc_result.bucket_2_external),
6143
+ "bucket_3_control": len(vpc_result.bucket_3_control),
6144
+ "cleanup_ready_vpcs": len([v for v in vpc_result.cleanup_candidates if v.cleanup_recommendation == "ready"]),
6145
+ "investigation_vpcs": len([v for v in vpc_result.cleanup_candidates if v.cleanup_recommendation == "investigate"]),
6146
+ "manual_review_vpcs": len([v for v in vpc_result.cleanup_candidates if v.cleanup_recommendation == "manual_review"]),
6147
+ "mcp_validation_accuracy": vpc_result.mcp_validation_accuracy,
6148
+ "evidence_hash": vpc_result.evidence_hash,
6149
+ "analysis_timestamp": vpc_result.analysis_timestamp.isoformat(),
6150
+ "success": True,
6151
+ "risk_level": "GRADUATED", # Three-bucket graduated risk approach
6152
+ "safety_assessment": vpc_result.safety_assessment,
6153
+ "three_bucket_breakdown": {
6154
+ "internal_data_plane": {
6155
+ "count": len(vpc_result.bucket_1_internal),
6156
+ "annual_savings": sum(v.annual_savings for v in vpc_result.bucket_1_internal),
6157
+ "risk_level": "LOW",
6158
+ "status": "Ready for deletion"
6159
+ },
6160
+ "external_interconnects": {
6161
+ "count": len(vpc_result.bucket_2_external),
6162
+ "annual_savings": sum(v.annual_savings for v in vpc_result.bucket_2_external),
6163
+ "risk_level": "MEDIUM",
6164
+ "status": "Dependency analysis required"
6165
+ },
6166
+ "control_plane": {
6167
+ "count": len(vpc_result.bucket_3_control),
6168
+ "annual_savings": sum(v.annual_savings for v in vpc_result.bucket_3_control),
6169
+ "risk_level": "HIGH",
6170
+ "status": "Security enhancement focus"
6171
+ }
6172
+ }
6173
+ }
5931
6174
 
5932
6175
  # Handle output file if report_name specified
5933
6176
  if report_name:
@@ -6222,6 +6465,185 @@ def finops_mcp_validation(profile, target_accuracy):
6222
6465
  raise click.Abort()
6223
6466
 
6224
6467
 
6468
+ @main.command("nat-gateway")
6469
+ @click.option('--profile', help='AWS profile name (3-tier priority: User > Environment > Default)')
6470
+ @click.option('--regions', multiple=True, help='AWS regions to analyze (space-separated)')
6471
+ @click.option('--dry-run/--no-dry-run', default=True, help='Execute in dry-run mode (READ-ONLY analysis)')
6472
+ @click.option('--export-format', type=click.Choice(['json', 'csv', 'markdown']),
6473
+ default='json', help='Export format for results')
6474
+ @click.option('--output-file', help='Output file path for results export')
6475
+ @click.option('--usage-threshold-days', type=int, default=7,
6476
+ help='CloudWatch analysis period in days')
6477
+ def nat_gateway_optimizer_cmd(profile, regions, dry_run, export_format, output_file, usage_threshold_days):
6478
+ """
6479
+ NAT Gateway Cost Optimizer - Enterprise Multi-Region Analysis
6480
+
6481
+ Part of $132,720+ annual savings methodology targeting $8K-$12K NAT Gateway optimization.
6482
+
6483
+ SAFETY: READ-ONLY analysis only - no resource modifications.
6484
+
6485
+ UNIFIED CLI: Use 'runbooks finops --scenario nat-gateway' for integrated workflow.
6486
+
6487
+ Examples:
6488
+ runbooks nat-gateway --analyze
6489
+ runbooks nat-gateway --profile my-profile --regions us-east-1 us-west-2
6490
+ runbooks nat-gateway --export-format csv --output-file nat_analysis.csv
6491
+ """
6492
+ try:
6493
+ from runbooks.finops.nat_gateway_optimizer import NATGatewayOptimizer
6494
+ import asyncio
6495
+
6496
+ # Initialize optimizer
6497
+ optimizer = NATGatewayOptimizer(
6498
+ profile_name=profile,
6499
+ regions=list(regions) if regions else None
6500
+ )
6501
+
6502
+ # Execute analysis
6503
+ results = asyncio.run(optimizer.analyze_nat_gateways(dry_run=dry_run))
6504
+
6505
+ # Export results if requested
6506
+ if output_file or export_format != 'json':
6507
+ optimizer.export_results(results, output_file, export_format)
6508
+
6509
+ # Display final success message
6510
+ from runbooks.common.rich_utils import print_success, print_info, format_cost
6511
+ if results.potential_annual_savings > 0:
6512
+ print_success(f"Analysis complete: {format_cost(results.potential_annual_savings)} potential annual savings identified")
6513
+ else:
6514
+ print_info("Analysis complete: All NAT Gateways are optimally configured")
6515
+
6516
+ except KeyboardInterrupt:
6517
+ console.print("[yellow]Analysis interrupted by user[/yellow]")
6518
+ raise click.Abort()
6519
+ except Exception as e:
6520
+ console.print(f"[red]NAT Gateway analysis failed: {e}[/red]")
6521
+ raise click.Abort()
6522
+
6523
+
6524
+ @main.command("elastic-ip")
6525
+ @click.option('--profile', help='AWS profile name (3-tier priority: User > Environment > Default)')
6526
+ @click.option('--regions', multiple=True, help='AWS regions to analyze (space-separated)')
6527
+ @click.option('--dry-run/--no-dry-run', default=True, help='Execute in dry-run mode (READ-ONLY analysis)')
6528
+ @click.option('--export-format', type=click.Choice(['json', 'csv', 'markdown']),
6529
+ default='json', help='Export format for results')
6530
+ @click.option('--output-file', help='Output file path for results export')
6531
+ def elastic_ip_optimizer_cmd(profile, regions, dry_run, export_format, output_file):
6532
+ """
6533
+ Elastic IP Cost Optimizer - Enterprise Multi-Region Analysis
6534
+
6535
+ Part of $132,720+ annual savings methodology targeting direct cost elimination.
6536
+
6537
+ SAFETY: READ-ONLY analysis only - no resource modifications.
6538
+
6539
+ UNIFIED CLI: Use 'runbooks finops --scenario elastic-ip' for integrated workflow.
6540
+
6541
+ Examples:
6542
+ runbooks elastic-ip --cleanup
6543
+ runbooks elastic-ip --profile my-profile --regions us-east-1 us-west-2
6544
+ runbooks elastic-ip --export-format csv --output-file eip_analysis.csv
6545
+ """
6546
+ try:
6547
+ from runbooks.finops.elastic_ip_optimizer import ElasticIPOptimizer
6548
+
6549
+ # Initialize optimizer
6550
+ optimizer = ElasticIPOptimizer(
6551
+ profile_name=profile,
6552
+ regions=list(regions) if regions else None
6553
+ )
6554
+
6555
+ # Execute analysis
6556
+ results = asyncio.run(optimizer.analyze_elastic_ips(dry_run=dry_run))
6557
+
6558
+ # Export results if requested
6559
+ if output_file or export_format != 'json':
6560
+ optimizer.export_results(results, output_file, export_format)
6561
+
6562
+ # Display final success message
6563
+ from runbooks.common.rich_utils import print_success, print_info, format_cost
6564
+ if results.potential_annual_savings > 0:
6565
+ print_success(f"Analysis complete: {format_cost(results.potential_annual_savings)} potential annual savings identified")
6566
+ else:
6567
+ print_info("Analysis complete: All Elastic IPs are optimally configured")
6568
+
6569
+ except KeyboardInterrupt:
6570
+ console.print("[yellow]Analysis interrupted by user[/yellow]")
6571
+ raise click.Abort()
6572
+ except Exception as e:
6573
+ console.print(f"[red]Elastic IP analysis failed: {e}[/red]")
6574
+ raise click.Abort()
6575
+
6576
+
6577
+ @main.command("ebs")
6578
+ @click.option('--profile', help='AWS profile name (3-tier priority: User > Environment > Default)')
6579
+ @click.option('--regions', multiple=True, help='AWS regions to analyze (space-separated)')
6580
+ @click.option('--dry-run/--no-dry-run', default=True, help='Execute in dry-run mode (READ-ONLY analysis)')
6581
+ @click.option('--export-format', type=click.Choice(['json', 'csv', 'markdown']),
6582
+ default='json', help='Export format for results')
6583
+ @click.option('--output-file', help='Output file path for results export')
6584
+ @click.option('--usage-threshold-days', type=int, default=7,
6585
+ help='CloudWatch analysis period in days')
6586
+ def ebs_optimizer_cmd(profile, regions, dry_run, export_format, output_file, usage_threshold_days):
6587
+ """
6588
+ EBS Volume Optimizer - Enterprise Multi-Region Storage Analysis
6589
+
6590
+ Comprehensive EBS storage cost optimization combining 3 strategies:
6591
+ • GP2→GP3 conversion analysis (15-20% storage cost reduction)
6592
+ • Low usage volume detection via CloudWatch metrics
6593
+ • Orphaned volume cleanup from stopped/terminated instances
6594
+
6595
+ Part of $132,720+ annual savings methodology completing Tier 1 High-Value engine.
6596
+
6597
+ SAFETY: READ-ONLY analysis only - no resource modifications.
6598
+
6599
+ UNIFIED CLI: Use 'runbooks finops --scenario ebs' for integrated workflow.
6600
+
6601
+ Examples:
6602
+ runbooks ebs --optimize
6603
+ runbooks ebs --profile my-profile --regions us-east-1 us-west-2
6604
+ runbooks ebs --export-format csv --output-file ebs_analysis.csv
6605
+ """
6606
+ try:
6607
+ from runbooks.finops.ebs_optimizer import EBSOptimizer
6608
+ import asyncio
6609
+
6610
+ # Initialize optimizer
6611
+ optimizer = EBSOptimizer(
6612
+ profile_name=profile,
6613
+ regions=list(regions) if regions else None
6614
+ )
6615
+
6616
+ # Execute comprehensive analysis
6617
+ results = asyncio.run(optimizer.analyze_ebs_volumes(dry_run=dry_run))
6618
+
6619
+ # Export results if requested
6620
+ if output_file or export_format != 'json':
6621
+ optimizer.export_results(results, output_file, export_format)
6622
+
6623
+ # Display final success message
6624
+ from runbooks.common.rich_utils import print_success, print_info, format_cost
6625
+ if results.total_potential_annual_savings > 0:
6626
+ savings_breakdown = []
6627
+ if results.gp3_potential_annual_savings > 0:
6628
+ savings_breakdown.append(f"GP2→GP3: {format_cost(results.gp3_potential_annual_savings)}")
6629
+ if results.low_usage_potential_annual_savings > 0:
6630
+ savings_breakdown.append(f"Usage: {format_cost(results.low_usage_potential_annual_savings)}")
6631
+ if results.orphaned_potential_annual_savings > 0:
6632
+ savings_breakdown.append(f"Orphaned: {format_cost(results.orphaned_potential_annual_savings)}")
6633
+
6634
+ print_success(f"Analysis complete: {format_cost(results.total_potential_annual_savings)} potential annual savings")
6635
+ print_info(f"Optimization strategies: {' | '.join(savings_breakdown)}")
6636
+ else:
6637
+ print_info("Analysis complete: All EBS volumes are optimally configured")
6638
+
6639
+ except KeyboardInterrupt:
6640
+ console.print("[yellow]Analysis interrupted by user[/yellow]")
6641
+ raise click.Abort()
6642
+ except Exception as e:
6643
+ console.print(f"[red]EBS optimization analysis failed: {e}[/red]")
6644
+ raise click.Abort()
6645
+
6646
+
6225
6647
  # ============================================================================
6226
6648
  # HELPER FUNCTIONS
6227
6649
  # ============================================================================
@@ -6931,44 +7353,69 @@ def vpc(ctx):
6931
7353
 
6932
7354
  @vpc.command()
6933
7355
  @common_aws_options
6934
- @click.option("--billing-profile", help="Billing profile for cost analysis")
6935
- @click.option("--days", default=30, help="Number of days to analyze")
6936
- @click.option("--output-dir", default="./exports", help="Output directory for results")
7356
+ @click.option("--vpc-ids", multiple=True, help="Specific VPC IDs to analyze (space-separated)")
7357
+ @click.option("--output-dir", default="./awso_evidence", help="Output directory for evidence")
7358
+ @click.option("--generate-evidence", is_flag=True, default=True, help="Generate AWSO-05 evidence bundle")
6937
7359
  @click.pass_context
6938
- def analyze(ctx, profile, region, dry_run, billing_profile, days, output_dir):
7360
+ def analyze(ctx, profile, region, dry_run, vpc_ids, output_dir, generate_evidence):
6939
7361
  """
6940
- 🔍 Analyze VPC networking components and costs
7362
+ 🔍 Comprehensive VPC analysis with AWSO-05 integration
7363
+
7364
+ Migrated from VPC module with enhanced capabilities:
7365
+ - Complete VPC topology discovery
7366
+ - 12-step AWSO-05 dependency analysis
7367
+ - ENI gate validation for workload protection
7368
+ - Evidence bundle generation for compliance
6941
7369
 
6942
7370
  Examples:
6943
- runbooks vpc analyze --profile prod --days 30
6944
- runbooks vpc analyze --billing-profile billing-profile
7371
+ runbooks vpc analyze --profile prod
7372
+ runbooks vpc analyze --vpc-ids vpc-123 vpc-456 --generate-evidence
7373
+ runbooks vpc analyze --output-dir ./custom_evidence
6945
7374
  """
6946
- console.print("[cyan]🔍 VPC Networking Analysis[/cyan]")
7375
+ console.print("[cyan]🔍 VPC Analysis - Enhanced with VPC Module Integration[/cyan]")
6947
7376
 
6948
7377
  try:
6949
- from runbooks.vpc import VPCNetworkingWrapper
7378
+ from runbooks.operate.vpc_operations import VPCOperations
7379
+ from runbooks.inventory.vpc_analyzer import VPCAnalyzer
6950
7380
 
6951
- # Initialize wrapper
6952
- wrapper = VPCNetworkingWrapper(
6953
- profile=profile, region=region, billing_profile=billing_profile or profile, console=console
6954
- )
7381
+ # Initialize VPC operations with analyzer integration
7382
+ vpc_ops = VPCOperations(profile=profile, region=region, dry_run=dry_run)
7383
+
7384
+ # Convert tuple to list for VPC IDs
7385
+ vpc_id_list = list(vpc_ids) if vpc_ids else None
6955
7386
 
6956
- # Analyze NAT Gateways
6957
- console.print("\n📊 Analyzing NAT Gateways...")
6958
- nat_results = wrapper.analyze_nat_gateways(days=days)
7387
+ console.print(f"\n🔍 Starting comprehensive VPC analysis...")
7388
+ if vpc_id_list:
7389
+ console.print(f"Analyzing specific VPCs: {', '.join(vpc_id_list)}")
7390
+ else:
7391
+ console.print("Analyzing all VPCs in region")
7392
+ console.print(f"📊 Analysis includes: topology discovery, cost analysis, AWSO-05 compliance")
6959
7393
 
6960
- # Analyze VPC Endpoints
6961
- console.print("\n🔗 Analyzing VPC Endpoints...")
6962
- vpc_endpoint_results = wrapper.analyze_vpc_endpoints()
7394
+ # Execute integrated VPC analysis workflow
7395
+ results = vpc_ops.execute_integrated_vpc_analysis(
7396
+ vpc_ids=vpc_id_list,
7397
+ generate_evidence=generate_evidence
7398
+ )
6963
7399
 
6964
- # Export results
6965
- if output_dir:
6966
- console.print(f"\n📁 Exporting results to {output_dir}...")
6967
- exported = wrapper.export_results(output_dir)
6968
- console.print(f"[green]✅ Exported {len(exported)} files[/green]")
7400
+ # Display results summary
7401
+ summary = results['analysis_summary']
7402
+ console.print(f"\n VPC Analysis Complete!")
7403
+ console.print(f"📊 Resources discovered: {summary['total_resources']}")
7404
+ console.print(f"💰 Monthly network cost: ${summary['estimated_monthly_cost']:.2f}")
7405
+
7406
+ if summary['default_vpcs_found'] > 0:
7407
+ console.print(f"🚨 Default VPCs found: {summary['default_vpcs_found']} (security risk)")
7408
+
7409
+ if summary['eni_gate_warnings'] > 0:
7410
+ console.print(f"⚠️ ENI warnings: {summary['eni_gate_warnings']} (workload protection)")
7411
+
7412
+ console.print(f"🎯 Cleanup readiness: {summary['cleanup_readiness']}")
7413
+
7414
+ if results.get('evidence_files'):
7415
+ console.print(f"📋 Evidence bundle: {len(results['evidence_files'])} files in {output_dir}")
6969
7416
 
6970
7417
  except Exception as e:
6971
- console.print(f"[red]❌ Error: {e}[/red]")
7418
+ console.print(f"[red]❌ VPC Analysis Error: {e}[/red]")
6972
7419
  logger.error(f"VPC analysis failed: {e}")
6973
7420
  sys.exit(1)
6974
7421