runbooks 0.7.9__py3-none-any.whl → 0.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/README.md +12 -1
  3. runbooks/cfat/__init__.py +1 -1
  4. runbooks/cfat/assessment/compliance.py +4 -1
  5. runbooks/cfat/assessment/runner.py +42 -34
  6. runbooks/cfat/models.py +1 -1
  7. runbooks/cloudops/__init__.py +123 -0
  8. runbooks/cloudops/base.py +385 -0
  9. runbooks/cloudops/cost_optimizer.py +811 -0
  10. runbooks/cloudops/infrastructure_optimizer.py +29 -0
  11. runbooks/cloudops/interfaces.py +828 -0
  12. runbooks/cloudops/lifecycle_manager.py +29 -0
  13. runbooks/cloudops/mcp_cost_validation.py +678 -0
  14. runbooks/cloudops/models.py +251 -0
  15. runbooks/cloudops/monitoring_automation.py +29 -0
  16. runbooks/cloudops/notebook_framework.py +676 -0
  17. runbooks/cloudops/security_enforcer.py +449 -0
  18. runbooks/common/__init__.py +152 -0
  19. runbooks/common/accuracy_validator.py +1039 -0
  20. runbooks/common/context_logger.py +440 -0
  21. runbooks/common/cross_module_integration.py +594 -0
  22. runbooks/common/enhanced_exception_handler.py +1108 -0
  23. runbooks/common/enterprise_audit_integration.py +634 -0
  24. runbooks/common/mcp_cost_explorer_integration.py +900 -0
  25. runbooks/common/mcp_integration.py +548 -0
  26. runbooks/common/performance_monitor.py +387 -0
  27. runbooks/common/profile_utils.py +216 -0
  28. runbooks/common/rich_utils.py +172 -1
  29. runbooks/feedback/user_feedback_collector.py +440 -0
  30. runbooks/finops/README.md +377 -458
  31. runbooks/finops/__init__.py +4 -21
  32. runbooks/finops/account_resolver.py +279 -0
  33. runbooks/finops/accuracy_cross_validator.py +638 -0
  34. runbooks/finops/aws_client.py +721 -36
  35. runbooks/finops/budget_integration.py +313 -0
  36. runbooks/finops/cli.py +59 -5
  37. runbooks/finops/cost_optimizer.py +1340 -0
  38. runbooks/finops/cost_processor.py +211 -37
  39. runbooks/finops/dashboard_router.py +900 -0
  40. runbooks/finops/dashboard_runner.py +990 -232
  41. runbooks/finops/embedded_mcp_validator.py +288 -0
  42. runbooks/finops/enhanced_dashboard_runner.py +8 -7
  43. runbooks/finops/enhanced_progress.py +327 -0
  44. runbooks/finops/enhanced_trend_visualization.py +423 -0
  45. runbooks/finops/finops_dashboard.py +184 -1829
  46. runbooks/finops/helpers.py +509 -196
  47. runbooks/finops/iam_guidance.py +400 -0
  48. runbooks/finops/markdown_exporter.py +466 -0
  49. runbooks/finops/multi_dashboard.py +1502 -0
  50. runbooks/finops/optimizer.py +15 -15
  51. runbooks/finops/profile_processor.py +2 -2
  52. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  53. runbooks/finops/runbooks.security.report_generator.log +0 -0
  54. runbooks/finops/runbooks.security.run_script.log +0 -0
  55. runbooks/finops/runbooks.security.security_export.log +0 -0
  56. runbooks/finops/schemas.py +589 -0
  57. runbooks/finops/service_mapping.py +195 -0
  58. runbooks/finops/single_dashboard.py +710 -0
  59. runbooks/finops/tests/test_reference_images_validation.py +1 -1
  60. runbooks/inventory/README.md +12 -1
  61. runbooks/inventory/core/collector.py +157 -29
  62. runbooks/inventory/list_ec2_instances.py +9 -6
  63. runbooks/inventory/list_ssm_parameters.py +10 -10
  64. runbooks/inventory/organizations_discovery.py +210 -164
  65. runbooks/inventory/rich_inventory_display.py +74 -107
  66. runbooks/inventory/run_on_multi_accounts.py +13 -13
  67. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  68. runbooks/inventory/runbooks.security.security_export.log +0 -0
  69. runbooks/main.py +1371 -240
  70. runbooks/metrics/dora_metrics_engine.py +711 -17
  71. runbooks/monitoring/performance_monitor.py +433 -0
  72. runbooks/operate/README.md +394 -0
  73. runbooks/operate/base.py +215 -47
  74. runbooks/operate/ec2_operations.py +435 -5
  75. runbooks/operate/iam_operations.py +598 -3
  76. runbooks/operate/privatelink_operations.py +1 -1
  77. runbooks/operate/rds_operations.py +508 -0
  78. runbooks/operate/s3_operations.py +508 -0
  79. runbooks/operate/vpc_endpoints.py +1 -1
  80. runbooks/remediation/README.md +489 -13
  81. runbooks/remediation/base.py +5 -3
  82. runbooks/remediation/commons.py +8 -4
  83. runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
  84. runbooks/security/README.md +12 -1
  85. runbooks/security/__init__.py +265 -33
  86. runbooks/security/cloudops_automation_security_validator.py +1164 -0
  87. runbooks/security/compliance_automation.py +12 -10
  88. runbooks/security/compliance_automation_engine.py +1021 -0
  89. runbooks/security/enterprise_security_framework.py +930 -0
  90. runbooks/security/enterprise_security_policies.json +293 -0
  91. runbooks/security/executive_security_dashboard.py +1247 -0
  92. runbooks/security/integration_test_enterprise_security.py +879 -0
  93. runbooks/security/module_security_integrator.py +641 -0
  94. runbooks/security/multi_account_security_controls.py +2254 -0
  95. runbooks/security/real_time_security_monitor.py +1196 -0
  96. runbooks/security/report_generator.py +1 -1
  97. runbooks/security/run_script.py +4 -8
  98. runbooks/security/security_baseline_tester.py +39 -52
  99. runbooks/security/security_export.py +99 -120
  100. runbooks/sre/README.md +472 -0
  101. runbooks/sre/__init__.py +33 -0
  102. runbooks/sre/mcp_reliability_engine.py +1049 -0
  103. runbooks/sre/performance_optimization_engine.py +1032 -0
  104. runbooks/sre/production_monitoring_framework.py +584 -0
  105. runbooks/sre/reliability_monitoring_framework.py +1011 -0
  106. runbooks/validation/__init__.py +2 -2
  107. runbooks/validation/benchmark.py +154 -149
  108. runbooks/validation/cli.py +159 -147
  109. runbooks/validation/mcp_validator.py +291 -248
  110. runbooks/vpc/README.md +478 -0
  111. runbooks/vpc/__init__.py +2 -2
  112. runbooks/vpc/manager_interface.py +366 -351
  113. runbooks/vpc/networking_wrapper.py +68 -36
  114. runbooks/vpc/rich_formatters.py +22 -8
  115. runbooks-0.9.1.dist-info/METADATA +308 -0
  116. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/RECORD +120 -59
  117. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/entry_points.txt +1 -1
  118. runbooks/finops/cross_validation.py +0 -375
  119. runbooks-0.7.9.dist-info/METADATA +0 -636
  120. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/WHEEL +0 -0
  121. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/licenses/LICENSE +0 -0
  122. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/top_level.txt +0 -0
