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
@@ -1,527 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Enhanced FinOps Dashboard Runner
4
-
5
- This module provides enterprise-grade FinOps dashboard capabilities including:
6
- - Multi-profile AWS cost analysis with Rich console formatting
7
- - Advanced audit reporting with PDF/CSV/JSON export
8
- - Resource utilization tracking and optimization recommendations
9
- - Budget monitoring and alerting integration
10
- - Trend analysis and forecasting capabilities
11
- """
12
-
13
- import argparse
14
- import csv
15
- import json
16
- from collections import defaultdict
17
- from datetime import datetime, timedelta
18
- from pathlib import Path
19
- from typing import Any, Dict, List, Optional, Tuple, Union
20
-
21
- import boto3
22
- from rich import box
23
- from rich.console import Console
24
- from rich.panel import Panel
25
- from rich.progress import Progress, SpinnerColumn, TextColumn, track
26
- from rich.status import Status
27
- from rich.table import Column, Table
28
- from rich.tree import Tree
29
-
30
- from ..common.rich_utils import get_console
31
-
32
- # Import FinOpsConfig for backward compatibility with tests
33
- from .finops_dashboard import FinOpsConfig
34
-
35
- console = Console()
36
-
37
-
38
- class EnhancedFinOpsDashboard:
39
- """Enhanced FinOps Dashboard with production-tested capabilities from runbooks finops"""
40
-
41
- def __init__(self, config: Optional[Dict[str, Any]] = None):
42
- self.config = config or {}
43
- self.console = Console()
44
- self.rich_console = self.console # Use the console instance directly
45
-
46
- # Export directory setup
47
- self.export_dir = Path("artifacts/finops-exports")
48
- self.export_dir.mkdir(parents=True, exist_ok=True)
49
-
50
- def get_aws_profiles(self) -> List[str]:
51
- """Get available AWS profiles from AWS CLI configuration"""
52
- try:
53
- import configparser
54
- import os
55
-
56
- aws_config_path = os.path.expanduser("~/.aws/config")
57
- aws_credentials_path = os.path.expanduser("~/.aws/credentials")
58
-
59
- profiles = set()
60
-
61
- # Parse AWS config file
62
- if os.path.exists(aws_config_path):
63
- config = configparser.ConfigParser()
64
- config.read(aws_config_path)
65
- for section in config.sections():
66
- if section.startswith("profile "):
67
- profiles.add(section.replace("profile ", ""))
68
- elif section == "default":
69
- profiles.add("default")
70
-
71
- # Parse AWS credentials file
72
- if os.path.exists(aws_credentials_path):
73
- credentials = configparser.ConfigParser()
74
- credentials.read(aws_credentials_path)
75
- profiles.update(credentials.sections())
76
-
77
- return sorted(list(profiles))
78
-
79
- except Exception as e:
80
- console.print(f"āš ļø Error reading AWS profiles: {e}", style="yellow")
81
- return []
82
-
83
- def get_account_info(self, profile: str) -> Dict[str, Any]:
84
- """Get AWS account information for a profile"""
85
- try:
86
- session = boto3.Session(profile_name=profile)
87
- sts = session.client("sts")
88
-
89
- identity = sts.get_caller_identity()
90
-
91
- return {
92
- "account_id": identity["Account"],
93
- "user_arn": identity["Arn"],
94
- "user_id": identity["UserId"],
95
- "profile": profile,
96
- "status": "active",
97
- }
98
-
99
- except Exception as e:
100
- return {"account_id": "N/A", "profile": profile, "status": "error", "error": str(e)}
101
-
102
- def get_resource_audit_data(self, profile: str, regions: Optional[List[str]] = None) -> Dict[str, Any]:
103
- """
104
- Get comprehensive resource audit data for a profile
105
-
106
- Enhanced with additional resource types and cost impact analysis
107
- """
108
- audit_data = {
109
- "profile": profile,
110
- "account_info": self.get_account_info(profile),
111
- "untagged_resources": 0,
112
- "stopped_instances": 0,
113
- "unused_volumes": 0,
114
- "unused_eips": 0,
115
- "budget_alerts": 0,
116
- "cost_optimization_opportunities": [],
117
- "regional_breakdown": {},
118
- "total_potential_savings": 0.0,
119
- }
120
-
121
- if audit_data["account_info"]["status"] == "error":
122
- return audit_data
123
-
124
- try:
125
- session = boto3.Session(profile_name=profile)
126
-
127
- # Default to common regions if none specified
128
- if not regions:
129
- regions = ["us-east-1", "us-west-2", "eu-west-1"]
130
-
131
- for region in regions:
132
- region_data = self._audit_region_resources(session, region)
133
- audit_data["regional_breakdown"][region] = region_data
134
-
135
- # Aggregate data
136
- audit_data["untagged_resources"] += region_data["untagged_resources"]
137
- audit_data["stopped_instances"] += region_data["stopped_instances"]
138
- audit_data["unused_volumes"] += region_data["unused_volumes"]
139
- audit_data["unused_eips"] += region_data["unused_eips"]
140
- audit_data["total_potential_savings"] += region_data["potential_savings"]
141
- audit_data["cost_optimization_opportunities"].extend(region_data["optimization_opportunities"])
142
-
143
- # Get budget information
144
- audit_data["budget_alerts"] = self._get_budget_alerts(session)
145
-
146
- except Exception as e:
147
- console.print(f"āš ļø Error auditing resources for {profile}: {e}", style="yellow")
148
-
149
- return audit_data
150
-
151
- def _audit_region_resources(self, session: boto3.Session, region: str) -> Dict[str, Any]:
152
- """Audit resources in a specific region"""
153
- region_data = {
154
- "region": region,
155
- "untagged_resources": 0,
156
- "stopped_instances": 0,
157
- "unused_volumes": 0,
158
- "unused_eips": 0,
159
- "potential_savings": 0.0,
160
- "optimization_opportunities": [],
161
- }
162
-
163
- try:
164
- ec2 = session.client("ec2", region_name=region)
165
-
166
- # Get stopped EC2 instances
167
- instances_response = ec2.describe_instances(
168
- Filters=[{"Name": "instance-state-name", "Values": ["stopped"]}]
169
- )
170
-
171
- stopped_instances = []
172
- for reservation in instances_response["Reservations"]:
173
- for instance in reservation["Instances"]:
174
- stopped_instances.append(
175
- {
176
- "instance_id": instance["InstanceId"],
177
- "instance_type": instance["InstanceType"],
178
- "launch_time": instance.get("LaunchTime"),
179
- "tags": instance.get("Tags", []),
180
- }
181
- )
182
-
183
- region_data["stopped_instances"] = len(stopped_instances)
184
-
185
- # Calculate potential savings from stopped instances (rough estimate)
186
- # Assume average $50/month per stopped instance in savings opportunity
187
- region_data["potential_savings"] += len(stopped_instances) * 50.0
188
-
189
- if stopped_instances:
190
- region_data["optimization_opportunities"].append(
191
- {
192
- "type": "stopped_instances",
193
- "count": len(stopped_instances),
194
- "description": f"{len(stopped_instances)} stopped EC2 instances - consider termination",
195
- "potential_savings": len(stopped_instances) * 50.0,
196
- "priority": "high",
197
- }
198
- )
199
-
200
- # Get unused EBS volumes
201
- volumes_response = ec2.describe_volumes(Filters=[{"Name": "status", "Values": ["available"]}])
202
-
203
- unused_volumes = volumes_response["Volumes"]
204
- region_data["unused_volumes"] = len(unused_volumes)
205
-
206
- # Note: EBS cost calculation requires real AWS Cost Explorer pricing data
207
- # Hardcoded pricing estimates removed per compliance requirements
208
- volume_savings = 0 # Cannot calculate without real AWS pricing API
209
- region_data["potential_savings"] += volume_savings
210
-
211
- if unused_volumes:
212
- region_data["optimization_opportunities"].append(
213
- {
214
- "type": "unused_volumes",
215
- "count": len(unused_volumes),
216
- "description": f"{len(unused_volumes)} unused EBS volumes",
217
- "potential_savings": volume_savings,
218
- "priority": "medium",
219
- }
220
- )
221
-
222
- # Get unused Elastic IPs
223
- eips_response = ec2.describe_addresses()
224
- unused_eips = [eip for eip in eips_response["Addresses"] if "InstanceId" not in eip]
225
- region_data["unused_eips"] = len(unused_eips)
226
-
227
- # Unused EIP cost: $3.65/month each
228
- eip_savings = len(unused_eips) * 3.65
229
- region_data["potential_savings"] += eip_savings
230
-
231
- if unused_eips:
232
- region_data["optimization_opportunities"].append(
233
- {
234
- "type": "unused_eips",
235
- "count": len(unused_eips),
236
- "description": f"{len(unused_eips)} unused Elastic IPs",
237
- "potential_savings": eip_savings,
238
- "priority": "high",
239
- }
240
- )
241
-
242
- # Count untagged resources (simplified check)
243
- untagged_count = 0
244
- for instance in stopped_instances:
245
- if not instance["tags"]:
246
- untagged_count += 1
247
- for volume in unused_volumes:
248
- if not volume.get("Tags"):
249
- untagged_count += 1
250
-
251
- region_data["untagged_resources"] = untagged_count
252
-
253
- except Exception as e:
254
- console.print(f"āš ļø Error auditing {region}: {e}", style="yellow")
255
-
256
- return region_data
257
-
258
- def _get_budget_alerts(self, session: boto3.Session) -> int:
259
- """Get budget alert count"""
260
- try:
261
- budgets = session.client("budgets")
262
-
263
- # Get account ID for budgets API
264
- sts = session.client("sts")
265
- account_id = sts.get_caller_identity()["Account"]
266
-
267
- response = budgets.describe_budgets(AccountId=account_id)
268
- return len(response.get("Budgets", []))
269
-
270
- except Exception:
271
- return 0 # Budgets API might not be accessible
272
-
273
- def generate_audit_report(
274
- self, profiles: Optional[List[str]] = None, regions: Optional[List[str]] = None
275
- ) -> Dict[str, Any]:
276
- """Generate comprehensive audit report for specified profiles"""
277
-
278
- if not profiles:
279
- profiles = self.get_aws_profiles()
280
- if not profiles:
281
- console.print("āŒ No AWS profiles found", style="red")
282
- return {}
283
-
284
- audit_results = {
285
- "report_metadata": {
286
- "generated_at": datetime.now().isoformat(),
287
- "profiles_analyzed": len(profiles),
288
- "regions_analyzed": len(regions) if regions else 3,
289
- "report_type": "comprehensive_audit",
290
- },
291
- "profile_data": {},
292
- "summary": {
293
- "total_untagged_resources": 0,
294
- "total_stopped_instances": 0,
295
- "total_unused_volumes": 0,
296
- "total_unused_eips": 0,
297
- "total_budget_alerts": 0,
298
- "total_potential_savings": 0.0,
299
- "optimization_opportunities": [],
300
- },
301
- }
302
-
303
- with Progress(
304
- SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console
305
- ) as progress:
306
- for profile in profiles:
307
- task = progress.add_task(f"Auditing profile {profile}...", total=None)
308
-
309
- profile_data = self.get_resource_audit_data(profile, regions)
310
- audit_results["profile_data"][profile] = profile_data
311
-
312
- # Aggregate summary data
313
- summary = audit_results["summary"]
314
- summary["total_untagged_resources"] += profile_data["untagged_resources"]
315
- summary["total_stopped_instances"] += profile_data["stopped_instances"]
316
- summary["total_unused_volumes"] += profile_data["unused_volumes"]
317
- summary["total_unused_eips"] += profile_data["unused_eips"]
318
- summary["total_budget_alerts"] += profile_data["budget_alerts"]
319
- summary["total_potential_savings"] += profile_data["total_potential_savings"]
320
- summary["optimization_opportunities"].extend(profile_data["cost_optimization_opportunities"])
321
-
322
- progress.remove_task(task)
323
-
324
- return audit_results
325
-
326
- def display_audit_report(self, audit_results: Dict[str, Any]):
327
- """Display audit report with enhanced Rich formatting"""
328
-
329
- summary = audit_results["summary"]
330
- profile_data = audit_results["profile_data"]
331
-
332
- # Report header
333
- header_panel = Panel.fit(
334
- f"[bold bright_cyan]šŸ¢ AWS FinOps Comprehensive Audit Report[/bold bright_cyan]\n\n"
335
- f"šŸ“Š Profiles Analyzed: [yellow]{len(profile_data)}[/yellow]\n"
336
- f"šŸ•’ Generated: [green]{audit_results['report_metadata']['generated_at'][:19]}[/green]\n"
337
- f"šŸ’° Total Savings Potential: [bold green]${summary['total_potential_savings']:.2f}/month[/bold green]",
338
- title="Audit Report",
339
- style="bright_cyan",
340
- )
341
- console.print(header_panel)
342
-
343
- # Summary table
344
- summary_table = Table(title="šŸ“ˆ Executive Summary", box=box.ASCII_DOUBLE_HEAD, style="bright_cyan")
345
- summary_table.add_column("Metric", style="cyan", width=25)
346
- summary_table.add_column("Count", style="yellow", width=10)
347
- summary_table.add_column("Impact", style="green", width=20)
348
-
349
- summary_table.add_row("Untagged Resources", str(summary["total_untagged_resources"]), "Compliance Risk")
350
- summary_table.add_row(
351
- "Stopped Instances",
352
- str(summary["total_stopped_instances"]),
353
- f"${summary['total_stopped_instances'] * 50:.0f}/month potential",
354
- )
355
- summary_table.add_row("Unused Volumes", str(summary["total_unused_volumes"]), "Storage waste")
356
- summary_table.add_row(
357
- "Unused EIPs", str(summary["total_unused_eips"]), f"${summary['total_unused_eips'] * 3.65:.0f}/month waste"
358
- )
359
- summary_table.add_row("Budget Alerts", str(summary["total_budget_alerts"]), "Monitoring coverage")
360
-
361
- console.print(summary_table)
362
-
363
- # Profile-specific table
364
- profile_table = Table(
365
- title="šŸ‘„ Profile-Specific Analysis", show_lines=True, box=box.ASCII_DOUBLE_HEAD, style="bright_cyan"
366
- )
367
-
368
- profile_table.add_column("Profile", justify="center", width=20)
369
- profile_table.add_column("Account ID", justify="center", width=15)
370
- profile_table.add_column("Untagged", width=10)
371
- profile_table.add_column("Stopped EC2", width=12)
372
- profile_table.add_column("Unused Vol", width=12)
373
- profile_table.add_column("Unused EIP", width=12)
374
- profile_table.add_column("Savings", width=12)
375
-
376
- for profile, data in profile_data.items():
377
- account_info = data["account_info"]
378
- profile_table.add_row(
379
- profile,
380
- account_info["account_id"] if account_info["status"] == "active" else "ERROR",
381
- str(data["untagged_resources"]),
382
- str(data["stopped_instances"]),
383
- str(data["unused_volumes"]),
384
- str(data["unused_eips"]),
385
- f"${data['total_potential_savings']:.0f}",
386
- )
387
-
388
- console.print(profile_table)
389
-
390
- # Top optimization opportunities
391
- if summary["optimization_opportunities"]:
392
- console.print("\n[bold blue]šŸŽÆ Top Optimization Opportunities[/bold blue]")
393
-
394
- # Sort by potential savings
395
- sorted_opportunities = sorted(
396
- summary["optimization_opportunities"], key=lambda x: x.get("potential_savings", 0), reverse=True
397
- )
398
-
399
- for i, opp in enumerate(sorted_opportunities[:10], 1): # Top 10
400
- priority_color = {"high": "red", "medium": "yellow", "low": "green"}
401
- color = priority_color.get(opp.get("priority", "low"), "white")
402
-
403
- console.print(
404
- f"{i:2d}. [bold {color}]{opp['description']}[/bold {color}] "
405
- f"([green]${opp.get('potential_savings', 0):.0f}/month[/green])"
406
- )
407
-
408
- def export_audit_report(
409
- self, audit_results: Dict[str, Any], formats: List[str] = ["json", "csv"]
410
- ) -> Dict[str, str]:
411
- """Export audit report in multiple formats"""
412
-
413
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
414
- export_files = {}
415
-
416
- # JSON export
417
- if "json" in formats:
418
- json_file = self.export_dir / f"finops_audit_report_{timestamp}.json"
419
- with open(json_file, "w") as f:
420
- json.dump(audit_results, f, indent=2, default=str)
421
- export_files["json"] = str(json_file)
422
-
423
- # CSV export
424
- if "csv" in formats:
425
- csv_file = self.export_dir / f"finops_audit_summary_{timestamp}.csv"
426
- with open(csv_file, "w", newline="") as f:
427
- writer = csv.writer(f)
428
-
429
- # Header
430
- writer.writerow(
431
- [
432
- "Profile",
433
- "Account_ID",
434
- "Untagged_Resources",
435
- "Stopped_Instances",
436
- "Unused_Volumes",
437
- "Unused_EIPs",
438
- "Budget_Alerts",
439
- "Potential_Savings_Monthly",
440
- ]
441
- )
442
-
443
- # Data rows
444
- for profile, data in audit_results["profile_data"].items():
445
- writer.writerow(
446
- [
447
- profile,
448
- data["account_info"]["account_id"],
449
- data["untagged_resources"],
450
- data["stopped_instances"],
451
- data["unused_volumes"],
452
- data["unused_eips"],
453
- data["budget_alerts"],
454
- f"${data['total_potential_savings']:.2f}",
455
- ]
456
- )
457
-
458
- export_files["csv"] = str(csv_file)
459
-
460
- return export_files
461
-
462
- def run_comprehensive_audit(
463
- self,
464
- profiles: Optional[List[str]] = None,
465
- regions: Optional[List[str]] = None,
466
- export_formats: List[str] = ["json", "csv"],
467
- display_report: bool = True,
468
- ) -> Dict[str, Any]:
469
- """Run comprehensive FinOps audit with reporting and export"""
470
-
471
- console.print("[bold bright_cyan]šŸš€ Starting Enhanced FinOps Audit...[/bold bright_cyan]")
472
-
473
- # Generate audit data
474
- audit_results = self.generate_audit_report(profiles, regions)
475
-
476
- if not audit_results:
477
- console.print("āŒ No audit data generated", style="red")
478
- return {}
479
-
480
- # Display report
481
- if display_report:
482
- self.display_audit_report(audit_results)
483
-
484
- # Export results
485
- if export_formats:
486
- console.print(f"\nšŸ“„ Exporting report in formats: {', '.join(export_formats)}")
487
- export_files = self.export_audit_report(audit_results, export_formats)
488
-
489
- console.print("āœ… Export completed:")
490
- for format_type, file_path in export_files.items():
491
- console.print(f" šŸ“ {format_type.upper()}: {file_path}")
492
-
493
- # Summary of potential savings
494
- total_savings = audit_results["summary"]["total_potential_savings"]
495
- if total_savings > 0:
496
- annual_savings = total_savings * 12
497
- console.print(f"\nšŸ’° [bold green]Total Optimization Potential:[/bold green]")
498
- console.print(f" Monthly: [yellow]${total_savings:.2f}[/yellow]")
499
- console.print(f" Annual: [green]${annual_savings:.2f}[/green]")
500
-
501
- return audit_results
502
-
503
-
504
- # CLI integration functions
505
- def enhanced_audit_cli(
506
- profiles: Optional[str] = None,
507
- regions: Optional[str] = None,
508
- export_formats: str = "json,csv",
509
- output_dir: Optional[str] = None,
510
- ) -> None:
511
- """CLI command for enhanced FinOps audit"""
512
-
513
- profile_list = profiles.split(",") if profiles else None
514
- region_list = regions.split(",") if regions else None
515
- format_list = export_formats.split(",") if export_formats else ["json"]
516
-
517
- dashboard = EnhancedFinOpsDashboard()
518
-
519
- if output_dir:
520
- dashboard.export_dir = Path(output_dir)
521
- dashboard.export_dir.mkdir(parents=True, exist_ok=True)
522
-
523
- audit_results = dashboard.run_comprehensive_audit(
524
- profiles=profile_list, regions=region_list, export_formats=format_list, display_report=True
525
- )
526
-
527
- return audit_results