runbooks 0.7.0__py3-none-any.whl → 0.7.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 (100) hide show
  1. runbooks/__init__.py +87 -37
  2. runbooks/cfat/README.md +300 -49
  3. runbooks/cfat/__init__.py +2 -2
  4. runbooks/finops/__init__.py +1 -1
  5. runbooks/finops/cli.py +1 -1
  6. runbooks/inventory/collectors/__init__.py +8 -0
  7. runbooks/inventory/collectors/aws_management.py +791 -0
  8. runbooks/inventory/collectors/aws_networking.py +3 -3
  9. runbooks/main.py +3389 -782
  10. runbooks/operate/__init__.py +207 -0
  11. runbooks/operate/base.py +311 -0
  12. runbooks/operate/cloudformation_operations.py +619 -0
  13. runbooks/operate/cloudwatch_operations.py +496 -0
  14. runbooks/operate/dynamodb_operations.py +812 -0
  15. runbooks/operate/ec2_operations.py +926 -0
  16. runbooks/operate/iam_operations.py +569 -0
  17. runbooks/operate/s3_operations.py +1211 -0
  18. runbooks/operate/tagging_operations.py +655 -0
  19. runbooks/remediation/CLAUDE.md +100 -0
  20. runbooks/remediation/DOME9.md +218 -0
  21. runbooks/remediation/README.md +26 -0
  22. runbooks/remediation/Tests/__init__.py +0 -0
  23. runbooks/remediation/Tests/update_policy.py +74 -0
  24. runbooks/remediation/__init__.py +95 -0
  25. runbooks/remediation/acm_cert_expired_unused.py +98 -0
  26. runbooks/remediation/acm_remediation.py +875 -0
  27. runbooks/remediation/api_gateway_list.py +167 -0
  28. runbooks/remediation/base.py +643 -0
  29. runbooks/remediation/cloudtrail_remediation.py +908 -0
  30. runbooks/remediation/cloudtrail_s3_modifications.py +296 -0
  31. runbooks/remediation/cognito_active_users.py +78 -0
  32. runbooks/remediation/cognito_remediation.py +856 -0
  33. runbooks/remediation/cognito_user_password_reset.py +163 -0
  34. runbooks/remediation/commons.py +455 -0
  35. runbooks/remediation/dynamodb_optimize.py +155 -0
  36. runbooks/remediation/dynamodb_remediation.py +744 -0
  37. runbooks/remediation/dynamodb_server_side_encryption.py +108 -0
  38. runbooks/remediation/ec2_public_ips.py +134 -0
  39. runbooks/remediation/ec2_remediation.py +892 -0
  40. runbooks/remediation/ec2_subnet_disable_auto_ip_assignment.py +72 -0
  41. runbooks/remediation/ec2_unattached_ebs_volumes.py +448 -0
  42. runbooks/remediation/ec2_unused_security_groups.py +202 -0
  43. runbooks/remediation/kms_enable_key_rotation.py +651 -0
  44. runbooks/remediation/kms_remediation.py +717 -0
  45. runbooks/remediation/lambda_list.py +243 -0
  46. runbooks/remediation/lambda_remediation.py +971 -0
  47. runbooks/remediation/multi_account.py +569 -0
  48. runbooks/remediation/rds_instance_list.py +199 -0
  49. runbooks/remediation/rds_remediation.py +873 -0
  50. runbooks/remediation/rds_snapshot_list.py +192 -0
  51. runbooks/remediation/requirements.txt +118 -0
  52. runbooks/remediation/s3_block_public_access.py +159 -0
  53. runbooks/remediation/s3_bucket_public_access.py +143 -0
  54. runbooks/remediation/s3_disable_static_website_hosting.py +74 -0
  55. runbooks/remediation/s3_downloader.py +215 -0
  56. runbooks/remediation/s3_enable_access_logging.py +562 -0
  57. runbooks/remediation/s3_encryption.py +526 -0
  58. runbooks/remediation/s3_force_ssl_secure_policy.py +143 -0
  59. runbooks/remediation/s3_list.py +141 -0
  60. runbooks/remediation/s3_object_search.py +201 -0
  61. runbooks/remediation/s3_remediation.py +816 -0
  62. runbooks/remediation/scan_for_phrase.py +425 -0
  63. runbooks/remediation/workspaces_list.py +220 -0
  64. runbooks/security/__init__.py +9 -10
  65. runbooks/security/security_baseline_tester.py +4 -2
  66. runbooks-0.7.5.dist-info/METADATA +606 -0
  67. {runbooks-0.7.0.dist-info → runbooks-0.7.5.dist-info}/RECORD +72 -44
  68. {runbooks-0.7.0.dist-info → runbooks-0.7.5.dist-info}/entry_points.txt +0 -1
  69. runbooks/aws/__init__.py +0 -58
  70. runbooks/aws/dynamodb_operations.py +0 -231
  71. runbooks/aws/ec2_copy_image_cross-region.py +0 -195
  72. runbooks/aws/ec2_describe_instances.py +0 -202
  73. runbooks/aws/ec2_ebs_snapshots_delete.py +0 -186
  74. runbooks/aws/ec2_run_instances.py +0 -213
  75. runbooks/aws/ec2_start_stop_instances.py +0 -212
  76. runbooks/aws/ec2_terminate_instances.py +0 -143
  77. runbooks/aws/ec2_unused_eips.py +0 -196
  78. runbooks/aws/ec2_unused_volumes.py +0 -188
  79. runbooks/aws/s3_create_bucket.py +0 -142
  80. runbooks/aws/s3_list_buckets.py +0 -152
  81. runbooks/aws/s3_list_objects.py +0 -156
  82. runbooks/aws/s3_object_operations.py +0 -183
  83. runbooks/aws/tagging_lambda_handler.py +0 -183
  84. runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +0 -619
  85. runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +0 -738
  86. runbooks/inventory/cfn_move_stack_instances.py +0 -1526
  87. runbooks/inventory/delete_s3_buckets_objects.py +0 -169
  88. runbooks/inventory/lockdown_cfn_stackset_role.py +0 -224
  89. runbooks/inventory/update_aws_actions.py +0 -173
  90. runbooks/inventory/update_cfn_stacksets.py +0 -1215
  91. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +0 -294
  92. runbooks/inventory/update_iam_roles_cross_accounts.py +0 -478
  93. runbooks/inventory/update_s3_public_access_block.py +0 -539
  94. runbooks/organizations/__init__.py +0 -12
  95. runbooks/organizations/manager.py +0 -374
  96. runbooks-0.7.0.dist-info/METADATA +0 -375
  97. /runbooks/{aws → operate}/tags.json +0 -0
  98. {runbooks-0.7.0.dist-info → runbooks-0.7.5.dist-info}/WHEEL +0 -0
  99. {runbooks-0.7.0.dist-info → runbooks-0.7.5.dist-info}/licenses/LICENSE +0 -0
  100. {runbooks-0.7.0.dist-info → runbooks-0.7.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,155 @@
1
+ """
2
+ DynamoDB Cost Optimization - Analyze and optimize table settings for cost savings.
3
+ """
4
+
5
+ import logging
6
+ from datetime import datetime, timedelta
7
+ from functools import lru_cache
8
+
9
+ import click
10
+ from botocore.exceptions import ClientError
11
+
12
+ from .commons import display_aws_account_info, get_client, list_tables, write_to_csv
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @lru_cache(maxsize=32)
18
+ def get_table_details(table_name):
19
+ """Get DynamoDB table details with error handling."""
20
+ try:
21
+ dynamodb = get_client("dynamodb")
22
+ return dynamodb.describe_table(TableName=table_name)["Table"]
23
+ except ClientError as e:
24
+ logger.error(f"Failed to get table details for {table_name}: {e}")
25
+ raise
26
+
27
+
28
+ def update_table_billing_mode(table_name, billing_mode):
29
+ """Update DynamoDB table billing mode."""
30
+ try:
31
+ dynamodb = get_client("dynamodb")
32
+ dynamodb.update_table(TableName=table_name, BillingMode=billing_mode)
33
+ logger.info(f"Updated {table_name} to {billing_mode} billing mode")
34
+ except ClientError as e:
35
+ logger.error(f"Failed to update {table_name} billing mode: {e}")
36
+ raise
37
+
38
+
39
+ @lru_cache(maxsize=32)
40
+ def analyze_table(table_name):
41
+ """Analyze table usage patterns and suggest cost optimizations."""
42
+ try:
43
+ table_details = get_table_details(table_name)
44
+
45
+ # Get basic table info
46
+ billing_mode_summary = table_details.get("BillingModeSummary", {})
47
+ billing_mode = billing_mode_summary.get("BillingMode", "PROVISIONED")
48
+ table_size_bytes = table_details.get("TableSizeBytes", 0)
49
+ table_item_count = table_details.get("ItemCount", 0)
50
+
51
+ # Get CloudWatch metrics for last 7 days
52
+ end_time = datetime.utcnow()
53
+ start_time = end_time - timedelta(days=7)
54
+
55
+ try:
56
+ cloudwatch = get_client("cloudwatch")
57
+ metrics = cloudwatch.get_metric_statistics(
58
+ Namespace="AWS/DynamoDB",
59
+ MetricName="ConsumedReadCapacityUnits",
60
+ Dimensions=[{"Name": "TableName", "Value": table_name}],
61
+ StartTime=start_time,
62
+ EndTime=end_time,
63
+ Period=86400, # Daily resolution
64
+ Statistics=["Average"],
65
+ )
66
+
67
+ # Calculate average RCU usage
68
+ datapoints = metrics.get("Datapoints", [])
69
+ average_rcu = sum(dp["Average"] for dp in datapoints) / len(datapoints) if datapoints else 0
70
+
71
+ except ClientError as e:
72
+ logger.warning(f"Could not get CloudWatch metrics for {table_name}: {e}")
73
+ average_rcu = 0
74
+
75
+ # Get provisioned capacity if applicable
76
+ provisioned_rcu = None
77
+ if billing_mode == "PROVISIONED" and "ProvisionedThroughput" in table_details:
78
+ provisioned_rcu = table_details["ProvisionedThroughput"].get("ReadCapacityUnits", 0)
79
+
80
+ # Build analysis result
81
+ analysis = {
82
+ "Table": table_name,
83
+ "Billing Mode": billing_mode,
84
+ "Table Size": f"{table_size_bytes / (1024**3):.2f} GB" if table_size_bytes else "0 GB",
85
+ "Item Count": table_item_count,
86
+ "Average RCU": round(average_rcu, 2),
87
+ "Provisioned RCU": provisioned_rcu,
88
+ "Recommendations": [],
89
+ }
90
+
91
+ # Generate recommendations
92
+ if billing_mode == "PROVISIONED" and provisioned_rcu and average_rcu < 0.9 * provisioned_rcu:
93
+ analysis["Recommendations"].append("Consider lowering provisioned RCU to match actual usage")
94
+
95
+ if average_rcu > 1000:
96
+ analysis["Recommendations"].append("Consider enabling DynamoDB Accelerator (DAX) for high read workloads")
97
+
98
+ # Storage optimization for large, infrequently accessed tables
99
+ if table_size_bytes > 10 * (1024**3) and table_item_count > 0 and average_rcu / table_item_count < 0.1:
100
+ table_class = table_details.get("TableClassSummary", {}).get("TableClass", "STANDARD")
101
+ if table_class != "STANDARD_INFREQUENT_ACCESS":
102
+ analysis["Recommendations"].append("Consider STANDARD_INFREQUENT_ACCESS for storage cost savings")
103
+
104
+ return analysis
105
+
106
+ except Exception as e:
107
+ logger.error(f"Failed to analyze table {table_name}: {e}")
108
+ return {
109
+ "Table": table_name,
110
+ "Error": str(e),
111
+ "Recommendations": ["Unable to analyze - check table permissions"],
112
+ }
113
+
114
+
115
+ @click.command()
116
+ @click.option("--apply", "-a", is_flag=True, help="Apply suggested optimizations")
117
+ @click.option("--output", "-o", default="/tmp/dynamodb_analysis.csv", help="Output CSV file path")
118
+ def dynamodb_analyze(apply=False, output="/tmp/dynamodb_analysis.csv"):
119
+ """Analyze DynamoDB tables and suggest cost optimizations."""
120
+ logger.info(f"Analyzing DynamoDB tables in {display_aws_account_info()}")
121
+
122
+ try:
123
+ table_names = list_tables()
124
+ if not table_names:
125
+ logger.info("No DynamoDB tables found")
126
+ return
127
+
128
+ logger.info(f"Found {len(table_names)} tables to analyze")
129
+
130
+ # Analyze each table
131
+ results = []
132
+ for table_name in table_names:
133
+ logger.info(f"Analyzing table: {table_name}")
134
+ result = analyze_table(table_name)
135
+ results.append(result)
136
+
137
+ # Log recommendations
138
+ if result.get("Recommendations"):
139
+ for rec in result["Recommendations"]:
140
+ logger.info(f" Recommendation: {rec}")
141
+ else:
142
+ logger.info(" No optimization recommendations")
143
+
144
+ # Save results to CSV
145
+ write_to_csv(results, output)
146
+ logger.info(f"Analysis results saved to: {output}")
147
+
148
+ # Apply optimizations if requested
149
+ if apply:
150
+ logger.info("Apply functionality not yet implemented")
151
+ # TODO: Implement safe optimization application
152
+
153
+ except Exception as e:
154
+ logger.error(f"Failed to analyze DynamoDB tables: {e}")
155
+ raise