runbooks 0.6.1__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 (142) hide show
  1. jupyter-agent/.env +2 -0
  2. jupyter-agent/.gradio/certificate.pem +31 -0
  3. jupyter-agent/__main__.log +8 -0
  4. jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
  5. jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
  6. jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
  7. jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
  8. jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
  9. jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
  10. runbooks/__init__.py +87 -37
  11. runbooks/cfat/README.md +300 -49
  12. runbooks/cfat/__init__.py +2 -2
  13. runbooks/finops/README.md +337 -0
  14. runbooks/finops/__init__.py +2 -4
  15. runbooks/finops/cli.py +1 -1
  16. runbooks/inventory/aws_organization.png +0 -0
  17. runbooks/inventory/collectors/__init__.py +8 -0
  18. runbooks/inventory/collectors/aws_management.py +791 -0
  19. runbooks/inventory/collectors/aws_networking.py +3 -3
  20. runbooks/main.py +3416 -590
  21. runbooks/operate/__init__.py +207 -0
  22. runbooks/operate/base.py +311 -0
  23. runbooks/operate/cloudformation_operations.py +619 -0
  24. runbooks/operate/cloudwatch_operations.py +496 -0
  25. runbooks/operate/dynamodb_operations.py +812 -0
  26. runbooks/operate/ec2_operations.py +926 -0
  27. runbooks/operate/iam_operations.py +569 -0
  28. runbooks/operate/s3_operations.py +1211 -0
  29. runbooks/operate/tagging_operations.py +655 -0
  30. runbooks/remediation/CLAUDE.md +100 -0
  31. runbooks/remediation/DOME9.md +218 -0
  32. runbooks/remediation/README.md +26 -0
  33. runbooks/remediation/Tests/update_policy.py +74 -0
  34. runbooks/remediation/__init__.py +95 -0
  35. runbooks/remediation/acm_cert_expired_unused.py +98 -0
  36. runbooks/remediation/acm_remediation.py +875 -0
  37. runbooks/remediation/api_gateway_list.py +167 -0
  38. runbooks/remediation/base.py +643 -0
  39. runbooks/remediation/cloudtrail_remediation.py +908 -0
  40. runbooks/remediation/cloudtrail_s3_modifications.py +296 -0
  41. runbooks/remediation/cognito_active_users.py +78 -0
  42. runbooks/remediation/cognito_remediation.py +856 -0
  43. runbooks/remediation/cognito_user_password_reset.py +163 -0
  44. runbooks/remediation/commons.py +455 -0
  45. runbooks/remediation/dynamodb_optimize.py +155 -0
  46. runbooks/remediation/dynamodb_remediation.py +744 -0
  47. runbooks/remediation/dynamodb_server_side_encryption.py +108 -0
  48. runbooks/remediation/ec2_public_ips.py +134 -0
  49. runbooks/remediation/ec2_remediation.py +892 -0
  50. runbooks/remediation/ec2_subnet_disable_auto_ip_assignment.py +72 -0
  51. runbooks/remediation/ec2_unattached_ebs_volumes.py +448 -0
  52. runbooks/remediation/ec2_unused_security_groups.py +202 -0
  53. runbooks/remediation/kms_enable_key_rotation.py +651 -0
  54. runbooks/remediation/kms_remediation.py +717 -0
  55. runbooks/remediation/lambda_list.py +243 -0
  56. runbooks/remediation/lambda_remediation.py +971 -0
  57. runbooks/remediation/multi_account.py +569 -0
  58. runbooks/remediation/rds_instance_list.py +199 -0
  59. runbooks/remediation/rds_remediation.py +873 -0
  60. runbooks/remediation/rds_snapshot_list.py +192 -0
  61. runbooks/remediation/requirements.txt +118 -0
  62. runbooks/remediation/s3_block_public_access.py +159 -0
  63. runbooks/remediation/s3_bucket_public_access.py +143 -0
  64. runbooks/remediation/s3_disable_static_website_hosting.py +74 -0
  65. runbooks/remediation/s3_downloader.py +215 -0
  66. runbooks/remediation/s3_enable_access_logging.py +562 -0
  67. runbooks/remediation/s3_encryption.py +526 -0
  68. runbooks/remediation/s3_force_ssl_secure_policy.py +143 -0
  69. runbooks/remediation/s3_list.py +141 -0
  70. runbooks/remediation/s3_object_search.py +201 -0
  71. runbooks/remediation/s3_remediation.py +816 -0
  72. runbooks/remediation/scan_for_phrase.py +425 -0
  73. runbooks/remediation/workspaces_list.py +220 -0
  74. runbooks/{security_baseline → security}/README.md +191 -68
  75. runbooks/security/__init__.py +70 -0
  76. runbooks/{security_baseline → security}/security_baseline_tester.py +5 -3
  77. runbooks-0.7.5.dist-info/METADATA +606 -0
  78. {runbooks-0.6.1.dist-info → runbooks-0.7.5.dist-info}/RECORD +115 -75
  79. {runbooks-0.6.1.dist-info → runbooks-0.7.5.dist-info}/entry_points.txt +0 -1
  80. runbooks/aws/__init__.py +0 -58
  81. runbooks/aws/dynamodb_operations.py +0 -231
  82. runbooks/aws/ec2_copy_image_cross-region.py +0 -195
  83. runbooks/aws/ec2_describe_instances.py +0 -202
  84. runbooks/aws/ec2_ebs_snapshots_delete.py +0 -186
  85. runbooks/aws/ec2_run_instances.py +0 -213
  86. runbooks/aws/ec2_start_stop_instances.py +0 -212
  87. runbooks/aws/ec2_terminate_instances.py +0 -143
  88. runbooks/aws/ec2_unused_eips.py +0 -196
  89. runbooks/aws/ec2_unused_volumes.py +0 -188
  90. runbooks/aws/s3_create_bucket.py +0 -142
  91. runbooks/aws/s3_list_buckets.py +0 -152
  92. runbooks/aws/s3_list_objects.py +0 -156
  93. runbooks/aws/s3_object_operations.py +0 -183
  94. runbooks/aws/tagging_lambda_handler.py +0 -183
  95. runbooks/inventory/cfn_move_stack_instances.py +0 -1526
  96. runbooks/inventory/delete_s3_buckets_objects.py +0 -169
  97. runbooks/inventory/lockdown_cfn_stackset_role.py +0 -224
  98. runbooks/inventory/update_aws_actions.py +0 -173
  99. runbooks/inventory/update_cfn_stacksets.py +0 -1215
  100. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +0 -294
  101. runbooks/inventory/update_iam_roles_cross_accounts.py +0 -478
  102. runbooks/inventory/update_s3_public_access_block.py +0 -539
  103. runbooks/organizations/__init__.py +0 -12
  104. runbooks/organizations/manager.py +0 -374
  105. runbooks/security_baseline/requirements.txt +0 -7
  106. runbooks-0.6.1.dist-info/METADATA +0 -373
  107. /runbooks/{aws → operate}/tags.json +0 -0
  108. /runbooks/{security_baseline → remediation/Tests}/__init__.py +0 -0
  109. /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
  110. /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
  111. /runbooks/{security_baseline → security}/checklist/alternate_contacts.py +0 -0
  112. /runbooks/{security_baseline → security}/checklist/bucket_public_access.py +0 -0
  113. /runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +0 -0
  114. /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
  115. /runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +0 -0
  116. /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
  117. /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
  118. /runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +0 -0
  119. /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
  120. /runbooks/{security_baseline → security}/checklist/root_access_key.py +0 -0
  121. /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
  122. /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
  123. /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
  124. /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
  125. /runbooks/{security_baseline → security}/config-origin.json +0 -0
  126. /runbooks/{security_baseline → security}/config.json +0 -0
  127. /runbooks/{security_baseline → security}/permission.json +0 -0
  128. /runbooks/{security_baseline → security}/report_generator.py +0 -0
  129. /runbooks/{security_baseline → security}/report_template_en.html +0 -0
  130. /runbooks/{security_baseline → security}/report_template_jp.html +0 -0
  131. /runbooks/{security_baseline → security}/report_template_kr.html +0 -0
  132. /runbooks/{security_baseline → security}/report_template_vn.html +0 -0
  133. /runbooks/{security_baseline → security}/run_script.py +0 -0
  134. /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
  135. /runbooks/{security_baseline → security}/utils/common.py +0 -0
  136. /runbooks/{security_baseline → security}/utils/enums.py +0 -0
  137. /runbooks/{security_baseline → security}/utils/language.py +0 -0
  138. /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
  139. /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
  140. {runbooks-0.6.1.dist-info → runbooks-0.7.5.dist-info}/WHEEL +0 -0
  141. {runbooks-0.6.1.dist-info → runbooks-0.7.5.dist-info}/licenses/LICENSE +0 -0
  142. {runbooks-0.6.1.dist-info → runbooks-0.7.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,199 @@
1
+ """
2
+ RDS Instance Cost Analysis - Analyze RDS instances for cost optimization opportunities.
3
+ """
4
+
5
+ import logging
6
+ from datetime import datetime, timedelta, timezone
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, write_to_csv
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @lru_cache(maxsize=32)
18
+ def get_rds_reserved_instances():
19
+ """Get RDS reserved instances with caching for performance."""
20
+ try:
21
+ rds_client = get_client("rds")
22
+ response = rds_client.describe_reserved_db_instances()
23
+ logger.debug(f"Found {len(response.get('ReservedDBInstances', []))} reserved instances")
24
+ return response
25
+ except ClientError as e:
26
+ logger.warning(f"Could not get reserved instances: {e}")
27
+ return {"ReservedDBInstances": []}
28
+
29
+
30
+ def get_cloudwatch_metrics(cloudwatch, instance_id, metric_name, days=7):
31
+ """Get CloudWatch metrics for RDS instance."""
32
+ try:
33
+ end_time = datetime.now(tz=timezone.utc)
34
+ start_time = end_time - timedelta(days=days)
35
+
36
+ response = cloudwatch.get_metric_statistics(
37
+ Namespace="AWS/RDS",
38
+ MetricName=metric_name,
39
+ Dimensions=[{"Name": "DBInstanceIdentifier", "Value": instance_id}],
40
+ StartTime=start_time,
41
+ EndTime=end_time,
42
+ Period=86400, # Daily average
43
+ Statistics=["Average", "Maximum"],
44
+ )
45
+
46
+ datapoints = response.get("Datapoints", [])
47
+ if datapoints:
48
+ avg_value = sum(dp["Average"] for dp in datapoints) / len(datapoints)
49
+ max_value = max(dp["Maximum"] for dp in datapoints)
50
+ return round(avg_value, 2), round(max_value, 2)
51
+ else:
52
+ logger.debug(f"No metrics data for {instance_id} - {metric_name}")
53
+ return 0, 0
54
+
55
+ except ClientError as e:
56
+ logger.warning(f"Could not get {metric_name} metrics for {instance_id}: {e}")
57
+ return 0, 0
58
+
59
+
60
+ @click.command()
61
+ @click.option("--output-file", default="/tmp/rds_cost_optimization.csv", help="Output CSV file path")
62
+ @click.option("--days", default=7, help="Number of days for CloudWatch metrics analysis")
63
+ @click.option("--include-storage", is_flag=True, help="Include detailed storage analysis")
64
+ def get_rds_list(output_file, days, include_storage):
65
+ """Analyze RDS instances for cost optimization and performance insights."""
66
+ logger.info(f"Analyzing RDS instances in {display_aws_account_info()}")
67
+
68
+ try:
69
+ rds = get_client("rds")
70
+ cloudwatch = get_client("cloudwatch")
71
+
72
+ # Get RDS instances
73
+ logger.info("Collecting RDS instance data...")
74
+ response = rds.describe_db_instances()
75
+ instances = response.get("DBInstances", [])
76
+
77
+ if not instances:
78
+ logger.info("No RDS instances found")
79
+ return
80
+
81
+ logger.info(f"Found {len(instances)} RDS instances to analyze")
82
+
83
+ # Get reserved instances for cost analysis
84
+ reserved_instances = get_rds_reserved_instances()
85
+ reserved_classes = {
86
+ ri["DBInstanceClass"]
87
+ for ri in reserved_instances.get("ReservedDBInstances", [])
88
+ if ri.get("State") == "active"
89
+ }
90
+
91
+ data = []
92
+ optimization_candidates = []
93
+
94
+ for i, instance in enumerate(instances, 1):
95
+ instance_id = instance["DBInstanceIdentifier"]
96
+ instance_class = instance["DBInstanceClass"]
97
+
98
+ logger.info(f"Analyzing instance {i}/{len(instances)}: {instance_id}")
99
+
100
+ # Basic instance information
101
+ instance_data = {
102
+ "InstanceIdentifier": instance_id,
103
+ "Engine": instance.get("Engine", "Unknown"),
104
+ "EngineVersion": instance.get("EngineVersion", "Unknown"),
105
+ "InstanceClass": instance_class,
106
+ "StorageType": instance.get("StorageType", "Unknown"),
107
+ "AllocatedStorage": instance.get("AllocatedStorage", 0),
108
+ "MultiAZ": instance.get("MultiAZ", False),
109
+ "DBInstanceStatus": instance.get("DBInstanceStatus", "Unknown"),
110
+ "AvailabilityZone": instance.get("AvailabilityZone", "Unknown"),
111
+ }
112
+
113
+ # Determine purchase type
114
+ purchase_type = "Reserved" if instance_class in reserved_classes else "On-Demand"
115
+ instance_data["PurchaseType"] = purchase_type
116
+
117
+ # Get CloudWatch metrics
118
+ logger.debug(f" Getting CloudWatch metrics for {instance_id}...")
119
+
120
+ # CPU Utilization
121
+ cpu_avg, cpu_max = get_cloudwatch_metrics(cloudwatch, instance_id, "CPUUtilization", days)
122
+ instance_data[f"CPUUtilization_Avg_{days}d"] = cpu_avg
123
+ instance_data[f"CPUUtilization_Max_{days}d"] = cpu_max
124
+
125
+ # Memory metrics (FreeableMemory)
126
+ mem_avg, mem_max = get_cloudwatch_metrics(cloudwatch, instance_id, "FreeableMemory", days)
127
+ # Convert bytes to GB
128
+ mem_avg_gb = round(mem_avg / (1024**3), 2) if mem_avg > 0 else 0
129
+ mem_max_gb = round(mem_max / (1024**3), 2) if mem_max > 0 else 0
130
+ instance_data[f"FreeMemory_Avg_GB_{days}d"] = mem_avg_gb
131
+ instance_data[f"FreeMemory_Max_GB_{days}d"] = mem_max_gb
132
+
133
+ # Storage analysis if requested
134
+ if include_storage:
135
+ try:
136
+ storage_response = rds.describe_db_instances(DBInstanceIdentifier=instance_id)
137
+ db_instance = storage_response["DBInstances"][0]
138
+ instance_data["StorageEncrypted"] = db_instance.get("StorageEncrypted", False)
139
+ instance_data["Iops"] = db_instance.get("Iops", "Standard")
140
+ except ClientError as e:
141
+ logger.debug(f"Could not get storage details for {instance_id}: {e}")
142
+
143
+ # Cost optimization analysis
144
+ optimization_notes = []
145
+
146
+ # Low CPU utilization suggests downsizing
147
+ if cpu_avg < 20:
148
+ optimization_notes.append(f"Low CPU utilization ({cpu_avg}%)")
149
+ optimization_candidates.append(instance_id)
150
+
151
+ # High memory availability suggests downsizing
152
+ if mem_avg_gb > 2: # More than 2GB free on average
153
+ optimization_notes.append(f"High free memory ({mem_avg_gb}GB avg)")
154
+
155
+ # Reserved instance recommendation
156
+ if purchase_type == "On-Demand" and cpu_avg > 10: # Active instance
157
+ optimization_notes.append("Consider Reserved Instance")
158
+
159
+ instance_data["OptimizationNotes"] = "; ".join(optimization_notes) if optimization_notes else "None"
160
+ instance_data["OptimizationCandidate"] = instance_id in optimization_candidates
161
+
162
+ data.append(instance_data)
163
+
164
+ # Log summary for this instance
165
+ logger.info(f" → {instance_class}, CPU: {cpu_avg}%, Free Mem: {mem_avg_gb}GB, {purchase_type}")
166
+
167
+ # Export results
168
+ write_to_csv(data, output_file)
169
+ logger.info(f"RDS analysis exported to: {output_file}")
170
+
171
+ # Summary report
172
+ logger.info("\n=== ANALYSIS SUMMARY ===")
173
+ logger.info(f"Total RDS instances: {len(instances)}")
174
+ logger.info(f"Reserved instances: {sum(1 for d in data if d['PurchaseType'] == 'Reserved')}")
175
+ logger.info(f"On-Demand instances: {sum(1 for d in data if d['PurchaseType'] == 'On-Demand')}")
176
+ logger.info(f"Optimization candidates: {len(optimization_candidates)}")
177
+
178
+ if optimization_candidates:
179
+ logger.warning(f"⚠ Instances with low utilization ({days}d avg):")
180
+ for instance_id in optimization_candidates:
181
+ instance_info = next(d for d in data if d["InstanceIdentifier"] == instance_id)
182
+ cpu_avg = instance_info[f"CPUUtilization_Avg_{days}d"]
183
+ logger.warning(f" - {instance_id}: {cpu_avg}% CPU")
184
+ else:
185
+ logger.info("✓ No obvious optimization candidates found")
186
+
187
+ # Engine distribution
188
+ engines = {}
189
+ for instance in data:
190
+ engine = instance["Engine"]
191
+ engines[engine] = engines.get(engine, 0) + 1
192
+
193
+ logger.info("Engine distribution:")
194
+ for engine, count in sorted(engines.items()):
195
+ logger.info(f" {engine}: {count} instances")
196
+
197
+ except Exception as e:
198
+ logger.error(f"Failed to analyze RDS instances: {e}")
199
+ raise