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,202 @@
1
+ """
2
+ EC2 Security Group Cleanup - Identify and remove unused security groups safely.
3
+ """
4
+
5
+ import logging
6
+
7
+ import click
8
+ from botocore.exceptions import ClientError
9
+
10
+ from .commons import display_aws_account_info, get_client
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @click.command()
16
+ @click.option("--dry-run", is_flag=True, default=True, help="Preview mode - show actions without making changes")
17
+ @click.option("--include-elb", is_flag=True, help="Also check ELB/ALB/NLB usage (comprehensive scan)")
18
+ @click.option("--include-eni", is_flag=True, help="Also check network interface usage")
19
+ def find_unused_security_groups(dry_run, include_elb, include_eni):
20
+ """Find and remove unused security groups with comprehensive usage checks."""
21
+ logger.info(f"Finding unused security groups in {display_aws_account_info()}")
22
+
23
+ try:
24
+ ec2 = get_client("ec2")
25
+
26
+ # Collect all security groups
27
+ logger.info("🔍 Scanning all security groups...")
28
+ response = ec2.describe_security_groups()
29
+ all_security_groups = {}
30
+ default_security_groups = set()
31
+
32
+ for sg in response["SecurityGroups"]:
33
+ sg_id = sg["GroupId"]
34
+ sg_name = sg.get("GroupName", "")
35
+ all_security_groups[sg_id] = {
36
+ "name": sg_name,
37
+ "description": sg.get("Description", ""),
38
+ "vpc_id": sg.get("VpcId", ""),
39
+ }
40
+
41
+ # Track default security groups (cannot be deleted)
42
+ if sg_name == "default":
43
+ default_security_groups.add(sg_id)
44
+
45
+ logger.info(f"Found {len(all_security_groups)} security groups")
46
+
47
+ # Find security groups in use
48
+ used_security_groups = set()
49
+
50
+ # Check EC2 instances
51
+ logger.info("📋 Checking EC2 instance usage...")
52
+ instances_response = ec2.describe_instances()
53
+ instance_count = 0
54
+
55
+ for reservation in instances_response["Reservations"]:
56
+ for instance in reservation["Instances"]:
57
+ instance_count += 1
58
+ for sg in instance.get("SecurityGroups", []):
59
+ used_security_groups.add(sg["GroupId"])
60
+
61
+ logger.info(f"Checked {instance_count} EC2 instances")
62
+
63
+ # Check Load Balancers if requested
64
+ if include_elb:
65
+ logger.info("🔍 Checking Load Balancer usage...")
66
+ try:
67
+ # Check Classic Load Balancers
68
+ elb = get_client("elb")
69
+ elb_response = elb.describe_load_balancers()
70
+ for lb in elb_response.get("LoadBalancerDescriptions", []):
71
+ for sg_id in lb.get("SecurityGroups", []):
72
+ used_security_groups.add(sg_id)
73
+
74
+ # Check Application/Network Load Balancers
75
+ elbv2 = get_client("elbv2")
76
+ elbv2_response = elbv2.describe_load_balancers()
77
+ for lb in elbv2_response.get("LoadBalancers", []):
78
+ for sg_id in lb.get("SecurityGroups", []):
79
+ used_security_groups.add(sg_id)
80
+
81
+ logger.info("✓ Load balancer usage checked")
82
+
83
+ except ClientError as e:
84
+ logger.warning(f"Could not check load balancers: {e}")
85
+
86
+ # Check Network Interfaces if requested
87
+ if include_eni:
88
+ logger.info("🔍 Checking network interface usage...")
89
+ try:
90
+ eni_response = ec2.describe_network_interfaces()
91
+ for eni in eni_response.get("NetworkInterfaces", []):
92
+ for group in eni.get("Groups", []):
93
+ used_security_groups.add(group["GroupId"])
94
+
95
+ logger.info("✓ Network interface usage checked")
96
+
97
+ except ClientError as e:
98
+ logger.warning(f"Could not check network interfaces: {e}")
99
+
100
+ # Check for security group references (ingress/egress rules)
101
+ logger.info("🔍 Checking security group rule references...")
102
+ referenced_security_groups = set()
103
+
104
+ for sg_id, sg_info in all_security_groups.items():
105
+ try:
106
+ sg_details = ec2.describe_security_groups(GroupIds=[sg_id])["SecurityGroups"][0]
107
+
108
+ # Check ingress rules for SG references
109
+ for rule in sg_details.get("IpPermissions", []):
110
+ for sg_ref in rule.get("UserIdGroupPairs", []):
111
+ referenced_security_groups.add(sg_ref["GroupId"])
112
+
113
+ # Check egress rules for SG references
114
+ for rule in sg_details.get("IpPermissionsEgress", []):
115
+ for sg_ref in rule.get("UserIdGroupPairs", []):
116
+ referenced_security_groups.add(sg_ref["GroupId"])
117
+
118
+ except ClientError as e:
119
+ logger.debug(f"Could not check rules for {sg_id}: {e}")
120
+
121
+ # Combine all usage types
122
+ all_used_groups = used_security_groups | referenced_security_groups | default_security_groups
123
+
124
+ # Find unused security groups
125
+ unused_security_groups = set(all_security_groups.keys()) - all_used_groups
126
+
127
+ logger.info("\n=== ANALYSIS RESULTS ===")
128
+ logger.info(f"Total security groups: {len(all_security_groups)}")
129
+ logger.info(f"Used by resources: {len(used_security_groups)}")
130
+ logger.info(f"Referenced in rules: {len(referenced_security_groups)}")
131
+ logger.info(f"Default groups (protected): {len(default_security_groups)}")
132
+ logger.info(f"Unused security groups: {len(unused_security_groups)}")
133
+
134
+ if not unused_security_groups:
135
+ logger.info("✅ No unused security groups found")
136
+ return
137
+
138
+ logger.warning(f"⚠ Found {len(unused_security_groups)} unused security groups")
139
+
140
+ # Show unused security groups details
141
+ logger.info("\n📋 Unused Security Groups:")
142
+ deletion_candidates = []
143
+
144
+ for sg_id in unused_security_groups:
145
+ sg_info = all_security_groups[sg_id]
146
+ logger.info(f" {sg_id}: {sg_info['name']}")
147
+ logger.info(f" Description: {sg_info['description']}")
148
+ logger.info(f" VPC: {sg_info['vpc_id']}")
149
+
150
+ # Skip default security groups
151
+ if sg_info["name"] == "default":
152
+ logger.info(f" Status: Protected (default security group)")
153
+ else:
154
+ deletion_candidates.append(sg_id)
155
+ logger.info(f" Status: Can be deleted")
156
+
157
+ logger.info(f"\n📊 Summary: {len(deletion_candidates)} security groups can be safely deleted")
158
+
159
+ # Delete unused security groups
160
+ if deletion_candidates:
161
+ if dry_run:
162
+ logger.info("DRY-RUN: Would delete the following security groups:")
163
+ for sg_id in deletion_candidates:
164
+ sg_info = all_security_groups[sg_id]
165
+ logger.info(f" - {sg_id} ({sg_info['name']})")
166
+ logger.info("To perform actual deletion, run with --no-dry-run")
167
+ else:
168
+ logger.info("🗑 Deleting unused security groups...")
169
+ deleted_count = 0
170
+ failed_count = 0
171
+
172
+ for sg_id in deletion_candidates:
173
+ sg_info = all_security_groups[sg_id]
174
+ logger.info(f" → Deleting {sg_id} ({sg_info['name']})...")
175
+
176
+ try:
177
+ ec2.delete_security_group(GroupId=sg_id)
178
+ deleted_count += 1
179
+ logger.info(f" ✓ Successfully deleted {sg_id}")
180
+
181
+ except ClientError as e:
182
+ error_code = e.response.get("Error", {}).get("Code", "Unknown")
183
+ if error_code == "DependencyViolation":
184
+ logger.warning(f" ⚠ Cannot delete {sg_id}: has dependencies")
185
+ elif error_code == "InvalidGroup.InUse":
186
+ logger.warning(f" ⚠ Cannot delete {sg_id}: currently in use")
187
+ else:
188
+ logger.error(f" ✗ Failed to delete {sg_id}: {e}")
189
+ failed_count += 1
190
+
191
+ logger.info(f"\n✅ Deletion complete: {deleted_count} deleted, {failed_count} failed")
192
+
193
+ except ClientError as e:
194
+ logger.error(f"Failed to scan security groups: {e}")
195
+ raise
196
+ except Exception as e:
197
+ logger.error(f"Unexpected error: {e}")
198
+ raise
199
+
200
+
201
+ if __name__ == "__main__":
202
+ find_unused_security_groups()