runbooks 1.1.3__py3-none-any.whl → 1.1.5__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 (247) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/WEIGHT_CONFIG_README.md +1 -1
  8. runbooks/cfat/assessment/compliance.py +8 -8
  9. runbooks/cfat/assessment/runner.py +1 -0
  10. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  11. runbooks/cfat/models.py +6 -2
  12. runbooks/cfat/tests/__init__.py +6 -1
  13. runbooks/cli/__init__.py +13 -0
  14. runbooks/cli/commands/cfat.py +274 -0
  15. runbooks/cli/commands/finops.py +1164 -0
  16. runbooks/cli/commands/inventory.py +379 -0
  17. runbooks/cli/commands/operate.py +239 -0
  18. runbooks/cli/commands/security.py +248 -0
  19. runbooks/cli/commands/validation.py +825 -0
  20. runbooks/cli/commands/vpc.py +310 -0
  21. runbooks/cli/registry.py +107 -0
  22. runbooks/cloudops/__init__.py +23 -30
  23. runbooks/cloudops/base.py +96 -107
  24. runbooks/cloudops/cost_optimizer.py +549 -547
  25. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  26. runbooks/cloudops/interfaces.py +226 -227
  27. runbooks/cloudops/lifecycle_manager.py +5 -4
  28. runbooks/cloudops/mcp_cost_validation.py +252 -235
  29. runbooks/cloudops/models.py +78 -53
  30. runbooks/cloudops/monitoring_automation.py +5 -4
  31. runbooks/cloudops/notebook_framework.py +179 -215
  32. runbooks/cloudops/security_enforcer.py +125 -159
  33. runbooks/common/accuracy_validator.py +11 -0
  34. runbooks/common/aws_pricing.py +349 -326
  35. runbooks/common/aws_pricing_api.py +211 -212
  36. runbooks/common/aws_profile_manager.py +341 -0
  37. runbooks/common/aws_utils.py +75 -80
  38. runbooks/common/business_logic.py +127 -105
  39. runbooks/common/cli_decorators.py +36 -60
  40. runbooks/common/comprehensive_cost_explorer_integration.py +456 -464
  41. runbooks/common/cross_account_manager.py +198 -205
  42. runbooks/common/date_utils.py +27 -39
  43. runbooks/common/decorators.py +235 -0
  44. runbooks/common/dry_run_examples.py +173 -208
  45. runbooks/common/dry_run_framework.py +157 -155
  46. runbooks/common/enhanced_exception_handler.py +15 -4
  47. runbooks/common/enhanced_logging_example.py +50 -64
  48. runbooks/common/enhanced_logging_integration_example.py +65 -37
  49. runbooks/common/env_utils.py +16 -16
  50. runbooks/common/error_handling.py +40 -38
  51. runbooks/common/lazy_loader.py +41 -23
  52. runbooks/common/logging_integration_helper.py +79 -86
  53. runbooks/common/mcp_cost_explorer_integration.py +478 -495
  54. runbooks/common/mcp_integration.py +63 -74
  55. runbooks/common/memory_optimization.py +140 -118
  56. runbooks/common/module_cli_base.py +37 -58
  57. runbooks/common/organizations_client.py +176 -194
  58. runbooks/common/patterns.py +204 -0
  59. runbooks/common/performance_monitoring.py +67 -71
  60. runbooks/common/performance_optimization_engine.py +283 -274
  61. runbooks/common/profile_utils.py +248 -39
  62. runbooks/common/rich_utils.py +643 -92
  63. runbooks/common/sre_performance_suite.py +177 -186
  64. runbooks/enterprise/__init__.py +1 -1
  65. runbooks/enterprise/logging.py +144 -106
  66. runbooks/enterprise/security.py +187 -204
  67. runbooks/enterprise/validation.py +43 -56
  68. runbooks/finops/__init__.py +29 -33
  69. runbooks/finops/account_resolver.py +1 -1
  70. runbooks/finops/advanced_optimization_engine.py +980 -0
  71. runbooks/finops/automation_core.py +268 -231
  72. runbooks/finops/business_case_config.py +184 -179
  73. runbooks/finops/cli.py +660 -139
  74. runbooks/finops/commvault_ec2_analysis.py +157 -164
  75. runbooks/finops/compute_cost_optimizer.py +336 -320
  76. runbooks/finops/config.py +20 -20
  77. runbooks/finops/cost_optimizer.py +488 -622
  78. runbooks/finops/cost_processor.py +332 -214
  79. runbooks/finops/dashboard_runner.py +1006 -172
  80. runbooks/finops/ebs_cost_optimizer.py +991 -657
  81. runbooks/finops/elastic_ip_optimizer.py +317 -257
  82. runbooks/finops/enhanced_mcp_integration.py +340 -0
  83. runbooks/finops/enhanced_progress.py +40 -37
  84. runbooks/finops/enhanced_trend_visualization.py +3 -2
  85. runbooks/finops/enterprise_wrappers.py +230 -292
  86. runbooks/finops/executive_export.py +203 -160
  87. runbooks/finops/helpers.py +130 -288
  88. runbooks/finops/iam_guidance.py +1 -1
  89. runbooks/finops/infrastructure/__init__.py +80 -0
  90. runbooks/finops/infrastructure/commands.py +506 -0
  91. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  92. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  93. runbooks/finops/markdown_exporter.py +338 -175
  94. runbooks/finops/mcp_validator.py +1952 -0
  95. runbooks/finops/nat_gateway_optimizer.py +1513 -482
  96. runbooks/finops/network_cost_optimizer.py +657 -587
  97. runbooks/finops/notebook_utils.py +226 -188
  98. runbooks/finops/optimization_engine.py +1136 -0
  99. runbooks/finops/optimizer.py +25 -29
  100. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  101. runbooks/finops/reservation_optimizer.py +427 -363
  102. runbooks/finops/scenario_cli_integration.py +77 -78
  103. runbooks/finops/scenarios.py +1278 -439
  104. runbooks/finops/schemas.py +218 -182
  105. runbooks/finops/snapshot_manager.py +2289 -0
  106. runbooks/finops/tests/test_finops_dashboard.py +3 -3
  107. runbooks/finops/tests/test_reference_images_validation.py +2 -2
  108. runbooks/finops/tests/test_single_account_features.py +17 -17
  109. runbooks/finops/tests/validate_test_suite.py +1 -1
  110. runbooks/finops/types.py +3 -3
  111. runbooks/finops/validation_framework.py +263 -269
  112. runbooks/finops/vpc_cleanup_exporter.py +191 -146
  113. runbooks/finops/vpc_cleanup_optimizer.py +593 -575
  114. runbooks/finops/workspaces_analyzer.py +171 -182
  115. runbooks/hitl/enhanced_workflow_engine.py +1 -1
  116. runbooks/integration/__init__.py +89 -0
  117. runbooks/integration/mcp_integration.py +1920 -0
  118. runbooks/inventory/CLAUDE.md +816 -0
  119. runbooks/inventory/README.md +3 -3
  120. runbooks/inventory/Tests/common_test_data.py +30 -30
  121. runbooks/inventory/__init__.py +2 -2
  122. runbooks/inventory/cloud_foundations_integration.py +144 -149
  123. runbooks/inventory/collectors/aws_comprehensive.py +28 -11
  124. runbooks/inventory/collectors/aws_networking.py +111 -101
  125. runbooks/inventory/collectors/base.py +4 -0
  126. runbooks/inventory/core/collector.py +495 -313
  127. runbooks/inventory/discovery.md +2 -2
  128. runbooks/inventory/drift_detection_cli.py +69 -96
  129. runbooks/inventory/find_ec2_security_groups.py +1 -1
  130. runbooks/inventory/inventory_mcp_cli.py +48 -46
  131. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  132. runbooks/inventory/mcp_inventory_validator.py +549 -465
  133. runbooks/inventory/mcp_vpc_validator.py +359 -442
  134. runbooks/inventory/organizations_discovery.py +56 -52
  135. runbooks/inventory/rich_inventory_display.py +33 -32
  136. runbooks/inventory/unified_validation_engine.py +278 -251
  137. runbooks/inventory/vpc_analyzer.py +733 -696
  138. runbooks/inventory/vpc_architecture_validator.py +293 -348
  139. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  140. runbooks/inventory/vpc_flow_analyzer.py +3 -3
  141. runbooks/main.py +152 -9147
  142. runbooks/main_final.py +91 -60
  143. runbooks/main_minimal.py +22 -10
  144. runbooks/main_optimized.py +131 -100
  145. runbooks/main_ultra_minimal.py +7 -2
  146. runbooks/mcp/__init__.py +36 -0
  147. runbooks/mcp/integration.py +679 -0
  148. runbooks/metrics/dora_metrics_engine.py +2 -2
  149. runbooks/monitoring/performance_monitor.py +9 -4
  150. runbooks/operate/dynamodb_operations.py +3 -1
  151. runbooks/operate/ec2_operations.py +145 -137
  152. runbooks/operate/iam_operations.py +146 -152
  153. runbooks/operate/mcp_integration.py +1 -1
  154. runbooks/operate/networking_cost_heatmap.py +33 -10
  155. runbooks/operate/privatelink_operations.py +1 -1
  156. runbooks/operate/rds_operations.py +223 -254
  157. runbooks/operate/s3_operations.py +107 -118
  158. runbooks/operate/vpc_endpoints.py +1 -1
  159. runbooks/operate/vpc_operations.py +648 -618
  160. runbooks/remediation/base.py +1 -1
  161. runbooks/remediation/commons.py +10 -7
  162. runbooks/remediation/commvault_ec2_analysis.py +71 -67
  163. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  164. runbooks/remediation/multi_account.py +24 -21
  165. runbooks/remediation/rds_snapshot_list.py +91 -65
  166. runbooks/remediation/remediation_cli.py +92 -146
  167. runbooks/remediation/universal_account_discovery.py +83 -79
  168. runbooks/remediation/workspaces_list.py +49 -44
  169. runbooks/security/__init__.py +19 -0
  170. runbooks/security/assessment_runner.py +1150 -0
  171. runbooks/security/baseline_checker.py +812 -0
  172. runbooks/security/cloudops_automation_security_validator.py +509 -535
  173. runbooks/security/compliance_automation_engine.py +17 -17
  174. runbooks/security/config/__init__.py +2 -2
  175. runbooks/security/config/compliance_config.py +50 -50
  176. runbooks/security/config_template_generator.py +63 -76
  177. runbooks/security/enterprise_security_framework.py +1 -1
  178. runbooks/security/executive_security_dashboard.py +519 -508
  179. runbooks/security/integration_test_enterprise_security.py +5 -3
  180. runbooks/security/multi_account_security_controls.py +959 -1210
  181. runbooks/security/real_time_security_monitor.py +422 -444
  182. runbooks/security/run_script.py +1 -1
  183. runbooks/security/security_baseline_tester.py +1 -1
  184. runbooks/security/security_cli.py +143 -112
  185. runbooks/security/test_2way_validation.py +439 -0
  186. runbooks/security/two_way_validation_framework.py +852 -0
  187. runbooks/sre/mcp_reliability_engine.py +6 -6
  188. runbooks/sre/production_monitoring_framework.py +167 -177
  189. runbooks/tdd/__init__.py +15 -0
  190. runbooks/tdd/cli.py +1071 -0
  191. runbooks/utils/__init__.py +14 -17
  192. runbooks/utils/logger.py +7 -2
  193. runbooks/utils/version_validator.py +51 -48
  194. runbooks/validation/__init__.py +6 -6
  195. runbooks/validation/cli.py +9 -3
  196. runbooks/validation/comprehensive_2way_validator.py +754 -708
  197. runbooks/validation/mcp_validator.py +906 -228
  198. runbooks/validation/terraform_citations_validator.py +104 -115
  199. runbooks/validation/terraform_drift_detector.py +447 -451
  200. runbooks/vpc/README.md +617 -0
  201. runbooks/vpc/__init__.py +8 -1
  202. runbooks/vpc/analyzer.py +577 -0
  203. runbooks/vpc/cleanup_wrapper.py +476 -413
  204. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  205. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  206. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  207. runbooks/vpc/config.py +92 -97
  208. runbooks/vpc/cost_engine.py +411 -148
  209. runbooks/vpc/cost_explorer_integration.py +553 -0
  210. runbooks/vpc/cross_account_session.py +101 -106
  211. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  212. runbooks/vpc/eni_gate_validator.py +961 -0
  213. runbooks/vpc/heatmap_engine.py +190 -162
  214. runbooks/vpc/mcp_no_eni_validator.py +681 -640
  215. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  216. runbooks/vpc/networking_wrapper.py +15 -8
  217. runbooks/vpc/pdca_remediation_planner.py +528 -0
  218. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  219. runbooks/vpc/runbooks_adapter.py +1167 -241
  220. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  221. runbooks/vpc/test_data_loader.py +358 -0
  222. runbooks/vpc/tests/conftest.py +314 -4
  223. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  224. runbooks/vpc/tests/test_cost_engine.py +0 -2
  225. runbooks/vpc/topology_generator.py +326 -0
  226. runbooks/vpc/unified_scenarios.py +1302 -1129
  227. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  228. runbooks-1.1.5.dist-info/METADATA +328 -0
  229. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/RECORD +233 -200
  230. runbooks/finops/README.md +0 -414
  231. runbooks/finops/accuracy_cross_validator.py +0 -647
  232. runbooks/finops/business_cases.py +0 -950
  233. runbooks/finops/dashboard_router.py +0 -922
  234. runbooks/finops/ebs_optimizer.py +0 -956
  235. runbooks/finops/embedded_mcp_validator.py +0 -1629
  236. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  237. runbooks/finops/finops_dashboard.py +0 -584
  238. runbooks/finops/finops_scenarios.py +0 -1218
  239. runbooks/finops/legacy_migration.py +0 -730
  240. runbooks/finops/multi_dashboard.py +0 -1519
  241. runbooks/finops/single_dashboard.py +0 -1113
  242. runbooks/finops/unlimited_scenarios.py +0 -393
  243. runbooks-1.1.3.dist-info/METADATA +0 -799
  244. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  245. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  246. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  247. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1164 @@
