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
@@ -1,374 +0,0 @@
1
- """
2
- Organizational Unit (OU) management for AWS Organizations.
3
-
4
- This module provides capabilities for setting up and managing
5
- AWS Organizations structure following Cloud Foundations best practices.
6
- """
7
-
8
- from pathlib import Path
9
- from typing import Any, Dict, List, Optional, Union
10
-
11
- import yaml
12
- from loguru import logger
13
-
14
- from runbooks.base import CloudFoundationsBase, ProgressTracker
15
- from runbooks.config import RunbooksConfig
16
-
17
-
18
- class OUManager(CloudFoundationsBase):
19
- """
20
- Manager for AWS Organizations OU structure.
21
-
22
- Provides capabilities to create and manage organizational unit
23
- structures based on Cloud Foundations templates.
24
- """
25
-
26
- def __init__(
27
- self, profile: Optional[str] = None, region: Optional[str] = None, config: Optional[RunbooksConfig] = None
28
- ):
29
- """Initialize OU manager."""
30
- super().__init__(profile, region, config)
31
- self._org_client = None
32
-
33
- @property
34
- def org_client(self):
35
- """Get AWS Organizations client."""
36
- if self._org_client is None:
37
- self._org_client = self.get_client("organizations")
38
- return self._org_client
39
-
40
- def get_template_structure(self, template: str) -> Dict[str, Any]:
41
- """
42
- Get predefined OU structure template.
43
-
44
- Args:
45
- template: Template name ('standard', 'security', 'custom')
46
-
47
- Returns:
48
- OU structure definition
49
- """
50
- templates = {
51
- "standard": {
52
- "name": "Standard OU Structure",
53
- "description": "Standard Cloud Foundations OU structure",
54
- "organizational_units": [
55
- {
56
- "name": "Core",
57
- "description": "Core organizational units for foundational services",
58
- "children": [
59
- {
60
- "name": "Log Archive",
61
- "description": "Centralized logging account",
62
- "policies": ["LogArchivePolicy"],
63
- },
64
- {
65
- "name": "Audit",
66
- "description": "Security and compliance auditing",
67
- "policies": ["AuditPolicy"],
68
- },
69
- {
70
- "name": "Shared Services",
71
- "description": "Shared infrastructure services",
72
- "policies": ["SharedServicesPolicy"],
73
- },
74
- ],
75
- },
76
- {
77
- "name": "Production",
78
- "description": "Production workload accounts",
79
- "children": [
80
- {
81
- "name": "Prod-WebApps",
82
- "description": "Production web applications",
83
- "policies": ["ProductionPolicy"],
84
- },
85
- {
86
- "name": "Prod-Data",
87
- "description": "Production data services",
88
- "policies": ["ProductionPolicy", "DataPolicy"],
89
- },
90
- ],
91
- },
92
- {
93
- "name": "Non-Production",
94
- "description": "Development and testing accounts",
95
- "children": [
96
- {
97
- "name": "Development",
98
- "description": "Development environments",
99
- "policies": ["DevelopmentPolicy"],
100
- },
101
- {
102
- "name": "Testing",
103
- "description": "Testing and staging environments",
104
- "policies": ["TestingPolicy"],
105
- },
106
- ],
107
- },
108
- ],
109
- },
110
- "security": {
111
- "name": "Security-Focused OU Structure",
112
- "description": "Enhanced security OU structure with additional controls",
113
- "organizational_units": [
114
- {
115
- "name": "Security",
116
- "description": "Security and compliance organizational unit",
117
- "children": [
118
- {
119
- "name": "Security-Prod",
120
- "description": "Production security tools",
121
- "policies": ["SecurityProdPolicy"],
122
- },
123
- {
124
- "name": "Security-NonProd",
125
- "description": "Non-production security tools",
126
- "policies": ["SecurityNonProdPolicy"],
127
- },
128
- {
129
- "name": "Log Archive",
130
- "description": "Centralized security logging",
131
- "policies": ["LogArchivePolicy", "SecurityLogPolicy"],
132
- },
133
- {
134
- "name": "Audit",
135
- "description": "Security auditing and compliance",
136
- "policies": ["AuditPolicy", "CompliancePolicy"],
137
- },
138
- ],
139
- },
140
- {
141
- "name": "Workloads",
142
- "description": "Application workload accounts",
143
- "children": [
144
- {
145
- "name": "Prod-HighSecurity",
146
- "description": "High security production workloads",
147
- "policies": ["HighSecurityPolicy", "ProductionPolicy"],
148
- },
149
- {
150
- "name": "Prod-Standard",
151
- "description": "Standard production workloads",
152
- "policies": ["StandardSecurityPolicy", "ProductionPolicy"],
153
- },
154
- {
155
- "name": "NonProd",
156
- "description": "Non-production workloads",
157
- "policies": ["NonProdPolicy"],
158
- },
159
- ],
160
- },
161
- ],
162
- },
163
- }
164
-
165
- if template not in templates:
166
- raise ValueError(f"Unknown template: {template}. Available: {list(templates.keys())}")
167
-
168
- logger.info(f"Using OU structure template: {template}")
169
- return templates[template]
170
-
171
- def load_structure_from_file(self, file_path: Union[str, Path]) -> Dict[str, Any]:
172
- """
173
- Load OU structure from YAML file.
174
-
175
- Args:
176
- file_path: Path to YAML structure file
177
-
178
- Returns:
179
- OU structure definition
180
- """
181
- config_path = Path(file_path)
182
-
183
- if not config_path.exists():
184
- raise FileNotFoundError(f"Structure file not found: {config_path}")
185
-
186
- try:
187
- with open(config_path, "r", encoding="utf-8") as f:
188
- structure = yaml.safe_load(f)
189
-
190
- logger.info(f"Loaded OU structure from: {config_path}")
191
- return structure
192
-
193
- except Exception as e:
194
- logger.error(f"Failed to load structure file: {e}")
195
- raise
196
-
197
- def create_ou_structure(self, structure: Dict[str, Any]) -> Dict[str, Any]:
198
- """
199
- Create OU structure in AWS Organizations.
200
-
201
- Args:
202
- structure: OU structure definition
203
-
204
- Returns:
205
- Creation results with OU IDs and status
206
- """
207
- logger.info(f"Creating OU structure: {structure.get('name', 'Unnamed')}")
208
-
209
- try:
210
- # Get organization root
211
- root_id = self._get_organization_root()
212
-
213
- # Create OUs
214
- results = {"structure_name": structure.get("name"), "root_id": root_id, "created_ous": [], "errors": []}
215
-
216
- organizational_units = structure.get("organizational_units", [])
217
- progress = ProgressTracker(len(organizational_units), "Creating organizational units")
218
-
219
- for ou_def in organizational_units:
220
- try:
221
- ou_result = self._create_ou_recursive(ou_def, root_id)
222
- results["created_ous"].append(ou_result)
223
- progress.update(status=f"Created {ou_def['name']}")
224
-
225
- except Exception as e:
226
- error_msg = f"Failed to create OU {ou_def['name']}: {e}"
227
- logger.error(error_msg)
228
- results["errors"].append(error_msg)
229
- progress.update(status=f"Failed {ou_def['name']}")
230
-
231
- progress.complete()
232
-
233
- logger.info(f"OU structure creation completed. Created {len(results['created_ous'])} OUs")
234
- return results
235
-
236
- except Exception as e:
237
- logger.error(f"OU structure creation failed: {e}")
238
- raise
239
-
240
- def _get_organization_root(self) -> str:
241
- """Get the organization root ID."""
242
- try:
243
- response = self._make_aws_call(self.org_client.list_roots)
244
-
245
- if not response.get("Roots"):
246
- raise Exception("No organization roots found")
247
-
248
- root_id = response["Roots"][0]["Id"]
249
- logger.debug(f"Found organization root: {root_id}")
250
- return root_id
251
-
252
- except Exception as e:
253
- logger.error(f"Failed to get organization root: {e}")
254
- raise
255
-
256
- def _create_ou_recursive(self, ou_def: Dict[str, Any], parent_id: str) -> Dict[str, Any]:
257
- """
258
- Recursively create OU and its children.
259
-
260
- Args:
261
- ou_def: OU definition
262
- parent_id: Parent OU ID
263
-
264
- Returns:
265
- Creation result with OU details
266
- """
267
- ou_name = ou_def["name"]
268
- ou_description = ou_def.get("description", "")
269
-
270
- logger.info(f"Creating OU: {ou_name} under parent: {parent_id}")
271
-
272
- # Check if OU already exists
273
- existing_ou = self._find_existing_ou(ou_name, parent_id)
274
- if existing_ou:
275
- logger.info(f"OU {ou_name} already exists: {existing_ou['Id']}")
276
- ou_id = existing_ou["Id"]
277
- else:
278
- # Create the OU
279
- response = self._make_aws_call(self.org_client.create_organizational_unit, ParentId=parent_id, Name=ou_name)
280
-
281
- ou_id = response["OrganizationalUnit"]["Id"]
282
- logger.info(f"Created OU {ou_name}: {ou_id}")
283
-
284
- result = {"name": ou_name, "id": ou_id, "parent_id": parent_id, "description": ou_description, "children": []}
285
-
286
- # Create child OUs
287
- children = ou_def.get("children", [])
288
- for child_def in children:
289
- try:
290
- child_result = self._create_ou_recursive(child_def, ou_id)
291
- result["children"].append(child_result)
292
- except Exception as e:
293
- logger.error(f"Failed to create child OU {child_def.get('name', 'Unknown')}: {e}")
294
-
295
- return result
296
-
297
- def _find_existing_ou(self, ou_name: str, parent_id: str) -> Optional[Dict[str, Any]]:
298
- """Find existing OU by name under a parent."""
299
- try:
300
- response = self._make_aws_call(self.org_client.list_organizational_units_for_parent, ParentId=parent_id)
301
-
302
- for ou in response.get("OrganizationalUnits", []):
303
- if ou["Name"] == ou_name:
304
- return ou
305
-
306
- return None
307
-
308
- except Exception as e:
309
- logger.warning(f"Error checking for existing OU {ou_name}: {e}")
310
- return None
311
-
312
- def list_organizational_units(self) -> List[Dict[str, Any]]:
313
- """List all organizational units in the organization."""
314
- try:
315
- root_id = self._get_organization_root()
316
- all_ous = []
317
-
318
- def collect_ous(parent_id: str, level: int = 0):
319
- response = self._make_aws_call(self.org_client.list_organizational_units_for_parent, ParentId=parent_id)
320
-
321
- for ou in response.get("OrganizationalUnits", []):
322
- ou["Level"] = level
323
- ou["ParentId"] = parent_id
324
- all_ous.append(ou)
325
-
326
- # Recursively collect child OUs
327
- collect_ous(ou["Id"], level + 1)
328
-
329
- collect_ous(root_id)
330
-
331
- logger.info(f"Found {len(all_ous)} organizational units")
332
- return all_ous
333
-
334
- except Exception as e:
335
- logger.error(f"Failed to list organizational units: {e}")
336
- raise
337
-
338
- def delete_ou(self, ou_id: str) -> bool:
339
- """
340
- Delete an organizational unit.
341
-
342
- Args:
343
- ou_id: OU ID to delete
344
-
345
- Returns:
346
- True if successful
347
- """
348
- try:
349
- # Check if OU has any accounts
350
- accounts_response = self._make_aws_call(self.org_client.list_accounts_for_parent, ParentId=ou_id)
351
-
352
- if accounts_response.get("Accounts"):
353
- raise Exception(f"Cannot delete OU {ou_id}: it contains accounts")
354
-
355
- # Check if OU has child OUs
356
- ous_response = self._make_aws_call(self.org_client.list_organizational_units_for_parent, ParentId=ou_id)
357
-
358
- if ous_response.get("OrganizationalUnits"):
359
- raise Exception(f"Cannot delete OU {ou_id}: it contains child OUs")
360
-
361
- # Delete the OU
362
- self._make_aws_call(self.org_client.delete_organizational_unit, OrganizationalUnitId=ou_id)
363
-
364
- logger.info(f"Deleted OU: {ou_id}")
365
- return True
366
-
367
- except Exception as e:
368
- logger.error(f"Failed to delete OU {ou_id}: {e}")
369
- raise
370
-
371
- def run(self):
372
- """Implementation of abstract base method."""
373
- # Default operation: list current OU structure
374
- return self.list_organizational_units()