runbooks 0.7.0__py3-none-any.whl → 0.7.6__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 (132) 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.6.dist-info/METADATA +608 -0
  67. {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/RECORD +84 -76
  68. {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/entry_points.txt +0 -1
  69. {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/top_level.txt +0 -1
  70. jupyter-agent/.env +0 -2
  71. jupyter-agent/.env.template +0 -2
  72. jupyter-agent/.gitattributes +0 -35
  73. jupyter-agent/.gradio/certificate.pem +0 -31
  74. jupyter-agent/README.md +0 -16
  75. jupyter-agent/__main__.log +0 -8
  76. jupyter-agent/app.py +0 -256
  77. jupyter-agent/cloudops-agent.png +0 -0
  78. jupyter-agent/ds-system-prompt.txt +0 -154
  79. jupyter-agent/jupyter-agent.png +0 -0
  80. jupyter-agent/llama3_template.jinja +0 -123
  81. jupyter-agent/requirements.txt +0 -9
  82. jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +0 -68
  83. jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +0 -91
  84. jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +0 -91
  85. jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +0 -57
  86. jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +0 -53
  87. jupyter-agent/tmp/jupyter-agent.ipynb +0 -27
  88. jupyter-agent/utils.py +0 -409
  89. runbooks/aws/__init__.py +0 -58
  90. runbooks/aws/dynamodb_operations.py +0 -231
  91. runbooks/aws/ec2_copy_image_cross-region.py +0 -195
  92. runbooks/aws/ec2_describe_instances.py +0 -202
  93. runbooks/aws/ec2_ebs_snapshots_delete.py +0 -186
  94. runbooks/aws/ec2_run_instances.py +0 -213
  95. runbooks/aws/ec2_start_stop_instances.py +0 -212
  96. runbooks/aws/ec2_terminate_instances.py +0 -143
  97. runbooks/aws/ec2_unused_eips.py +0 -196
  98. runbooks/aws/ec2_unused_volumes.py +0 -188
  99. runbooks/aws/s3_create_bucket.py +0 -142
  100. runbooks/aws/s3_list_buckets.py +0 -152
  101. runbooks/aws/s3_list_objects.py +0 -156
  102. runbooks/aws/s3_object_operations.py +0 -183
  103. runbooks/aws/tagging_lambda_handler.py +0 -183
  104. runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +0 -619
  105. runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +0 -738
  106. runbooks/inventory/aws_organization.png +0 -0
  107. runbooks/inventory/cfn_move_stack_instances.py +0 -1526
  108. runbooks/inventory/delete_s3_buckets_objects.py +0 -169
  109. runbooks/inventory/lockdown_cfn_stackset_role.py +0 -224
  110. runbooks/inventory/update_aws_actions.py +0 -173
  111. runbooks/inventory/update_cfn_stacksets.py +0 -1215
  112. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +0 -294
  113. runbooks/inventory/update_iam_roles_cross_accounts.py +0 -478
  114. runbooks/inventory/update_s3_public_access_block.py +0 -539
  115. runbooks/organizations/__init__.py +0 -12
  116. runbooks/organizations/manager.py +0 -374
  117. runbooks-0.7.0.dist-info/METADATA +0 -375
  118. /runbooks/inventory/{tests → Tests}/common_test_data.py +0 -0
  119. /runbooks/inventory/{tests → Tests}/common_test_functions.py +0 -0
  120. /runbooks/inventory/{tests → Tests}/script_test_data.py +0 -0
  121. /runbooks/inventory/{tests → Tests}/setup.py +0 -0
  122. /runbooks/inventory/{tests → Tests}/src.py +0 -0
  123. /runbooks/inventory/{tests/test_inventory_modules.py → Tests/test_Inventory_Modules.py} +0 -0
  124. /runbooks/inventory/{tests → Tests}/test_cfn_describe_stacks.py +0 -0
  125. /runbooks/inventory/{tests → Tests}/test_ec2_describe_instances.py +0 -0
  126. /runbooks/inventory/{tests → Tests}/test_lambda_list_functions.py +0 -0
  127. /runbooks/inventory/{tests → Tests}/test_moto_integration_example.py +0 -0
  128. /runbooks/inventory/{tests → Tests}/test_org_list_accounts.py +0 -0
  129. /runbooks/inventory/{Inventory_Modules.py → inventory_modules.py} +0 -0
  130. /runbooks/{aws → operate}/tags.json +0 -0
  131. {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/WHEEL +0 -0
  132. {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/licenses/LICENSE +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()