1
+ """
2
+ FinOps Commands Module - Financial Operations & Cost Optimization
3
+
4
+ KISS Principle: Focused on financial operations and cost optimization
5
+ DRY Principle: Uses centralized patterns from DRYPatternManager
6
+
7
+ Phase 2 Enhancement: Eliminates pattern duplication through reference-based access.
8
+ Context Efficiency: Reduced imports and shared instances for memory optimization.
9
+ """
10
+
11
+ # Essential imports that can't be centralized due to decorator usage
12
+ import click
13
+
14
+ # DRY Pattern Manager - eliminates duplication across CLI modules
15
+ from runbooks.common.patterns import get_console, get_error_handlers, get_click_group_creator, get_common_decorators
16
+
17
+ # Import common utilities and decorators
18
+ from runbooks.common.decorators import common_aws_options
19
+
20
+ # Single console instance shared across all modules (DRY principle)
21
+ console = get_console()
22
+
23
+ # Import additional modules for enhanced functionality
24
+ from runbooks.common.rich_utils import print_header, print_success, print_error, print_info
25
+
26
+ # Centralized error handlers - replaces 6 duplicate patterns in this module
27
+ error_handlers = get_error_handlers()
28
+
29
+
30
+ def _get_cost_metric_display(cost_metrics):
31
+ """Get display string for cost metrics."""
32
+ if len(cost_metrics) == 1:
33
+ return cost_metrics[0]
34
+ else:
35
+ return " + ".join(cost_metrics)
36
+
37
+
38
+ def create_finops_group():
39
+ """
40
+ Create the finops command group with all subcommands.
41
+
42
+ Returns:
43
+ Click Group object with all finops commands
44
+
45
+ Performance: Lazy creation only when needed by DRYCommandRegistry
46
+ Context Reduction: ~800 lines extracted from main.py
47
+ """
48
+
49
+ @click.group(invoke_without_command=True)
50
+ @common_aws_options
51
+ @click.pass_context
52
+ def finops(ctx, profile, region, dry_run):
53
+ """
54
+ Financial operations and cost optimization for AWS resources.
55
+
56
+ Comprehensive cost analysis, budget management, and financial reporting
57
+ with enterprise-grade accuracy and multi-format export capabilities.
58
+
59
+ Features:
60
+ • Real-time cost analysis with MCP validation (≥99.5% accuracy)
61
+ • Multi-format exports: CSV, JSON, PDF, Markdown
62
+ • Quarterly intelligence with strategic financial reporting
63
+ • Enterprise AWS profile support with multi-account capabilities
64
+
65
+ Examples:
66
+ runbooks finops dashboard --profile billing-profile
67
+ runbooks finops analyze --service ec2 --timeframe monthly
68
+ runbooks finops export --format pdf --output-dir ./reports
69
+ """
70
+ # Ensure context object exists
71
+ if ctx.obj is None:
72
+ ctx.obj = {}
73
+ ctx.obj.update({"profile": profile, "region": region, "dry_run": dry_run})
74
+
75
+ if ctx.invoked_subcommand is None:
76
+ click.echo(ctx.get_help())
77
+
78
+ @finops.command()
79
+ @click.option("--profile", help="AWS profile to use for authentication")
80
+ @click.option(
81
+ "--all-profiles", help="Enable multi-account Landing Zone analysis using specified management profile"
82
+ )
83
+ @click.option(
84
+ "--timeframe",
85
+ type=click.Choice(["daily", "weekly", "monthly", "quarterly"]),
86
+ default="monthly",
87
+ help="Analysis timeframe",
88
+ )
89
+ @click.option("--services", multiple=True, help="Specific AWS services to analyze")
90
+ @click.option("--accounts", multiple=True, help="Specific AWS accounts to analyze")
91
+ @click.option("--validate", is_flag=True, help="Enable MCP validation for accuracy")
92
+ @click.option("--validate-mcp", is_flag=True, help="Run standalone MCP validation framework (AWS-2 implementation)")
93
+ @click.option("--mcp-validate", is_flag=True, help="Enable MCP validation for ≥99.5% accuracy cross-validation")
94
+ @click.option("--csv", is_flag=True, help="Export results to CSV format")
95
+ @click.option("--markdown", is_flag=True, help="Export results to Markdown format")
96
+ @click.option("--pdf", is_flag=True, help="Export results to PDF format")
97
+ @click.option("--json", is_flag=True, help="Export results to JSON format")
98
+ @click.option(
99
+ "--export-format",
100
+ type=click.Choice(["json", "csv", "pdf", "markdown"]),
101
+ help="Export format for results (legacy option - use individual flags)",
102
+ )
103
+ @click.option("--unblended", is_flag=True, help="Use unblended cost metrics (default: BlendedCost)")
104
+ @click.option("--amortized", is_flag=True, help="Use amortized cost metrics for Reserved Instances")
105
+ @click.option("--dual-metrics", is_flag=True, help="Show both BlendedCost and AmortizedCost")
106
+ @click.option("--dry-run", is_flag=True, default=True, help="Execute in dry-run mode")
107
+ @click.pass_context
108
+ def dashboard(
109
+ ctx,
110
+ profile,
111
+ all_profiles,
112
+ timeframe,
113
+ services,
114
+ accounts,
115
+ validate,
116
+ validate_mcp,
117
+ mcp_validate,
118
+ csv,
119
+ markdown,
120
+ pdf,
121
+ json,
122
+ export_format,
123
+ unblended,
124
+ amortized,
125
+ dual_metrics,
126
+ dry_run,
127
+ ):
128
+ """
129
+ Generate comprehensive cost analysis dashboard.
130
+
131
+ Enterprise Features:
132
+ • MCP validation with ≥99.5% accuracy
133
+ • Multi-account Landing Zone consolidated billing analysis
134
+ • Organizational unit hierarchy and cost allocation
135
+ • Rich CLI formatting for executive presentations
136
+ • Multi-format exports for stakeholder consumption
137
+
138
+ Examples:
139
+ # Single account analysis
140
+ runbooks finops dashboard --profile BILLING_PROFILE --timeframe monthly --validate
141
+
142
+ # Multi-account Landing Zone analysis
143
+ runbooks finops dashboard --all-profiles MANAGEMENT_PROFILE --mcp-validate
144
+
145
+ # Service-specific analysis across all accounts
146
+ runbooks finops dashboard --all-profiles MANAGEMENT_PROFILE --services ec2,s3
147
+
148
+ # Export multi-account analysis
149
+ runbooks finops dashboard --all-profiles MANAGEMENT_PROFILE --export-format pdf
150
+ """
151
+ # Handle multi-account Landing Zone analysis
152
+ if all_profiles:
153
+ try:
154
+ from runbooks.finops.dashboard_runner import MultiAccountDashboard, DashboardRouter
155
+ from runbooks.common.rich_utils import print_header, print_success, print_error, print_info
156
+ import argparse
157
+
158
+ print_header("Multi-Account Landing Zone Dashboard", all_profiles)
159
+ console.print("[cyan]🏢 Activating enterprise multi-account consolidated billing analysis[/cyan]")
160
+ console.print(f"[dim]Management Profile: {all_profiles}[/dim]")
161
+ console.print(f"[dim]Analysis Scope: Organization-wide with Landing Zone support[/dim]\n")
162
+
163
+ # Create mock args object for multi-dashboard compatibility
164
+ args = argparse.Namespace()
165
+ args.profile = all_profiles
166
+ args.timeframe = timeframe
167
+ args.services = services
168
+ args.accounts = accounts
169
+ args.validate = validate or mcp_validate
170
+ # CRITICAL FIX: Handle multiple export format flags
171
+ export_formats = []
172
+ if csv:
173
+ export_formats.append("csv")
174
+ if markdown:
175
+ export_formats.append("markdown")
176
+ if pdf:
177
+ export_formats.append("pdf")
178
+ if json:
179
+ export_formats.append("json")
180
+ if export_format and export_format not in export_formats:
181
+ export_formats.append(export_format)
182
+
183
+ args.export_format = export_formats[0] if export_formats else None
184
+ args.export_formats = export_formats # Store all requested formats
185
+
186
+ # CRITICAL FIX: Handle cost metric options
187
+ cost_metrics = ["BlendedCost"] # Default metric
188
+ if unblended:
189
+ cost_metrics = ["UnblendedCost"]
190
+ elif amortized:
191
+ cost_metrics = ["AmortizedCost"]
192
+ elif dual_metrics:
193
+ cost_metrics = ["BlendedCost", "AmortizedCost"]
194
+
195
+ args.cost_metrics = cost_metrics
196
+ args.cost_metric_display = _get_cost_metric_display(cost_metrics)
197
+ args.dry_run = dry_run
198
+ args.all = True # Enable all accounts mode
199
+ args.top_accounts = 50 # Show many accounts for enterprise view
200
+ args.services_per_account = 3
201
+ args.time_range = None
202
+ args.tag = None
203
+ args.regions = None
204
+
205
+ # Initialize router and dashboard
206
+ router = DashboardRouter(console=console)
207
+ routing_config = router.route_dashboard_request(args)
208
+
209
+ # Create multi-account dashboard
210
+ multi_dashboard = MultiAccountDashboard(console=console)
211
+
212
+ # Execute multi-account analysis
213
+ result = multi_dashboard.run_dashboard(args, routing_config)
214
+
215
+ if result == 0:
216
+ print_success("Multi-account Landing Zone analysis completed successfully")
217
+ else:
218
+ print_error("Multi-account analysis encountered issues")
219
+
220
+ return result
221
+
222
+ except ImportError as e:
223
+ console.print(f"[red]❌ Multi-account dashboard not available: {e}[/red]")
224
+ console.print("[yellow]💡 Falling back to single-account mode with specified profile[/yellow]")
225
+ # Fallback to single account with the specified profile
226
+ resolved_profile = all_profiles
227
+ except Exception as e:
228
+ console.print(f"[red]❌ Multi-account analysis failed: {e}[/red]")
229
+ console.print("[yellow]💡 Falling back to single-account mode[/yellow]")
230
+ resolved_profile = all_profiles
231
+ else:
232
+ resolved_profile = profile or ctx.obj.get("profile", "default")
233
+
234
+ # Handle standalone MCP validation (AWS-2 implementation)
235
+ if validate_mcp:
236
+ try:
237
+ from runbooks.common.rich_utils import print_header, print_success, print_error, print_info
238
+ import asyncio
239
+
240
+ print_header("MCP Validation Framework", "AWS-2 Implementation")
241
+ console.print("[cyan]🔍 Running comprehensive MCP validation for ≥99.5% accuracy[/cyan]")
242
+
243
+ # Import and initialize MCP validator
244
+ from runbooks.validation.mcp_validator import MCPValidator
245
+
246
+ # Enterprise profiles configuration
247
+ validation_profiles = {
248
+ "billing": "ams-admin-Billing-ReadOnlyAccess-909135376185",
249
+ "management": "ams-admin-ReadOnlyAccess-909135376185",
250
+ "centralised_ops": "ams-centralised-ops-ReadOnlyAccess-335083429030",
251
+ "single_aws": "ams-shared-services-non-prod-ReadOnlyAccess-499201730520",
252
+ }
253
+
254
+ # Initialize validator with configured profiles
255
+ validator = MCPValidator(
256
+ profiles=validation_profiles, tolerance_percentage=5.0, performance_target_seconds=30.0
257
+ )
258
+
259
+ # Run comprehensive validation
260
+ validation_report = asyncio.run(validator.validate_all_operations())
261
+
262
+ # Success criteria for AWS-2
263
+ if validation_report.overall_accuracy >= 99.5:
264
+ print_success(
265
+ f"✅ AWS-2 SUCCESS: {validation_report.overall_accuracy:.1f}% ≥ 99.5% target achieved"
266
+ )
267
+ return 0
268
+ else:
269
+ print_error(f"❌ AWS-2 FAILED: {validation_report.overall_accuracy:.1f}% < 99.5% target")
270
+ return 1
271
+
272
+ except Exception as e:
273
+ print_error(f"❌ AWS-2 MCP validation failed: {e}")
274
+ return 1
275
+
276
+ try:
277
+ from runbooks.common.rich_utils import print_header, print_success, print_error, create_table, format_cost
278
+ from runbooks.common.profile_utils import create_cost_session
279
+ from runbooks.finops.cost_processor import get_cost_data
280
+ from runbooks.finops.aws_client import get_account_id, ec2_summary, get_accessible_regions
281
+ import boto3
282
+ from datetime import datetime, timedelta
283
+ from rich.table import Table
284
+ from rich.panel import Panel
285
+
286
+ # Resolve profile with priority: command --profile > parent context > default
287
+ # Note: resolved_profile already set above for multi-account vs single-account mode
288
+ if "resolved_profile" not in locals():
289
+ resolved_profile = profile or ctx.obj.get("profile", "default")
290
+ resolved_dry_run = dry_run if dry_run is not None else ctx.obj.get("dry_run", True)
291
+
292
+ # MCP validation integration
293
+ mcp_results = None
294
+ if mcp_validate or validate:
295
+ try:
296
+ from runbooks.validation.mcp_validator import MCPValidator
297
+ import asyncio
298
+
299
+ console.print("[cyan]🔍 Running MCP validation for dashboard data accuracy[/cyan]")
300
+
301
+ # Configure validation profiles using resolved profile
302
+ validation_profiles = {
303
+ "billing": resolved_profile,
304
+ "management": resolved_profile,
305
+ "centralised_ops": resolved_profile,
306
+ "single_aws": resolved_profile,
307
+ }
308
+
309
+ # Initialize validator
310
+ validator = MCPValidator(
311
+ profiles=validation_profiles, tolerance_percentage=5.0, performance_target_seconds=30.0
312
+ )
313
+
314
+ # Run validation focused on cost explorer operations (primary finops validation)
315
+ mcp_results = asyncio.run(validator.validate_cost_explorer())
316
+
317
+ # Display validation results
318
+ if mcp_results.accuracy_percentage >= 99.5:
319
+ console.print(
320
+ f"[green]✅ MCP Validation PASSED: {mcp_results.accuracy_percentage:.1f}% accuracy[/green]"
321
+ )
322
+ elif mcp_results.accuracy_percentage >= 95.0:
323
+ console.print(
324
+ f"[yellow]⚠️ MCP Validation WARNING: {mcp_results.accuracy_percentage:.1f}% accuracy (target: ≥99.5%)[/yellow]"
325
+ )
326
+ else:
327
+ console.print(
328
+ f"[red]❌ MCP Validation FAILED: {mcp_results.accuracy_percentage:.1f}% accuracy[/red]"
329
+ )
330
+
331
+ except Exception as e:
332
+ console.print(f"[yellow]⚠️ MCP validation failed: {e}[/yellow]")
333
+ console.print("[dim]Continuing with dashboard generation...[/dim]")
334
+
335
+ print_header("FinOps Cost Analysis Dashboard", resolved_profile)
336
+
337
+ # Create AWS session and get account info
338
+ session = create_cost_session(profile_name=resolved_profile)
339
+ account_id = get_account_id(session)
340
+
341
+ console.print(f"[cyan]📊 Analyzing costs for AWS Account: {account_id}[/cyan]\n")
342
+
343
+ # Get cost data for the specified timeframe
344
+ try:
345
+ # Calculate time range based on timeframe
346
+ time_range_days = {"daily": 7, "weekly": 30, "monthly": 90, "quarterly": 365}.get(timeframe, 30)
347
+
348
+ # Get comprehensive cost data
349
+ cost_data = get_cost_data(
350
+ session,
351
+ time_range=time_range_days,
352
+ get_trend=True,
353
+ profile_name=resolved_profile,
354
+ account_id=account_id,
355
+ )
356
+
357
+ # Display Cost Summary Table
358
+ cost_table = create_table(title="💰 Cost Analysis Summary")
359
+ cost_table.add_column("Metric", style="bold")
360
+ cost_table.add_column("Value", style="cyan")
361
+
362
+ # Access cost data using correct field names from CostData TypedDict
363
+ current_cost = cost_data.get("current_month", 0)
364
+ previous_cost = cost_data.get("last_month", 0)
365
+
366
+ cost_table.add_row("Current Monthly Spend", f"${current_cost:,.2f}")
367
+ cost_table.add_row("Previous Month", f"${previous_cost:,.2f}")
368
+
369
+ # CRITICAL FIX: Enhanced month-over-month calculation for partial months
370
+ if previous_cost > 0:
371
+ change_pct = ((current_cost - previous_cost) / previous_cost) * 100
372
+
373
+ # Check if current month is partial
374
+ from datetime import datetime
375
+
376
+ today = datetime.now()
377
+ current_day = today.day
378
+
379
+ # Add context for partial month comparisons
380
+ if current_day < 28: # Likely partial month
381
+ # Calculate daily average for comparison
382
+ daily_current = current_cost / current_day
383
+ daily_previous = previous_cost / 30 # Assume 30-day month
384
+ daily_change_pct = ((daily_current - daily_previous) / daily_previous) * 100
385
+
386
+ change_str = f"{change_pct:+.1f}% (Daily Avg: {daily_change_pct:+.1f}%)"
387
+ cost_table.add_row("Month-over-Month Change", f"[yellow]{change_str}[/yellow]")
388
+ cost_table.add_row(
389
+ "Comparison Note", f"[dim]Partial month ({current_day} days vs full previous month)[/dim]"
390
+ )
391
+ else:
392
+ # Full month comparison
393
+ change_str = f"{change_pct:+.1f}%"
394
+ if change_pct > 10:
395
+ change_str = f"[red]{change_str} ⚠️[/red]"
396
+ elif change_pct < -5:
397
+ change_str = f"[green]{change_str} ✅[/green]"
398
+ else:
399
+ change_str = f"[yellow]{change_str}[/yellow]"
400
+ cost_table.add_row("Month-over-Month Change", change_str)
401
+
402
+ cost_table.add_row("Account ID", account_id)
403
+ cost_table.add_row("Analysis Period", f"{timeframe.title()} ({time_range_days} days)")
404
+ console.print(cost_table)
405
+ console.print()
406
+
407
+ # Display Top Services by Cost
408
+ services_data = cost_data.get("costs_by_service", {})
409
+ if services_data:
410
+ services_table = create_table(title="🏗️ Top AWS Services by Cost")
411
+ services_table.add_column("Service", style="bold")
412
+ services_table.add_column("Cost", style="green")
413
+ services_table.add_column("% of Total", style="yellow")
414
+
415
+ # Sort services by cost and show top 10
416
+ sorted_services = sorted(services_data.items(), key=lambda x: x[1], reverse=True)[:10]
417
+
418
+ for service, cost in sorted_services:
419
+ percentage = (cost / current_cost * 100) if current_cost > 0 else 0
420
+ services_table.add_row(service, f"${cost:,.2f}", f"{percentage:.1f}%")
421
+
422
+ console.print(services_table)
423
+ console.print()
424
+
425
+ # Get EC2 resource summary for optimization opportunities
426
+ try:
427
+ ec2_data = ec2_summary(session, profile_name=resolved_profile)
428
+
429
+ resources_table = create_table(title="💡 Optimization Opportunities")
430
+ resources_table.add_column("Resource Type", style="bold")
431
+ resources_table.add_column("Count", style="cyan")
432
+ resources_table.add_column("Potential Action", style="yellow")
433
+
434
+ # EC2Summary is a Dict[str, int], so access it accordingly
435
+ total_instances = ec2_data.get("total_instances", 0)
436
+ running_instances = ec2_data.get("running_instances", 0)
437
+ stopped_instances = ec2_data.get("stopped_instances", 0)
438
+
439
+ # Add EC2 optimization opportunities
440
+ if total_instances > 0:
441
+ resources_table.add_row("Total EC2 Instances", str(total_instances), "Review rightsizing")
442
+ resources_table.add_row("Running Instances", str(running_instances), "Monitor utilization")
443
+ resources_table.add_row("Stopped Instances", str(stopped_instances), "Consider termination")
444
+
445
+ # Calculate potential savings estimates
446
+ if stopped_instances > 0:
447
+ # Estimate $100/month per stopped instance for EBS storage costs
448
+ stopped_savings = stopped_instances * 100
449
+ resources_table.add_row(
450
+ "Stopped Instance Savings", f"~${stopped_savings}/month", "Terminate unused instances"
451
+ )
452
+
453
+ if running_instances > 5:
454
+ # Estimate 20% rightsizing opportunity
455
+ ec2_cost_estimate = services_data.get("Amazon Elastic Compute Cloud - Compute", 0)
456
+ rightsizing_savings = ec2_cost_estimate * 0.20
457
+ if rightsizing_savings > 100:
458
+ resources_table.add_row(
459
+ "EC2 Rightsizing Potential",
460
+ f"~${rightsizing_savings:.0f}/month",
461
+ "Rightsize overprovisioned instances",
462
+ )
463
+
464
+ else:
465
+ resources_table.add_row("EC2 Instances", "0", "No instances found in accessible regions")
466
+
467
+ console.print(resources_table)
468
+ console.print()
469
+
470
+ except Exception as e:
471
+ console.print(f"[yellow]⚠️ Could not fetch EC2 optimization data: {e}[/yellow]\n")
472
+
473
+ # Display Business Impact Summary
474
+ total_annual = current_cost * 12
475
+ optimization_potential = total_annual * 0.15 # Conservative 15% optimization target
476
+
477
+ business_panel = Panel(
478
+ f"""[bold]Annual Spend Projection:[/bold] ${total_annual:,.2f}
479
+ [bold]Conservative Optimization Target (15%):[/bold] ${optimization_potential:,.2f}
480
+ [bold]Recommended Actions:[/bold]
481
+ • Review EC2 instance rightsizing opportunities
482
+ • Analyze unused resources and storage
483
+ • Implement automated cost monitoring
484
+ • Set up budget alerts for cost control
485
+
486
+ [dim]Analysis Period: {timeframe.title()} view | Account: {account_id}[/dim]""",
487
+ title="💼 Business Impact Summary",
488
+ border_style="blue",
489
+ )
490
+ console.print(business_panel)
491
+
492
+ # Prepare results dictionary
493
+ results = {
494
+ "status": "completed",
495
+ "account_id": account_id,
496
+ "timeframe": timeframe,
497
+ "cost_analysis": {
498
+ "current_monthly_spend": current_cost,
499
+ "previous_monthly_spend": previous_cost,
500
+ "annual_projection": total_annual,
501
+ "optimization_potential": optimization_potential,
502
+ "top_services": dict(sorted_services[:5]) if services_data else {},
503
+ "ec2_summary": {
504
+ "total_instances": total_instances if "total_instances" in locals() else 0,
505
+ "running_instances": running_instances if "running_instances" in locals() else 0,
506
+ "stopped_instances": stopped_instances if "stopped_instances" in locals() else 0,
507
+ },
508
+ },
509
+ }
510
+
511
+ # Attach MCP validation results if available
512
+ if mcp_results:
513
+ results["mcp_validation"] = {
514
+ "accuracy_percentage": mcp_results.accuracy_percentage,
515
+ "validation_passed": mcp_results.accuracy_percentage >= 99.5,
516
+ "operation_name": mcp_results.operation_name,
517
+ "status": mcp_results.status.value,
518
+ "detailed_results": mcp_results,
519
+ }
520
+
521
+ return results
522
+
523
+ except Exception as e:
524
+ print_error(f"Failed to retrieve cost data: {e}")
525
+ console.print(
526
+ f"[yellow]💡 Tip: Ensure your AWS profile '{resolved_profile}' has Cost Explorer permissions[/yellow]"
527
+ )
528
+ console.print(f"[dim]Required permissions: ce:GetCostAndUsage, ce:GetDimensionValues[/dim]")
529
+ raise
530
+
531
+ except ImportError as e:
532
+ error_handlers["module_not_available"]("FinOps dashboard", e)
533
+ raise click.ClickException("FinOps dashboard functionality not available")
534
+ except Exception as e:
535
+ error_handlers["operation_failed"]("FinOps dashboard generation", e)
536
+ raise click.ClickException(str(e))
537
+
538
+ @finops.command()
539
+ @click.option(
540
+ "--resource-type",
541
+ type=click.Choice(["ec2", "s3", "rds", "lambda", "vpc"]),
542
+ required=True,
543
+ help="Resource type for optimization analysis",
544
+ )
545
+ @click.option(
546
+ "--savings-target", type=click.FloatRange(0.1, 0.8), default=0.3, help="Target savings percentage (0.1-0.8)"
547
+ )
548
+ @click.option(
549
+ "--analysis-depth",
550
+ type=click.Choice(["basic", "comprehensive", "enterprise"]),
551
+ default="comprehensive",
552
+ help="Analysis depth level",
553
+ )
554
+ @click.option("--mcp-validate", is_flag=True, help="Enable MCP validation for ≥99.5% accuracy cross-validation")
555
+ @click.pass_context
556
+ def optimize(ctx, resource_type, savings_target, analysis_depth, mcp_validate):
557
+ """
558
+ Generate cost optimization recommendations for specific resource types.
559
+
560
+ Enterprise Optimization Features:
561
+ • Safety-first analysis with READ-ONLY operations
562
+ • Quantified savings projections with ROI analysis
563
+ • Risk assessment and business impact evaluation
564
+ • Implementation timeline and priority recommendations
565
+
566
+ Examples:
567
+ runbooks finops optimize --resource-type ec2 --savings-target 0.25
568
+ runbooks finops optimize --resource-type s3 --analysis-depth enterprise
569
+ """
570
+ try:
571
+ from runbooks.finops.optimization_engine import ResourceOptimizer
572
+
573
+ # MCP validation integration for optimization accuracy
574
+ mcp_results = None
575
+ if mcp_validate:
576
+ try:
577
+ from runbooks.validation.mcp_validator import MCPValidator
578
+ import asyncio
579
+
580
+ console.print(f"[cyan]🔍 Running MCP validation for {resource_type} optimization accuracy[/cyan]")
581
+
582
+ # Configure validation profiles
583
+ validation_profiles = {
584
+ "billing": ctx.obj.get("profile", "default"),
585
+ "management": ctx.obj.get("profile", "default"),
586
+ "centralised_ops": ctx.obj.get("profile", "default"),
587
+ "single_aws": ctx.obj.get("profile", "default"),
588
+ }
589
+
590
+ # Initialize validator
591
+ validator = MCPValidator(
592
+ profiles=validation_profiles, tolerance_percentage=5.0, performance_target_seconds=30.0
593
+ )
594
+
595
+ # Run validation based on resource type
596
+ if resource_type in ["ec2"]:
597
+ mcp_results = asyncio.run(validator.validate_ec2_inventory())
598
+ elif resource_type in ["vpc"]:
599
+ mcp_results = asyncio.run(validator.validate_vpc_analysis())
600
+ elif resource_type in ["s3", "rds", "lambda"]:
601
+ # For these resource types, use cost explorer validation
602
+ mcp_results = asyncio.run(validator.validate_cost_explorer())
603
+ else:
604
+ # Default to cost explorer validation
605
+ mcp_results = asyncio.run(validator.validate_cost_explorer())
606
+
607
+ # Display validation results
608
+ if mcp_results.accuracy_percentage >= 99.5:
609
+ console.print(
610
+ f"[green]✅ MCP Validation PASSED: {mcp_results.accuracy_percentage:.1f}% accuracy for {resource_type}[/green]"
611
+ )
612
+ elif mcp_results.accuracy_percentage >= 95.0:
613
+ console.print(
614
+ f"[yellow]⚠️ MCP Validation WARNING: {mcp_results.accuracy_percentage:.1f}% accuracy (target: ≥99.5%)[/yellow]"
615
+ )
616
+ else:
617
+ console.print(
618
+ f"[red]❌ MCP Validation FAILED: {mcp_results.accuracy_percentage:.1f}% accuracy[/red]"
619
+ )
620
+
621
+ except Exception as e:
622
+ console.print(f"[yellow]⚠️ MCP validation failed: {e}[/yellow]")
623
+ console.print("[dim]Continuing with optimization analysis...[/dim]")
624
+
625
+ optimizer = ResourceOptimizer(
626
+ profile=ctx.obj["profile"],
627
+ region=ctx.obj["region"],
628
+ resource_type=resource_type,
629
+ savings_target=savings_target,
630
+ analysis_depth=analysis_depth,
631
+ mcp_validate=mcp_validate,
632
+ )
633
+
634
+ optimization_results = optimizer.analyze_optimization_opportunities()
635
+
636
+ # Attach MCP validation results if available
637
+ if mcp_results and isinstance(optimization_results, dict):
638
+ optimization_results["mcp_validation"] = {
639
+ "accuracy_percentage": mcp_results.accuracy_percentage,
640
+ "validation_passed": mcp_results.accuracy_percentage >= 99.5,
641
+ "resource_type": resource_type,
642
+ "operation_name": mcp_results.operation_name,
643
+ "status": mcp_results.status.value,
644
+ "detailed_results": mcp_results,
645
+ }
646
+
647
+ return optimization_results
648
+
649
+ except ImportError as e:
650
+ error_handlers["module_not_available"]("FinOps optimization", e)
651
+ raise click.ClickException("FinOps optimization functionality not available")
652
+ except Exception as e:
653
+ error_handlers["operation_failed"]("FinOps optimization analysis", e)
654
+ raise click.ClickException(str(e))
655
+
656
+ @finops.command()
657
+ @click.option(
658
+ "--format",
659
+ "export_format",
660
+ type=click.Choice(["csv", "json", "pdf", "markdown"]),
661
+ multiple=True,
662
+ default=["json"],
663
+ help="Export formats",
664
+ )
665
+ @click.option("--output-dir", default="./finops_reports", help="Output directory for exports")
666
+ @click.option("--include-quarterly", is_flag=True, help="Include quarterly intelligence data")
667
+ @click.option("--executive-summary", is_flag=True, help="Generate executive summary format")
668
+ @click.option("--mcp-validate", is_flag=True, help="Enable MCP validation for ≥99.5% accuracy cross-validation")
669
+ @click.pass_context
670
+ def export(ctx, export_format, output_dir, include_quarterly, executive_summary, mcp_validate):
671
+ """
672
+ Export financial analysis results in multiple formats.
673
+
674
+ Enterprise Export Features:
675
+ • Multi-format simultaneous export
676
+ • Executive-ready formatting and presentation
677
+ • Quarterly intelligence integration
678
+ • Complete audit trail documentation
679
+
680
+ Examples:
681
+ runbooks finops export --format csv,pdf --executive-summary
682
+ runbooks finops export --include-quarterly --output-dir ./executive_reports
683
+ """
684
+ try:
685
+ from runbooks.finops.export_manager import FinOpsExportManager
686
+
687
+ # MCP validation integration for export accuracy
688
+ mcp_results = None
689
+ if mcp_validate:
690
+ try:
691
+ from runbooks.validation.mcp_validator import MCPValidator
692
+ import asyncio
693
+
694
+ console.print("[cyan]🔍 Running MCP validation for export data accuracy[/cyan]")
695
+
696
+ # Configure validation profiles
697
+ validation_profiles = {
698
+ "billing": ctx.obj.get("profile", "default"),
699
+ "management": ctx.obj.get("profile", "default"),
700
+ "centralised_ops": ctx.obj.get("profile", "default"),
701
+ "single_aws": ctx.obj.get("profile", "default"),
702
+ }
703
+
704
+ # Initialize validator
705
+ validator = MCPValidator(
706
+ profiles=validation_profiles, tolerance_percentage=5.0, performance_target_seconds=30.0
707
+ )
708
+
709
+ # Run validation for export data accuracy using cost explorer validation
710
+ mcp_results = asyncio.run(validator.validate_cost_explorer())
711
+
712
+ # Display validation results
713
+ if mcp_results.accuracy_percentage >= 99.5:
714
+ console.print(
715
+ f"[green]✅ MCP Validation PASSED: {mcp_results.accuracy_percentage:.1f}% accuracy for exports[/green]"
716
+ )
717
+ elif mcp_results.accuracy_percentage >= 95.0:
718
+ console.print(
719
+ f"[yellow]⚠️ MCP Validation WARNING: {mcp_results.accuracy_percentage:.1f}% accuracy (target: ≥99.5%)[/yellow]"
720
+ )
721
+ else:
722
+ console.print(
723
+ f"[red]❌ MCP Validation FAILED: {mcp_results.accuracy_percentage:.1f}% accuracy[/red]"
724
+ )
725
+
726
+ except Exception as e:
727
+ console.print(f"[yellow]⚠️ MCP validation failed: {e}[/yellow]")
728
+ console.print("[dim]Continuing with export operation...[/dim]")
729
+
730
+ export_manager = FinOpsExportManager(
731
+ profile=ctx.obj["profile"],
732
+ output_dir=output_dir,
733
+ include_quarterly=include_quarterly,
734
+ executive_summary=executive_summary,
735
+ mcp_validate=mcp_validate,
736
+ )
737
+
738
+ export_results = {}
739
+ for format_type in export_format:
740
+ result = export_manager.export_analysis(format=format_type)
741
+ export_results[format_type] = result
742
+
743
+ # Attach MCP validation results if available
744
+ if mcp_results:
745
+ export_results["mcp_validation"] = {
746
+ "accuracy_percentage": mcp_results.accuracy_percentage,
747
+ "validation_passed": mcp_results.accuracy_percentage >= 99.5,
748
+ "export_formats": list(export_format),
749
+ "operation_name": mcp_results.operation_name,
750
+ "status": mcp_results.status.value,
751
+ "detailed_results": mcp_results,
752
+ }
753
+
754
+ error_handlers["success"](
755
+ f"Successfully exported to {len(export_format)} format(s)", f"Output directory: {output_dir}"
756
+ )
757
+
758
+ return export_results
759
+
760
+ except ImportError as e:
761
+ error_handlers["module_not_available"]("FinOps export", e)
762
+ raise click.ClickException("FinOps export functionality not available")
763
+ except Exception as e:
764
+ error_handlers["operation_failed"]("FinOps export operation", e)
765
+ raise click.ClickException(str(e))
766
+
767
+ @finops.command()
768
+ @click.option(
769
+ "--older-than-days", type=int, default=90, help="Minimum age in days for cleanup consideration (default: 90)"
770
+ )
771
+ @click.option(
772
+ "--validate", is_flag=True, default=True, help="Enable MCP validation for ≥99.5% accuracy (default: enabled)"
773
+ )
774
+ @click.option("--cleanup", is_flag=True, help="Enable cleanup recommendations (READ-ONLY analysis only)")
775
+ @click.option("--export-results", is_flag=True, help="Export analysis results to JSON file")
776
+ @click.option(
777
+ "--safety-checks/--no-safety-checks",
778
+ default=True,
779
+ help="Enable comprehensive safety validations (default: enabled)",
780
+ )
781
+ @click.option("--all-profiles", help="Use specified profile for all operations (overrides parent --profile)")
782
+ @click.pass_context
783
+ def ec2_snapshots(ctx, older_than_days, validate, cleanup, export_results, safety_checks, all_profiles):
784
+ """
785
+ EC2 snapshot cost optimization and cleanup analysis.
786
+
787
+ Sprint 1, Task 1: Analyze EC2 snapshots for cost optimization opportunities
788
+ targeting $50K+ annual savings through systematic age-based cleanup with
789
+ enterprise safety validations and MCP accuracy frameworks.
790
+
791
+ Enterprise Features:
792
+ • Multi-account snapshot discovery via AWS Config aggregator
793
+ • Dynamic pricing via AWS Pricing API for accurate cost calculations
794
+ • MCP validation framework with ≥99.5% accuracy cross-validation
795
+ • Comprehensive safety checks (volume attachment, AMI association, age)
796
+ • Executive reporting with Sprint 1 business impact metrics
797
+
798
+ Safety Features:
799
+ • READ-ONLY analysis by default (no actual cleanup performed)
800
+ • Volume attachment verification before recommendations
801
+ • AMI association checking to prevent data loss
802
+ • Configurable age thresholds with safety validations
803
+
804
+ Examples:
805
+ # Basic analysis with MCP validation using parent profile
806
+ runbooks finops --profile BILLING_PROFILE ec2-snapshots --validate
807
+
808
+ # Override parent profile with command-specific profile
809
+ runbooks finops ec2-snapshots --all-profiles BILLING_PROFILE --validate
810
+
811
+ # Custom age threshold with export
812
+ runbooks finops --profile BILLING_PROFILE ec2-snapshots --older-than-days 120 --export-results
813
+
814
+ # Comprehensive analysis for Sprint 1
815
+ runbooks finops --profile BILLING_PROFILE ec2-snapshots --cleanup --validate --export-results
816
+
817
+ # Quick analysis without safety checks (not recommended)
818
+ runbooks finops ec2-snapshots --all-profiles BILLING_PROFILE --no-safety-checks --older-than-days 30
819
+
820
+ Sprint 1 Context:
821
+ Task 1 targeting $50K+ annual savings through systematic snapshot cleanup
822
+ with enterprise coordination and MCP validation accuracy ≥99.5%
823
+ """
824
+ try:
825
+ import asyncio
826
+ from runbooks.finops.snapshot_manager import EC2SnapshotManager
827
+
828
+ console.print("\n[bold blue]🎯 Sprint 1, Task 1: EC2 Snapshot Cost Optimization[/bold blue]")
829
+
830
+ # Resolve profile with priority: --all-profiles > ctx.obj['profile'] > 'default'
831
+ resolved_profile = all_profiles or ctx.obj.get("profile", "default")
832
+ resolved_region = ctx.obj.get("region", "all")
833
+ resolved_dry_run = ctx.obj.get("dry_run", True)
834
+
835
+ # Validate profile resolution
836
+ if not resolved_profile:
837
+ console.print("[red]❌ Error: No AWS profile specified or found[/red]")
838
+ console.print("[yellow]💡 Use --all-profiles PROFILE_NAME or set parent --profile option[/yellow]")
839
+ raise click.ClickException("AWS profile required for ec2-snapshots command")
840
+
841
+ console.print(
842
+ f"[dim]Profile: {resolved_profile} | Region: {resolved_region} | Dry-run: {resolved_dry_run}[/dim]\n"
843
+ )
844
+
845
+ # Initialize snapshot manager with enterprise configuration
846
+ manager = EC2SnapshotManager(profile=resolved_profile, dry_run=resolved_dry_run)
847
+
848
+ # Configure safety checks based on user preference
849
+ if not safety_checks:
850
+ console.print("[yellow]⚠️ Safety checks disabled - use with caution[/yellow]")
851
+ manager.safety_checks = {
852
+ "volume_attachment_check": False,
853
+ "ami_association_check": False,
854
+ "minimum_age_check": True, # Always keep age check for safety
855
+ "cross_account_validation": False,
856
+ }
857
+
858
+ # Run the main analysis using the enhanced method
859
+ async def run_analysis():
860
+ return await manager.analyze_snapshot_opportunities(
861
+ profile=resolved_profile,
862
+ older_than_days=older_than_days,
863
+ enable_mcp_validation=validate,
864
+ export_results=export_results,
865
+ )
866
+
867
+ # Execute analysis
868
+ results = asyncio.run(run_analysis())
869
+
870
+ # Sprint 1 success validation
871
+ annual_savings = results["cost_analysis"]["annual_savings"]
872
+ sprint_target = 50000 # $50K Sprint 1 target
873
+
874
+ if annual_savings >= sprint_target:
875
+ console.print(f"\n[bold green]✅ Sprint 1 Task 1 SUCCESS![/bold green]")
876
+ console.print(f"[green]Target: ${sprint_target:,} | Achieved: ${annual_savings:,.2f}[/green]")
877
+ else:
878
+ console.print(f"\n[bold yellow]⚠️ Sprint 1 Task 1 - Below Target[/bold yellow]")
879
+ console.print(f"[yellow]Target: ${sprint_target:,} | Achieved: ${annual_savings:,.2f}[/yellow]")
880
+
881
+ # MCP validation status for Sprint 1
882
+ if validate and results.get("mcp_validation"):
883
+ mcp_results = results["mcp_validation"]
884
+ if mcp_results["validation_passed"]:
885
+ console.print(
886
+ f"[green]✅ MCP Validation: {mcp_results['accuracy_percentage']:.2f}% accuracy[/green]"
887
+ )
888
+ else:
889
+ console.print(
890
+ f"[red]❌ MCP Validation: {mcp_results['accuracy_percentage']:.2f}% accuracy (Required: ≥99.5%)[/red]"
891
+ )
892
+
893
+ # Enterprise coordination confirmation
894
+ console.print(f"\n[dim]🏢 Enterprise coordination: python-runbooks-engineer [1] (Primary)[/dim]")
895
+ console.print(f"[dim]🎯 Sprint coordination: Systematic delegation activated[/dim]")
896
+
897
+ return results
898
+
899
+ except ImportError as e:
900
+ error_handlers["module_not_available"]("EC2 Snapshot Manager", e)
901
+ raise click.ClickException("EC2 snapshot analysis functionality not available")
902
+ except Exception as e:
903
+ error_handlers["operation_failed"]("EC2 snapshot analysis", e)
904
+ raise click.ClickException(str(e))
905
+
906
+ # Epic 2 Infrastructure Optimization Commands
907
+ @finops.group()
908
+ def infrastructure():
909
+ """Epic 2 Infrastructure Optimization - $210,147 annual savings target"""
910
+ pass
911
+
912
+ @infrastructure.command()
913
+ @click.option(
914
+ "--components",
915
+ multiple=True,
916
+ type=click.Choice(["nat-gateway", "elastic-ip", "load-balancer", "vpc-endpoint"]),
917
+ help="Infrastructure components to analyze (default: all)",
918
+ )
919
+ @click.option(
920
+ "--export-format",
921
+ type=click.Choice(["json", "csv", "markdown"]),
922
+ default="json",
923
+ help="Export format for results",
924
+ )
925
+ @click.option("--output-file", help="Output file path for results export")
926
+ @click.option("--mcp-validate", is_flag=True, help="Enable MCP validation for ≥99.5% accuracy cross-validation")
927
+ @click.pass_context
928
+ def analyze(ctx, components, export_format, output_file, mcp_validate):
929
+ """
930
+ Comprehensive Infrastructure Optimization Analysis - Epic 2
931
+
932
+ Analyze all infrastructure components to achieve $210,147 Epic 2 annual savings target:
933
+ • NAT Gateway optimization: $147,420 target
934
+ • Elastic IP optimization: $21,593 target
935
+ • Load Balancer optimization: $35,280 target
936
+ • VPC Endpoint optimization: $5,854 target
937
+
938
+ SAFETY: READ-ONLY analysis only - no resource modifications.
939
+
940
+ Examples:
941
+ runbooks finops infrastructure analyze
942
+ runbooks finops infrastructure analyze --components nat-gateway load-balancer
943
+ """
944
+ try:
945
+ import asyncio
946
+ from runbooks.finops.infrastructure.commands import InfrastructureOptimizer
947
+
948
+ # MCP validation integration for infrastructure analysis
949
+ mcp_results = None
950
+ if mcp_validate:
951
+ try:
952
+ from runbooks.validation.mcp_validator import MCPValidator
953
+
954
+ console.print("[cyan]🔍 Running MCP validation for infrastructure optimization accuracy[/cyan]")
955
+
956
+ # Configure validation profiles
957
+ validation_profiles = {
958
+ "billing": ctx.obj.get("profile", "default"),
959
+ "management": ctx.obj.get("profile", "default"),
960
+ "centralised_ops": ctx.obj.get("profile", "default"),
961
+ "single_aws": ctx.obj.get("profile", "default"),
962
+ }
963
+
964
+ # Initialize validator
965
+ validator = MCPValidator(
966
+ profiles=validation_profiles, tolerance_percentage=5.0, performance_target_seconds=30.0
967
+ )
968
+
969
+ # Run validation for infrastructure operations using VPC validation for networking components
970
+ component_types = (
971
+ list(components)
972
+ if components
973
+ else ["nat-gateway", "elastic-ip", "load-balancer", "vpc-endpoint"]
974
+ )
975
+ if any(comp in ["nat-gateway", "vpc-endpoint"] for comp in component_types):
976
+ mcp_results = asyncio.run(validator.validate_vpc_analysis())
977
+ elif any(comp in ["elastic-ip"] for comp in component_types):
978
+ mcp_results = asyncio.run(validator.validate_ec2_inventory())
979
+ else:
980
+ # Default to cost explorer for load balancer cost analysis
981
+ mcp_results = asyncio.run(validator.validate_cost_explorer())
982
+
983
+ # Display validation results
984
+ if mcp_results.accuracy_percentage >= 99.5:
985
+ console.print(
986
+ f"[green]✅ MCP Validation PASSED: {mcp_results.accuracy_percentage:.1f}% accuracy for infrastructure[/green]"
987
+ )
988
+ elif mcp_results.accuracy_percentage >= 95.0:
989
+ console.print(
990
+ f"[yellow]⚠️ MCP Validation WARNING: {mcp_results.accuracy_percentage:.1f}% accuracy (target: ≥99.5%)[/yellow]"
991
+ )
992
+ else:
993
+ console.print(
994
+ f"[red]❌ MCP Validation FAILED: {mcp_results.accuracy_percentage:.1f}% accuracy[/red]"
995
+ )
996
+
997
+ except Exception as e:
998
+ console.print(f"[yellow]⚠️ MCP validation failed: {e}[/yellow]")
999
+ console.print("[dim]Continuing with infrastructure analysis...[/dim]")
1000
+
1001
+ # Initialize comprehensive optimizer
1002
+ optimizer = InfrastructureOptimizer(
1003
+ profile_name=ctx.obj.get("profile"),
1004
+ regions=[ctx.obj.get("region")] if ctx.obj.get("region") else None,
1005
+ mcp_validate=mcp_validate,
1006
+ )
1007
+
1008
+ # Execute comprehensive analysis
1009
+ results = asyncio.run(
1010
+ optimizer.analyze_comprehensive_infrastructure(
1011
+ components=list(components) if components else None, dry_run=ctx.obj.get("dry_run", True)
1012
+ )
1013
+ )
1014
+
1015
+ # Attach MCP validation results if available
1016
+ if mcp_results and hasattr(results, "__dict__"):
1017
+ results.mcp_validation = {
1018
+ "accuracy_percentage": mcp_results.accuracy_percentage,
1019
+ "validation_passed": mcp_results.accuracy_percentage >= 99.5,
1020
+ "components_validated": list(components) if components else "all",
1021
+ "operation_name": mcp_results.operation_name,
1022
+ "status": mcp_results.status.value,
1023
+ "detailed_results": mcp_results,
1024
+ }
1025
+
1026
+ # Display Epic 2 progress
1027
+ if results.epic_2_target_achieved:
1028
+ console.print(f"\n[bold green]✅ Epic 2 Infrastructure Target Achieved![/bold green]")
1029
+ console.print(
1030
+ f"[green]Target: ${results.epic_2_target_savings:,.0f} | Achieved: ${results.total_potential_savings:,.0f}[/green]"
1031
+ )
1032
+ else:
1033
+ progress_pct = results.epic_2_progress_percentage
1034
+ console.print(f"\n[bold yellow]📊 Epic 2 Infrastructure Progress: {progress_pct:.1f}%[/bold yellow]")
1035
+ console.print(
1036
+ f"[yellow]Target: ${results.epic_2_target_savings:,.0f} | Achieved: ${results.total_potential_savings:,.0f}[/yellow]"
1037
+ )
1038
+
1039
+ # Export results if requested
1040
+ if output_file or export_format != "json":
1041
+ console.print(f"[dim]Export functionality available - results ready for {export_format} export[/dim]")
1042
+
1043
+ return results
1044
+
1045
+ except ImportError as e:
1046
+ error_handlers["module_not_available"]("Infrastructure Optimizer", e)
1047
+ raise click.ClickException("Infrastructure optimization functionality not available")
1048
+ except Exception as e:
1049
+ error_handlers["operation_failed"]("Infrastructure optimization analysis", e)
1050
+ raise click.ClickException(str(e))
1051
+
1052
+ @infrastructure.command()
1053
+ @click.pass_context
1054
+ def nat_gateway(ctx):
1055
+ """NAT Gateway optimization analysis - $147,420 Epic 2 target"""
1056
+ try:
1057
+ import asyncio
1058
+ from runbooks.finops.nat_gateway_optimizer import NATGatewayOptimizer
1059
+
1060
+ optimizer = NATGatewayOptimizer(
1061
+ profile_name=ctx.obj.get("profile"), regions=[ctx.obj.get("region")] if ctx.obj.get("region") else None
1062
+ )
1063
+
1064
+ results = asyncio.run(optimizer.analyze_nat_gateways(dry_run=ctx.obj.get("dry_run", True)))
1065
+
1066
+ # Display Epic 2 component progress
1067
+ target = 147420.0
1068
+ if results.potential_annual_savings >= target:
1069
+ console.print(f"\n[bold green]✅ NAT Gateway Epic 2 Target Achieved![/bold green]")
1070
+ else:
1071
+ progress = (results.potential_annual_savings / target) * 100
1072
+ console.print(f"\n[yellow]📊 NAT Gateway Progress: {progress:.1f}% of Epic 2 target[/yellow]")
1073
+
1074
+ return results
1075
+
1076
+ except Exception as e:
1077
+ error_handlers["operation_failed"]("NAT Gateway optimization", e)
1078
+ raise click.ClickException(str(e))
1079
+
1080
+ @infrastructure.command()
1081
+ @click.pass_context
1082
+ def elastic_ip(ctx):
1083
+ """Elastic IP optimization analysis - $21,593 Epic 2 target"""
1084
+ try:
1085
+ import asyncio
1086
+ from runbooks.finops.elastic_ip_optimizer import ElasticIPOptimizer
1087
+
1088
+ optimizer = ElasticIPOptimizer(
1089
+ profile_name=ctx.obj.get("profile"), regions=[ctx.obj.get("region")] if ctx.obj.get("region") else None
1090
+ )
1091
+
1092
+ results = asyncio.run(optimizer.analyze_elastic_ips(dry_run=ctx.obj.get("dry_run", True)))
1093
+
1094
+ # Display Epic 2 component progress
1095
+ target = 21593.0
1096
+ if results.potential_annual_savings >= target:
1097
+ console.print(f"\n[bold green]✅ Elastic IP Epic 2 Target Achieved![/bold green]")
1098
+ else:
1099
+ progress = (results.potential_annual_savings / target) * 100
1100
+ console.print(f"\n[yellow]📊 Elastic IP Progress: {progress:.1f}% of Epic 2 target[/yellow]")
1101
+
1102
+ return results
1103
+
1104
+ except Exception as e:
1105
+ error_handlers["operation_failed"]("Elastic IP optimization", e)
1106
+ raise click.ClickException(str(e))
1107
+
1108
+ @infrastructure.command()
1109
+ @click.pass_context
1110
+ def load_balancer(ctx):
1111
+ """Load Balancer optimization analysis - $35,280 Epic 2 target"""
1112
+ try:
1113
+ import asyncio
1114
+ from runbooks.finops.infrastructure.load_balancer_optimizer import LoadBalancerOptimizer
1115
+
1116
+ optimizer = LoadBalancerOptimizer(
1117
+ profile_name=ctx.obj.get("profile"), regions=[ctx.obj.get("region")] if ctx.obj.get("region") else None
1118
+ )
1119
+
1120
+ results = asyncio.run(optimizer.analyze_load_balancers(dry_run=ctx.obj.get("dry_run", True)))
1121
+
1122
+ # Display Epic 2 component progress
1123
+ target = 35280.0
1124
+ if results.potential_annual_savings >= target:
1125
+ console.print(f"\n[bold green]✅ Load Balancer Epic 2 Target Achieved![/bold green]")
1126
+ else:
1127
+ progress = (results.potential_annual_savings / target) * 100
1128
+ console.print(f"\n[yellow]📊 Load Balancer Progress: {progress:.1f}% of Epic 2 target[/yellow]")
1129
+
1130
+ return results
1131
+
1132
+ except Exception as e:
1133
+ error_handlers["operation_failed"]("Load Balancer optimization", e)
1134
+ raise click.ClickException(str(e))
1135
+
1136
+ @infrastructure.command()
1137
+ @click.pass_context
1138
+ def vpc_endpoint(ctx):
1139
+ """VPC Endpoint optimization analysis - $5,854 Epic 2 target"""
1140
+ try:
1141
+ import asyncio
1142
+ from runbooks.finops.infrastructure.vpc_endpoint_optimizer import VPCEndpointOptimizer
1143
+
1144
+ optimizer = VPCEndpointOptimizer(
1145
+ profile_name=ctx.obj.get("profile"), regions=[ctx.obj.get("region")] if ctx.obj.get("region") else None
1146
+ )
1147
+
1148
+ results = asyncio.run(optimizer.analyze_vpc_endpoints(dry_run=ctx.obj.get("dry_run", True)))
1149
+
1150
+ # Display Epic 2 component progress
1151
+ target = 5854.0
1152
+ if results.potential_annual_savings >= target:
1153
+ console.print(f"\n[bold green]✅ VPC Endpoint Epic 2 Target Achieved![/bold green]")
1154
+ else:
1155
+ progress = (results.potential_annual_savings / target) * 100
1156
+ console.print(f"\n[yellow]📊 VPC Endpoint Progress: {progress:.1f}% of Epic 2 target[/yellow]")
1157
+
1158
+ return results
1159
+
1160
+ except Exception as e:
1161
+ error_handlers["operation_failed"]("VPC Endpoint optimization", e)
1162
+ raise click.ClickException(str(e))
1163
+
1164
+ return finops