runbooks 0.7.6__py3-none-any.whl → 0.7.9__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.
- runbooks/__init__.py +1 -1
- runbooks/base.py +5 -1
- runbooks/cfat/__init__.py +8 -4
- runbooks/cfat/assessment/collectors.py +171 -14
- runbooks/cfat/assessment/compliance.py +871 -0
- runbooks/cfat/assessment/runner.py +122 -11
- runbooks/cfat/models.py +6 -2
- runbooks/common/logger.py +14 -0
- runbooks/common/rich_utils.py +451 -0
- runbooks/enterprise/__init__.py +68 -0
- runbooks/enterprise/error_handling.py +411 -0
- runbooks/enterprise/logging.py +439 -0
- runbooks/enterprise/multi_tenant.py +583 -0
- runbooks/finops/README.md +468 -241
- runbooks/finops/__init__.py +39 -3
- runbooks/finops/cli.py +83 -18
- runbooks/finops/cross_validation.py +375 -0
- runbooks/finops/dashboard_runner.py +812 -164
- runbooks/finops/enhanced_dashboard_runner.py +525 -0
- runbooks/finops/finops_dashboard.py +1892 -0
- runbooks/finops/helpers.py +485 -51
- runbooks/finops/optimizer.py +823 -0
- runbooks/finops/tests/__init__.py +19 -0
- runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
- runbooks/finops/tests/run_comprehensive_tests.py +421 -0
- runbooks/finops/tests/run_tests.py +305 -0
- runbooks/finops/tests/test_finops_dashboard.py +705 -0
- runbooks/finops/tests/test_integration.py +477 -0
- runbooks/finops/tests/test_performance.py +380 -0
- runbooks/finops/tests/test_performance_benchmarks.py +500 -0
- runbooks/finops/tests/test_reference_images_validation.py +867 -0
- runbooks/finops/tests/test_single_account_features.py +715 -0
- runbooks/finops/tests/validate_test_suite.py +220 -0
- runbooks/finops/types.py +1 -1
- runbooks/hitl/enhanced_workflow_engine.py +725 -0
- runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
- runbooks/inventory/collectors/aws_comprehensive.py +442 -0
- runbooks/inventory/collectors/enterprise_scale.py +281 -0
- runbooks/inventory/core/collector.py +172 -13
- runbooks/inventory/discovery.md +1 -1
- runbooks/inventory/list_ec2_instances.py +18 -20
- runbooks/inventory/list_ssm_parameters.py +31 -3
- runbooks/inventory/organizations_discovery.py +1269 -0
- runbooks/inventory/rich_inventory_display.py +393 -0
- runbooks/inventory/run_on_multi_accounts.py +35 -19
- runbooks/inventory/runbooks.security.report_generator.log +0 -0
- runbooks/inventory/runbooks.security.run_script.log +0 -0
- runbooks/inventory/vpc_flow_analyzer.py +1030 -0
- runbooks/main.py +2215 -119
- runbooks/metrics/dora_metrics_engine.py +599 -0
- runbooks/operate/__init__.py +2 -2
- runbooks/operate/base.py +122 -10
- runbooks/operate/deployment_framework.py +1032 -0
- runbooks/operate/deployment_validator.py +853 -0
- runbooks/operate/dynamodb_operations.py +10 -6
- runbooks/operate/ec2_operations.py +319 -11
- runbooks/operate/executive_dashboard.py +779 -0
- runbooks/operate/mcp_integration.py +750 -0
- runbooks/operate/nat_gateway_operations.py +1120 -0
- runbooks/operate/networking_cost_heatmap.py +685 -0
- runbooks/operate/privatelink_operations.py +940 -0
- runbooks/operate/s3_operations.py +10 -6
- runbooks/operate/vpc_endpoints.py +644 -0
- runbooks/operate/vpc_operations.py +1038 -0
- runbooks/remediation/__init__.py +2 -2
- runbooks/remediation/acm_remediation.py +1 -1
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/cloudtrail_remediation.py +1 -1
- runbooks/remediation/cognito_remediation.py +1 -1
- runbooks/remediation/dynamodb_remediation.py +1 -1
- runbooks/remediation/ec2_remediation.py +1 -1
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
- runbooks/remediation/kms_enable_key_rotation.py +1 -1
- runbooks/remediation/kms_remediation.py +1 -1
- runbooks/remediation/lambda_remediation.py +1 -1
- runbooks/remediation/multi_account.py +1 -1
- runbooks/remediation/rds_remediation.py +1 -1
- runbooks/remediation/s3_block_public_access.py +1 -1
- runbooks/remediation/s3_enable_access_logging.py +1 -1
- runbooks/remediation/s3_encryption.py +1 -1
- runbooks/remediation/s3_remediation.py +1 -1
- runbooks/remediation/vpc_remediation.py +475 -0
- runbooks/security/__init__.py +3 -1
- runbooks/security/compliance_automation.py +632 -0
- runbooks/security/report_generator.py +10 -0
- runbooks/security/run_script.py +31 -5
- runbooks/security/security_baseline_tester.py +169 -30
- runbooks/security/security_export.py +477 -0
- runbooks/validation/__init__.py +10 -0
- runbooks/validation/benchmark.py +484 -0
- runbooks/validation/cli.py +356 -0
- runbooks/validation/mcp_validator.py +768 -0
- runbooks/vpc/__init__.py +38 -0
- runbooks/vpc/config.py +212 -0
- runbooks/vpc/cost_engine.py +347 -0
- runbooks/vpc/heatmap_engine.py +605 -0
- runbooks/vpc/manager_interface.py +634 -0
- runbooks/vpc/networking_wrapper.py +1260 -0
- runbooks/vpc/rich_formatters.py +679 -0
- runbooks/vpc/tests/__init__.py +5 -0
- runbooks/vpc/tests/conftest.py +356 -0
- runbooks/vpc/tests/test_cli_integration.py +530 -0
- runbooks/vpc/tests/test_config.py +458 -0
- runbooks/vpc/tests/test_cost_engine.py +479 -0
- runbooks/vpc/tests/test_networking_wrapper.py +512 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
runbooks/__init__.py
CHANGED
@@ -67,7 +67,7 @@ try:
|
|
67
67
|
__version__ = _pkg_version("runbooks")
|
68
68
|
except Exception:
|
69
69
|
# Fallback if metadata is unavailable during editable installs
|
70
|
-
__version__ = "0.7.
|
70
|
+
__version__ = "0.7.8"
|
71
71
|
|
72
72
|
# Core module exports
|
73
73
|
from runbooks.config import RunbooksConfig, load_config, save_config
|
runbooks/base.py
CHANGED
@@ -5,6 +5,7 @@ This module provides common base classes and utilities used across
|
|
5
5
|
all Cloud Foundations components including CFAT, inventory, and organizations.
|
6
6
|
"""
|
7
7
|
|
8
|
+
import os
|
8
9
|
from abc import ABC, abstractmethod
|
9
10
|
from datetime import datetime
|
10
11
|
from pathlib import Path
|
@@ -86,7 +87,10 @@ class CloudFoundationsBase(ABC):
|
|
86
87
|
|
87
88
|
def _create_session(self) -> boto3.Session:
|
88
89
|
"""Create boto3 session with appropriate configuration."""
|
89
|
-
|
90
|
+
# Use environment variable first, then profile parameter, then default
|
91
|
+
profile = os.environ.get("AWS_PROFILE") or self.profile
|
92
|
+
|
93
|
+
session_kwargs = {"profile_name": profile}
|
90
94
|
if self.region:
|
91
95
|
session_kwargs["region_name"] = self.region
|
92
96
|
|
runbooks/cfat/__init__.py
CHANGED
@@ -30,11 +30,15 @@ Example:
|
|
30
30
|
report.to_html("assessment_report.html")
|
31
31
|
report.to_json("findings.json")
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
# Rich console output for better formatting
|
34
|
+
from rich.console import Console
|
35
|
+
console = Console()
|
36
|
+
|
37
|
+
console.print(f"[green]Compliance Score: {report.summary.compliance_score}/100[/green]")
|
38
|
+
console.print(f"[red]Critical Issues: {report.summary.critical_issues}[/red]")
|
35
39
|
```
|
36
40
|
|
37
|
-
Version: 0.7.
|
41
|
+
Version: 0.7.8 (Latest with enhanced CLI integration, rust tooling, and modern dependency stack)
|
38
42
|
"""
|
39
43
|
|
40
44
|
# Core assessment engine
|
@@ -53,7 +57,7 @@ from runbooks.cfat.models import (
|
|
53
57
|
from runbooks.cfat.runner import AssessmentRunner
|
54
58
|
|
55
59
|
# Version info
|
56
|
-
__version__ = "0.7.
|
60
|
+
__version__ = "0.7.8"
|
57
61
|
__author__ = "CloudOps Runbooks Team"
|
58
62
|
|
59
63
|
# Public API exports
|
@@ -66,7 +66,7 @@ class IAMCollector(BaseCollector):
|
|
66
66
|
|
67
67
|
|
68
68
|
class VPCCollector(BaseCollector):
|
69
|
-
"""Virtual Private Cloud resource collector."""
|
69
|
+
"""Virtual Private Cloud resource collector with NAT Gateway cost optimization integration."""
|
70
70
|
|
71
71
|
def get_service_name(self) -> str:
|
72
72
|
"""Get service name."""
|
@@ -74,23 +74,180 @@ class VPCCollector(BaseCollector):
|
|
74
74
|
|
75
75
|
def collect(self) -> Dict[str, Any]:
|
76
76
|
"""
|
77
|
-
Collect VPC resources for assessment.
|
77
|
+
Collect VPC resources for assessment with NAT Gateway cost analysis.
|
78
78
|
|
79
79
|
Returns:
|
80
|
-
Dictionary containing VPC resource data
|
80
|
+
Dictionary containing VPC resource data including cost optimization insights
|
81
81
|
"""
|
82
|
-
logger.info("Collecting VPC resources...")
|
82
|
+
logger.info("Collecting VPC resources with cost optimization analysis...")
|
83
|
+
|
84
|
+
try:
|
85
|
+
ec2_client = self.session.client("ec2", region_name=self.region)
|
86
|
+
|
87
|
+
# Collect VPCs
|
88
|
+
vpcs_response = ec2_client.describe_vpcs()
|
89
|
+
vpcs = vpcs_response.get("Vpcs", [])
|
90
|
+
|
91
|
+
# Collect Subnets
|
92
|
+
subnets_response = ec2_client.describe_subnets()
|
93
|
+
subnets = subnets_response.get("Subnets", [])
|
94
|
+
|
95
|
+
# Collect NAT Gateways with cost analysis (GitHub Issue #96)
|
96
|
+
nat_gateways_response = ec2_client.describe_nat_gateways()
|
97
|
+
nat_gateways = nat_gateways_response.get("NatGateways", [])
|
98
|
+
|
99
|
+
# Calculate NAT Gateway costs ($45/month per gateway)
|
100
|
+
active_nat_gateways = [ng for ng in nat_gateways if ng.get("State") == "available"]
|
101
|
+
nat_cost_analysis = {
|
102
|
+
"total_nat_gateways": len(active_nat_gateways),
|
103
|
+
"estimated_monthly_cost": len(active_nat_gateways) * 45.0,
|
104
|
+
"optimization_opportunities": self._analyze_nat_optimization(active_nat_gateways, subnets),
|
105
|
+
"cost_alerts": [],
|
106
|
+
}
|
107
|
+
|
108
|
+
if len(active_nat_gateways) > 3:
|
109
|
+
nat_cost_analysis["cost_alerts"].append(
|
110
|
+
f"HIGH COST: {len(active_nat_gateways)} NAT Gateways detected. "
|
111
|
+
f"Monthly cost: ${nat_cost_analysis['estimated_monthly_cost']:,.2f}"
|
112
|
+
)
|
113
|
+
|
114
|
+
# Collect Security Groups
|
115
|
+
sg_response = ec2_client.describe_security_groups()
|
116
|
+
security_groups = sg_response.get("SecurityGroups", [])
|
117
|
+
|
118
|
+
# Collect Network ACLs
|
119
|
+
nacls_response = ec2_client.describe_network_acls()
|
120
|
+
nacls = nacls_response.get("NetworkAcls", [])
|
121
|
+
|
122
|
+
# Collect Internet Gateways
|
123
|
+
igw_response = ec2_client.describe_internet_gateways()
|
124
|
+
internet_gateways = igw_response.get("InternetGateways", [])
|
125
|
+
|
126
|
+
# Collect VPC Flow Logs
|
127
|
+
flow_logs_response = ec2_client.describe_flow_logs()
|
128
|
+
flow_logs = flow_logs_response.get("FlowLogs", [])
|
129
|
+
|
130
|
+
# Collect Route Tables for routing analysis
|
131
|
+
route_tables_response = ec2_client.describe_route_tables()
|
132
|
+
route_tables = route_tables_response.get("RouteTables", [])
|
133
|
+
|
134
|
+
logger.info(
|
135
|
+
f"Collected {len(vpcs)} VPCs, {len(nat_gateways)} NAT Gateways, "
|
136
|
+
f"estimated monthly NAT cost: ${nat_cost_analysis['estimated_monthly_cost']:,.2f}"
|
137
|
+
)
|
138
|
+
|
139
|
+
return {
|
140
|
+
"vpcs": vpcs,
|
141
|
+
"subnets": subnets,
|
142
|
+
"nat_gateways": nat_gateways,
|
143
|
+
"nat_cost_analysis": nat_cost_analysis, # New: Cost optimization data
|
144
|
+
"security_groups": security_groups,
|
145
|
+
"nacls": nacls,
|
146
|
+
"flow_logs": flow_logs,
|
147
|
+
"internet_gateways": internet_gateways,
|
148
|
+
"route_tables": route_tables,
|
149
|
+
"assessment_metadata": {
|
150
|
+
"collector_version": "v0.7.8-vpc-enhanced",
|
151
|
+
"github_issue": "#96",
|
152
|
+
"cost_optimization_enabled": True,
|
153
|
+
},
|
154
|
+
}
|
155
|
+
|
156
|
+
except Exception as e:
|
157
|
+
logger.error(f"Failed to collect VPC resources: {e}")
|
158
|
+
return {
|
159
|
+
"vpcs": [],
|
160
|
+
"subnets": [],
|
161
|
+
"nat_gateways": [],
|
162
|
+
"nat_cost_analysis": {"error": str(e)},
|
163
|
+
"security_groups": [],
|
164
|
+
"nacls": [],
|
165
|
+
"flow_logs": [],
|
166
|
+
"internet_gateways": [],
|
167
|
+
"route_tables": [],
|
168
|
+
"assessment_metadata": {"collector_version": "v0.7.8-vpc-enhanced", "error": str(e)},
|
169
|
+
}
|
170
|
+
|
171
|
+
def _analyze_nat_optimization(self, nat_gateways: List[Dict], subnets: List[Dict]) -> int:
|
172
|
+
"""
|
173
|
+
Analyze NAT Gateway placement for cost optimization opportunities.
|
83
174
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
175
|
+
Args:
|
176
|
+
nat_gateways: List of NAT Gateway configurations
|
177
|
+
subnets: List of subnet configurations
|
178
|
+
|
179
|
+
Returns:
|
180
|
+
Number of optimization opportunities found
|
181
|
+
"""
|
182
|
+
opportunities = 0
|
183
|
+
|
184
|
+
# Group NAT Gateways by Availability Zone
|
185
|
+
az_nat_count = {}
|
186
|
+
for nat in nat_gateways:
|
187
|
+
if nat.get("State") == "available":
|
188
|
+
subnet_id = nat.get("SubnetId")
|
189
|
+
# Find AZ for this subnet
|
190
|
+
subnet_az = None
|
191
|
+
for subnet in subnets:
|
192
|
+
if subnet.get("SubnetId") == subnet_id:
|
193
|
+
subnet_az = subnet.get("AvailabilityZone")
|
194
|
+
break
|
195
|
+
|
196
|
+
if subnet_az:
|
197
|
+
az_nat_count[subnet_az] = az_nat_count.get(subnet_az, 0) + 1
|
198
|
+
|
199
|
+
# Check for potential consolidation opportunities
|
200
|
+
for az, count in az_nat_count.items():
|
201
|
+
if count > 1:
|
202
|
+
opportunities += count - 1 # Could potentially consolidate to 1 per AZ
|
203
|
+
|
204
|
+
return opportunities
|
205
|
+
|
206
|
+
def run(self) -> "CloudFoundationsResult":
|
207
|
+
"""
|
208
|
+
Run VPC resource collection and return standardized result.
|
209
|
+
|
210
|
+
Returns:
|
211
|
+
CloudFoundationsResult with VPC assessment data including NAT Gateway cost analysis
|
212
|
+
"""
|
213
|
+
try:
|
214
|
+
# Collect VPC resources with cost optimization analysis
|
215
|
+
vpc_data = self.collect()
|
216
|
+
|
217
|
+
# Determine success based on data collection
|
218
|
+
success = bool(vpc_data) and not vpc_data.get("assessment_metadata", {}).get("error")
|
219
|
+
|
220
|
+
# Create message with cost insights
|
221
|
+
nat_cost_analysis = vpc_data.get("nat_cost_analysis", {})
|
222
|
+
total_cost = nat_cost_analysis.get("estimated_monthly_cost", 0)
|
223
|
+
total_nats = nat_cost_analysis.get("total_nat_gateways", 0)
|
224
|
+
|
225
|
+
if success:
|
226
|
+
message = (
|
227
|
+
f"VPC assessment completed: {len(vpc_data.get('vpcs', []))} VPCs, "
|
228
|
+
f"{total_nats} NAT Gateways, estimated monthly NAT cost: ${total_cost:,.2f}"
|
229
|
+
)
|
230
|
+
|
231
|
+
# Add cost alerts to message if present
|
232
|
+
cost_alerts = nat_cost_analysis.get("cost_alerts", [])
|
233
|
+
if cost_alerts:
|
234
|
+
message += f". {len(cost_alerts)} cost optimization opportunities identified"
|
235
|
+
else:
|
236
|
+
error = vpc_data.get("assessment_metadata", {}).get("error", "Unknown error")
|
237
|
+
message = f"VPC assessment failed: {error}"
|
238
|
+
|
239
|
+
return self.create_result(
|
240
|
+
success=success,
|
241
|
+
message=message,
|
242
|
+
data=vpc_data,
|
243
|
+
errors=[vpc_data.get("assessment_metadata", {}).get("error")] if not success else [],
|
244
|
+
)
|
245
|
+
|
246
|
+
except Exception as e:
|
247
|
+
logger.error(f"VPC collector run failed: {e}")
|
248
|
+
return self.create_result(
|
249
|
+
success=False, message=f"VPC assessment failed: {str(e)}", data={}, errors=[str(e)]
|
250
|
+
)
|
94
251
|
|
95
252
|
|
96
253
|
class CloudTrailCollector(BaseCollector):
|