@@ -39,18 +39,9 @@ from runbooks.finops.dashboard_runner import (
39
39
  run_dashboard,
40
40
  )
41
41
 
42
- # Enterprise FinOps Dashboard Components (NEW in v0.7.8)
43
- from runbooks.finops.finops_dashboard import (
44
- EnterpriseDiscovery,
45
- EnterpriseExecutiveDashboard,
46
- EnterpriseExportEngine,
47
- EnterpriseResourceAuditor,
48
- FinOpsConfig,
49
- MultiAccountCostTrendAnalyzer,
50
- ResourceUtilizationHeatmapAnalyzer,
51
- create_finops_dashboard,
52
- run_complete_finops_analysis,
53
- )
42
+ # Enterprise FinOps Dashboard Components - Using existing dashboard_runner.py
43
+ # Backward compatibility module for legacy tests and components
44
+ from runbooks.finops.finops_dashboard import FinOpsConfig
54
45
  from runbooks.finops.helpers import (
55
46
  export_audit_report_to_csv,
56
47
  export_audit_report_to_json,
@@ -76,16 +67,8 @@ __all__ = [
76
67
  "_run_cost_trend_analysis",
77
68
  "_run_resource_heatmap_analysis",
78
69
  "_run_executive_dashboard",
79
- # NEW v0.7.8: Enterprise Dashboard Classes
70
+ # Enterprise Dashboard Classes - backward compatibility
80
71
  "FinOpsConfig",
81
- "EnterpriseDiscovery",
82
- "MultiAccountCostTrendAnalyzer",
83
- "ResourceUtilizationHeatmapAnalyzer",
84
- "EnterpriseResourceAuditor",
85
- "EnterpriseExecutiveDashboard",
86
- "EnterpriseExportEngine",
87
- "create_finops_dashboard",
88
- "run_complete_finops_analysis",
89
72
  # Processors
90
73
  "get_cost_data",
91
74
  "get_trend",
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ AWS Account Name Resolution Module for CloudOps Runbooks FinOps
4
+
5
+ This module provides account ID to account name mapping functionality using
6
+ the AWS Organizations API for readable account display in FinOps dashboards.
7
+
8
+ Features:
9
+ - Organizations API integration for account discovery
10
+ - Fallback to account ID when Organizations access unavailable
11
+ - Caching for performance optimization
12
+ - Profile-aware session management
13
+ - Error handling with graceful degradation
14
+
15
+ Author: CloudOps Runbooks Team
16
+ Version: 0.7.8
17
+ """
18
+
19
+ import functools
20
+ import time
21
+ from typing import Dict, Optional
22
+
23
+ import boto3
24
+ from botocore.exceptions import ClientError, NoCredentialsError
25
+
26
+ from runbooks.common.rich_utils import console, print_error, print_info, print_warning
27
+
28
+
29
+ class AccountResolver:
30
+ """AWS account name resolution using Organizations API."""
31
+
32
+ def __init__(self, management_profile: Optional[str] = None):
33
+ """
34
+ Initialize account resolver with management profile.
35
+
36
+ Args:
37
+ management_profile: AWS profile with Organizations read access
38
+ """
39
+ self.management_profile = management_profile
40
+ self._account_cache: Dict[str, str] = {}
41
+ self._cache_timestamp = 0
42
+ self._cache_ttl = 300 # 5 minutes cache TTL
43
+
44
+ @functools.lru_cache(maxsize=1)
45
+ def _get_organizations_client(self) -> Optional[boto3.client]:
46
+ """
47
+ Get Organizations client with error handling.
48
+
49
+ Returns:
50
+ Boto3 Organizations client or None if unavailable
51
+ """
52
+ try:
53
+ if self.management_profile:
54
+ session = boto3.Session(profile_name=self.management_profile)
55
+ else:
56
+ session = boto3.Session()
57
+
58
+ # Use us-east-1 for Organizations API (global service)
59
+ client = session.client("organizations", region_name="us-east-1")
60
+
61
+ # Test connectivity with a simple API call
62
+ client.describe_organization()
63
+ return client
64
+
65
+ except ClientError as e:
66
+ error_code = e.response.get("Error", {}).get("Code", "")
67
+ if error_code == "AccessDeniedException":
68
+ print_warning(f"Organizations access denied for profile '{self.management_profile}'")
69
+ elif error_code == "AWSOrganizationsNotInUseException":
70
+ print_info("AWS Organizations not enabled for this account")
71
+ else:
72
+ print_warning(f"Organizations API error: {error_code}")
73
+ return None
74
+
75
+ except NoCredentialsError:
76
+ print_warning("AWS credentials not available for Organizations API")
77
+ return None
78
+
79
+ except Exception as e:
80
+ print_warning(f"Unexpected error accessing Organizations API: {e}")
81
+ return None
82
+
83
+ def _refresh_account_cache(self) -> bool:
84
+ """
85
+ Refresh account cache from Organizations API.
86
+
87
+ Returns:
88
+ True if cache was refreshed successfully
89
+ """
90
+ current_time = time.time()
91
+
92
+ # Check if cache is still valid
93
+ if self._account_cache and (current_time - self._cache_timestamp) < self._cache_ttl:
94
+ return True
95
+
96
+ client = self._get_organizations_client()
97
+ if not client:
98
+ return False
99
+
100
+ try:
101
+ print_info("Refreshing account names from Organizations API...")
102
+
103
+ # List all accounts in the organization
104
+ paginator = client.get_paginator("list_accounts")
105
+ accounts = {}
106
+
107
+ for page in paginator.paginate():
108
+ for account in page.get("Accounts", []):
109
+ account_id = account.get("Id", "")
110
+ account_name = account.get("Name", account_id)
111
+
112
+ # Clean up account name for display
113
+ display_name = self._clean_account_name(account_name)
114
+ accounts[account_id] = display_name
115
+
116
+ # Update cache
117
+ self._account_cache = accounts
118
+ self._cache_timestamp = current_time
119
+
120
+ print_info(f"✅ Loaded {len(accounts)} account names from Organizations API")
121
+ return True
122
+
123
+ except ClientError as e:
124
+ error_code = e.response.get("Error", {}).get("Code", "")
125
+ print_warning(f"Failed to refresh account cache: {error_code}")
126
+ return False
127
+
128
+ except Exception as e:
129
+ print_error(f"Unexpected error refreshing account cache: {e}")
130
+ return False
131
+
132
+ def _clean_account_name(self, account_name: str, max_length: int = 40) -> str:
133
+ """
134
+ Clean up account name for display with intelligent truncation.
135
+
136
+ Args:
137
+ account_name: Raw account name from Organizations API
138
+ max_length: Maximum display length (default 40 for better readability)
139
+
140
+ Returns:
141
+ Cleaned account name suitable for enterprise display
142
+ """
143
+ if not account_name:
144
+ return "Unknown"
145
+
146
+ # Remove common prefixes and suffixes for cleaner display
147
+ cleaned = account_name
148
+
149
+ # Remove common organizational prefixes (be more selective)
150
+ prefixes_to_remove = ["ams-", "aws-", "org-", "account-"]
151
+ for prefix in prefixes_to_remove:
152
+ if cleaned.lower().startswith(prefix):
153
+ cleaned = cleaned[len(prefix) :]
154
+ break
155
+
156
+ # Remove common suffixes (be more selective to preserve meaningful info)
157
+ suffixes_to_remove = ["-account"] # Only remove truly redundant suffixes
158
+ for suffix in suffixes_to_remove:
159
+ if cleaned.lower().endswith(suffix):
160
+ cleaned = cleaned[: -len(suffix)]
161
+ break
162
+
163
+ # Capitalize first letter of each word for professional presentation
164
+ cleaned = " ".join(word.capitalize() for word in cleaned.replace("-", " ").replace("_", " ").split())
165
+
166
+ # Smart truncation - preserve meaningful parts
167
+ if len(cleaned) > max_length:
168
+ # Strategy 1: Try to truncate at word boundaries
169
+ words = cleaned.split()
170
+ if len(words) > 2:
171
+ # Keep first two meaningful words and truncate
172
+ truncated = f"{words[0]} {words[1]}..."
173
+ if len(truncated) <= max_length:
174
+ return truncated
175
+
176
+ # Strategy 2: Truncate with ellipsis, preserving more characters
177
+ cleaned = cleaned[: max_length - 3] + "..."
178
+
179
+ return cleaned or "Unknown"
180
+
181
+ def get_account_name(self, account_id: str, max_length: int = 40) -> str:
182
+ """
183
+ Get readable account name for account ID with configurable length.
184
+
185
+ Args:
186
+ account_id: AWS account ID
187
+ max_length: Maximum display length for the name
188
+
189
+ Returns:
190
+ Readable account name or account ID if resolution fails
191
+ """
192
+ if not account_id:
193
+ return "Unknown"
194
+
195
+ # Try to refresh cache if needed
196
+ self._refresh_account_cache()
197
+
198
+ # Get cached name or fallback to account ID
199
+ cached_name = self._account_cache.get(account_id, account_id)
200
+
201
+ # If we have a resolved name and it's different from account ID, apply length constraint
202
+ if cached_name != account_id:
203
+ return self._clean_account_name(cached_name, max_length)
204
+
205
+ return cached_name
206
+
207
+ def get_full_account_name(self, account_id: str) -> str:
208
+ """
209
+ Get full, untruncated account name for account ID.
210
+
211
+ Args:
212
+ account_id: AWS account ID
213
+
214
+ Returns:
215
+ Full readable account name or account ID if resolution fails
216
+ """
217
+ if not account_id:
218
+ return "Unknown"
219
+
220
+ # Try to refresh cache if needed
221
+ self._refresh_account_cache()
222
+
223
+ # Return raw cached name without truncation
224
+ return self._account_cache.get(account_id, account_id)
225
+
226
+ def get_account_names(self, account_ids: list) -> Dict[str, str]:
227
+ """
228
+ Get readable account names for multiple account IDs.
229
+
230
+ Args:
231
+ account_ids: List of AWS account IDs
232
+
233
+ Returns:
234
+ Dictionary mapping account IDs to readable names
235
+ """
236
+ if not account_ids:
237
+ return {}
238
+
239
+ # Refresh cache once for all accounts
240
+ self._refresh_account_cache()
241
+
242
+ # Build result dictionary
243
+ result = {}
244
+ for account_id in account_ids:
245
+ result[account_id] = self._account_cache.get(account_id, account_id)
246
+
247
+ return result
248
+
249
+
250
+ def get_account_resolver(management_profile: Optional[str] = None) -> AccountResolver:
251
+ """
252
+ Get account resolver instance with specified profile.
253
+
254
+ Args:
255
+ management_profile: AWS profile with Organizations read access
256
+
257
+ Returns:
258
+ AccountResolver instance
259
+ """
260
+ return AccountResolver(management_profile)
261
+
262
+
263
+ def resolve_account_name(account_id: str, management_profile: Optional[str] = None) -> str:
264
+ """
265
+ Convenience function to resolve single account name.
266
+
267
+ Args:
268
+ account_id: AWS account ID to resolve
269
+ management_profile: AWS profile with Organizations read access
270
+
271
+ Returns:
272
+ Readable account name or account ID if resolution fails
273
+ """
274
+ resolver = get_account_resolver(management_profile)
275
+ return resolver.get_account_name(account_id)
276
+
277
+
278
+ # Export public interface
279
+ __all__ = ["AccountResolver", "get_account_resolver", "resolve_account_name"]