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,643 @@
1
+ """
2
+ Enterprise Remediation Base Classes - Production-Ready Security & Compliance Automation
3
+
4
+ This module provides the foundational architecture for AWS security and compliance
5
+ remediation operations, designed to integrate seamlessly with assessment findings
6
+ from security and CFAT modules.
7
+
8
+ ## Design Principles
9
+
10
+ **Safety First**: All operations include dry-run, backup, and rollback capabilities
11
+ **Enterprise Ready**: Multi-account, multi-region support with SSO integration
12
+ **Audit Compliant**: Complete operation tracking and compliance mapping
13
+ **Assessment Integration**: Direct integration with security/CFAT findings
14
+
15
+ ## Architecture
16
+
17
+ The remediation framework follows proven enterprise patterns from the operate module:
18
+
19
+ - **BaseRemediation**: Abstract base class for all remediation operations
20
+ - **RemediationContext**: Execution context with assessment findings integration
21
+ - **RemediationResult**: Structured outcomes with compliance mapping
22
+ - **RemediationStatus**: Operation states and progress tracking
23
+
24
+ ## Example Usage
25
+
26
+ ```python
27
+ from runbooks.remediation import S3SecurityRemediation
28
+ from runbooks.security import SecurityBaselineTester
29
+
30
+ # 1. Run security assessment
31
+ security_findings = SecurityBaselineTester().run_assessment()
32
+
33
+ # 2. Create remediation context from findings
34
+ context = RemediationContext.from_security_findings(security_findings)
35
+
36
+ # 3. Execute remediation with safety checks
37
+ s3_remediation = S3SecurityRemediation()
38
+ results = s3_remediation.enforce_ssl(context, bucket_name="critical-data")
39
+
40
+ # 4. Verify remediation success
41
+ verification_results = security_findings.verify_remediation(results)
42
+ ```
43
+
44
+ ## Multi-Account Integration
45
+
46
+ All remediation operations support enterprise multi-account patterns:
47
+
48
+ ```python
49
+ # Multi-account remediation execution
50
+ accounts = ["123456789012", "987654321098", "456789012345"]
51
+ results = s3_remediation.enforce_ssl_bulk(context, accounts=accounts)
52
+ ```
53
+
54
+ ## Compliance Mapping
55
+
56
+ Each remediation operation includes compliance framework mapping:
57
+
58
+ - **CIS AWS Foundations Benchmark**: Direct control mapping
59
+ - **NIST Cybersecurity Framework**: Category and function alignment
60
+ - **AWS Well-Architected Framework**: Pillar and principle mapping
61
+ - **CheckPoint CloudGuard/Dome9**: Rule-by-rule remediation mapping
62
+
63
+ Version: 0.7.6 - Enterprise Production Ready
64
+ Compatibility: AWS SDK v3, Python 3.8+, Multi-deployment ready
65
+ """
66
+
67
+ import json
68
+ import logging
69
+ import os
70
+ import uuid
71
+ from abc import ABC, abstractmethod
72
+ from datetime import datetime, timedelta
73
+ from enum import Enum
74
+ from typing import Any, Dict, List, Optional, Set, Union
75
+
76
+ import boto3
77
+ import click
78
+ from botocore.exceptions import BotoCoreError, ClientError
79
+ from loguru import logger
80
+ from pydantic import BaseModel, Field
81
+
82
+ from runbooks.inventory.models.account import AWSAccount
83
+
84
+
85
+ class RemediationStatus(Enum):
86
+ """
87
+ Enumerated remediation operation states.
88
+
89
+ Provides consistent status tracking across all remediation operations
90
+ with clear semantic meaning for enterprise reporting and monitoring.
91
+ """
92
+
93
+ PENDING = "pending" # Remediation planned but not started
94
+ IN_PROGRESS = "in_progress" # Remediation currently executing
95
+ SUCCESS = "success" # Remediation completed successfully
96
+ FAILED = "failed" # Remediation failed with errors
97
+ DRY_RUN = "dry_run" # Dry-run mode (no actual changes)
98
+ CANCELLED = "cancelled" # User cancelled operation
99
+ ROLLED_BACK = "rolled_back" # Operation rolled back
100
+ REQUIRES_MANUAL = "requires_manual" # Manual intervention required
101
+ SKIPPED = "skipped" # Operation skipped (already compliant)
102
+
103
+
104
+ class ComplianceMapping(BaseModel):
105
+ """
106
+ Compliance framework mapping for remediation operations.
107
+
108
+ Maps each remediation to relevant compliance frameworks and controls,
109
+ enabling automated compliance reporting and audit trail generation.
110
+ """
111
+
112
+ cis_controls: List[str] = Field(default_factory=list, description="CIS AWS Foundations controls")
113
+ nist_categories: List[str] = Field(default_factory=list, description="NIST Cybersecurity Framework categories")
114
+ well_architected_pillars: List[str] = Field(default_factory=list, description="AWS Well-Architected pillars")
115
+ dome9_rules: List[str] = Field(default_factory=list, description="CheckPoint CloudGuard/Dome9 rules")
116
+ aws_config_rules: List[str] = Field(default_factory=list, description="AWS Config compliance rules")
117
+ severity: str = Field(default="medium", description="Risk severity level")
118
+
119
+
120
+ class RemediationContext(BaseModel):
121
+ """
122
+ Comprehensive execution context for remediation operations.
123
+
124
+ Provides all necessary context for safe, auditable remediation execution
125
+ including account information, safety settings, and assessment integration.
126
+
127
+ Attributes:
128
+ account: AWS account information
129
+ region: Target AWS region
130
+ operation_type: Type of remediation operation
131
+ resource_types: AWS resource types affected
132
+ dry_run: Safety flag for testing operations
133
+ force: Override confirmation prompts (for automation)
134
+ backup_enabled: Enable automatic backup creation
135
+ compliance_mapping: Compliance framework mapping
136
+ assessment_findings: Related assessment findings
137
+ rollback_plan: Automatic rollback configuration
138
+ change_ticket: Change management integration
139
+ """
140
+
141
+ account: AWSAccount
142
+ region: str = Field(default="us-east-1", description="AWS region")
143
+ operation_type: str = Field(description="Remediation operation type")
144
+ resource_types: List[str] = Field(default_factory=list, description="AWS resource types")
145
+
146
+ # Safety and control flags
147
+ dry_run: bool = Field(default=True, description="Enable dry-run mode")
148
+ force: bool = Field(default=False, description="Skip confirmation prompts")
149
+ backup_enabled: bool = Field(default=True, description="Enable automatic backups")
150
+
151
+ # Compliance and audit
152
+ compliance_mapping: ComplianceMapping = Field(default_factory=ComplianceMapping)
153
+ assessment_findings: Dict[str, Any] = Field(default_factory=dict, description="Related assessment findings")
154
+
155
+ # Enterprise features
156
+ rollback_plan: Dict[str, Any] = Field(default_factory=dict, description="Rollback configuration")
157
+ change_ticket: Optional[str] = Field(default=None, description="Change management ticket")
158
+ notification_targets: List[str] = Field(default_factory=list, description="SNS notification targets")
159
+
160
+ @classmethod
161
+ def from_security_findings(cls, findings: Dict[str, Any], **kwargs) -> "RemediationContext":
162
+ """
163
+ Create remediation context from security assessment findings.
164
+
165
+ Args:
166
+ findings: Security assessment findings from security module
167
+ **kwargs: Additional context parameters
168
+
169
+ Returns:
170
+ RemediationContext with populated assessment data
171
+
172
+ Example:
173
+ ```python
174
+ security_findings = SecurityBaselineTester().run_assessment()
175
+ context = RemediationContext.from_security_findings(security_findings)
176
+ ```
177
+ """
178
+ # Extract account information from findings
179
+ account_id = findings.get("account_id", "unknown")
180
+ account_name = findings.get("account_name", "unknown")
181
+ account = AWSAccount(account_id=account_id, account_name=account_name)
182
+
183
+ # Map findings to compliance frameworks
184
+ compliance_mapping = ComplianceMapping()
185
+ if "cis_controls" in findings:
186
+ compliance_mapping.cis_controls = findings["cis_controls"]
187
+ if "severity" in findings:
188
+ compliance_mapping.severity = findings["severity"]
189
+
190
+ return cls(
191
+ account=account,
192
+ operation_type="security_remediation",
193
+ assessment_findings=findings,
194
+ compliance_mapping=compliance_mapping,
195
+ **kwargs,
196
+ )
197
+
198
+ @classmethod
199
+ def from_cfat_findings(cls, findings: Dict[str, Any], **kwargs) -> "RemediationContext":
200
+ """
201
+ Create remediation context from CFAT assessment findings.
202
+
203
+ Args:
204
+ findings: CFAT assessment findings from cfat module
205
+ **kwargs: Additional context parameters
206
+
207
+ Returns:
208
+ RemediationContext with populated assessment data
209
+ """
210
+ account_id = findings.get("account_id", "unknown")
211
+ account_name = findings.get("account_name", "unknown")
212
+ account = AWSAccount(account_id=account_id, account_name=account_name)
213
+
214
+ # Map CFAT findings to Well-Architected pillars
215
+ compliance_mapping = ComplianceMapping()
216
+ if "well_architected_pillars" in findings:
217
+ compliance_mapping.well_architected_pillars = findings["well_architected_pillars"]
218
+
219
+ return cls(
220
+ account=account,
221
+ operation_type="cfat_remediation",
222
+ assessment_findings=findings,
223
+ compliance_mapping=compliance_mapping,
224
+ **kwargs,
225
+ )
226
+
227
+
228
+ class RemediationResult(BaseModel):
229
+ """
230
+ Structured result from remediation operations.
231
+
232
+ Provides comprehensive outcome tracking with compliance mapping,
233
+ backup information, and rollback capabilities for enterprise audit trails.
234
+
235
+ Attributes:
236
+ operation_id: Unique operation identifier
237
+ context: Original operation context
238
+ status: Current operation status
239
+ start_time: Operation start timestamp
240
+ end_time: Operation completion timestamp
241
+ affected_resources: List of resources modified
242
+ backup_locations: Backup storage locations
243
+ rollback_instructions: Manual rollback procedures
244
+ compliance_evidence: Compliance verification data
245
+ error_message: Error details if operation failed
246
+ response_data: Raw AWS API response data
247
+ """
248
+
249
+ operation_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique operation ID")
250
+ context: RemediationContext
251
+ status: RemediationStatus = Field(default=RemediationStatus.PENDING)
252
+
253
+ # Timing information
254
+ start_time: datetime = Field(default_factory=datetime.utcnow)
255
+ end_time: Optional[datetime] = Field(default=None)
256
+ duration_seconds: Optional[float] = Field(default=None)
257
+
258
+ # Resource tracking
259
+ affected_resources: List[str] = Field(default_factory=list, description="Resources modified")
260
+ backup_locations: Dict[str, str] = Field(default_factory=dict, description="Backup storage locations")
261
+
262
+ # Enterprise features
263
+ rollback_instructions: List[str] = Field(default_factory=list, description="Rollback procedures")
264
+ compliance_evidence: Dict[str, Any] = Field(default_factory=dict, description="Compliance verification")
265
+
266
+ # Operation outcomes
267
+ error_message: Optional[str] = Field(default=None)
268
+ response_data: Dict[str, Any] = Field(default_factory=dict)
269
+
270
+ @property
271
+ def success(self) -> bool:
272
+ """Check if remediation was successful."""
273
+ return self.status == RemediationStatus.SUCCESS
274
+
275
+ @property
276
+ def failed(self) -> bool:
277
+ """Check if remediation failed."""
278
+ return self.status == RemediationStatus.FAILED
279
+
280
+ def mark_completed(self, status: RemediationStatus, error_message: str = None) -> None:
281
+ """
282
+ Mark remediation as completed with final status.
283
+
284
+ Args:
285
+ status: Final operation status
286
+ error_message: Error details if operation failed
287
+ """
288
+ self.status = status
289
+ self.end_time = datetime.utcnow()
290
+ self.duration_seconds = (self.end_time - self.start_time).total_seconds()
291
+
292
+ if error_message:
293
+ self.error_message = error_message
294
+
295
+ logger.info(f"Remediation {self.operation_id} completed with status: {status.value}")
296
+
297
+ def create_rollback_plan(self, instructions: List[str]) -> None:
298
+ """
299
+ Create rollback plan with manual instructions.
300
+
301
+ Args:
302
+ instructions: Step-by-step rollback procedures
303
+ """
304
+ self.rollback_instructions = instructions
305
+ logger.info(f"Rollback plan created for operation {self.operation_id}")
306
+
307
+ def add_compliance_evidence(self, framework: str, evidence: Dict[str, Any]) -> None:
308
+ """
309
+ Add compliance verification evidence.
310
+
311
+ Args:
312
+ framework: Compliance framework name (CIS, NIST, etc.)
313
+ evidence: Verification evidence data
314
+ """
315
+ self.compliance_evidence[framework] = evidence
316
+ logger.debug(f"Compliance evidence added for {framework}")
317
+
318
+
319
+ class BaseRemediation(ABC):
320
+ """
321
+ Abstract base class for all AWS remediation operations.
322
+
323
+ Provides consistent enterprise patterns for safety, auditing, and compliance
324
+ across all remediation implementations. Follows the proven architecture
325
+ from the operate module with enhanced safety and compliance features.
326
+
327
+ Key Features:
328
+ - Automatic backup creation before changes
329
+ - Comprehensive dry-run capabilities
330
+ - Multi-account and multi-region support
331
+ - Compliance framework mapping
332
+ - Integration with assessment findings
333
+ - Rollback and recovery procedures
334
+
335
+ Example Implementation:
336
+ ```python
337
+ class S3SecurityRemediation(BaseRemediation):
338
+ supported_operations = ["enforce_ssl", "block_public_access", "enable_encryption"]
339
+
340
+ def enforce_ssl(self, context: RemediationContext, bucket_name: str) -> List[RemediationResult]:
341
+ result = self.create_remediation_result(context, "enforce_ssl", "s3:bucket", bucket_name)
342
+
343
+ try:
344
+ # Create backup if enabled
345
+ if context.backup_enabled:
346
+ self.create_backup(context, bucket_name)
347
+
348
+ # Execute remediation
349
+ if not context.dry_run:
350
+ self.apply_ssl_policy(bucket_name)
351
+
352
+ result.mark_completed(RemediationStatus.SUCCESS)
353
+
354
+ except Exception as e:
355
+ result.mark_completed(RemediationStatus.FAILED, str(e))
356
+
357
+ return [result]
358
+ ```
359
+ """
360
+
361
+ supported_operations: List[str] = []
362
+
363
+ def __init__(self, profile: str = None, region: str = None, **kwargs):
364
+ """
365
+ Initialize remediation base with AWS configuration.
366
+
367
+ Args:
368
+ profile: AWS profile name (uses environment if not specified)
369
+ region: AWS region (uses environment if not specified)
370
+ **kwargs: Additional configuration parameters
371
+ """
372
+ self.profile = profile or os.getenv("AWS_PROFILE", "default")
373
+ self.region = region or os.getenv("AWS_REGION", "us-east-1")
374
+
375
+ # Enterprise configuration
376
+ self.backup_enabled = kwargs.get("backup_enabled", True)
377
+ self.notification_enabled = kwargs.get("notification_enabled", False)
378
+ self.sns_topic_arn = kwargs.get("sns_topic_arn", os.getenv("REMEDIATION_SNS_TOPIC_ARN"))
379
+
380
+ # Initialize AWS clients lazily
381
+ self._session = None
382
+ self._clients = {}
383
+
384
+ logger.info(f"Initialized remediation base for profile: {self.profile}, region: {self.region}")
385
+
386
+ @property
387
+ def session(self) -> boto3.Session:
388
+ """Get or create AWS session with profile configuration."""
389
+ if self._session is None:
390
+ try:
391
+ self._session = boto3.Session(profile_name=self.profile, region_name=self.region)
392
+ except Exception as e:
393
+ logger.warning(f"Failed to create session with profile {self.profile}: {e}")
394
+ self._session = boto3.Session(region_name=self.region)
395
+ return self._session
396
+
397
+ def get_client(self, service_name: str, region: str = None) -> Any:
398
+ """
399
+ Get or create AWS service client with caching.
400
+
401
+ Args:
402
+ service_name: AWS service name (e.g., 's3', 'ec2', 'iam')
403
+ region: Override region for client
404
+
405
+ Returns:
406
+ Configured AWS service client
407
+ """
408
+ region = region or self.region
409
+ client_key = f"{service_name}_{region}"
410
+
411
+ if client_key not in self._clients:
412
+ try:
413
+ self._clients[client_key] = self.session.client(service_name, region_name=region)
414
+ logger.debug(f"Created AWS client for {service_name} in {region}")
415
+ except Exception as e:
416
+ logger.error(f"Failed to create {service_name} client: {e}")
417
+ raise
418
+
419
+ return self._clients[client_key]
420
+
421
+ def create_remediation_result(
422
+ self, context: RemediationContext, operation_type: str, resource_type: str, resource_id: str
423
+ ) -> RemediationResult:
424
+ """
425
+ Create standardized remediation result object.
426
+
427
+ Args:
428
+ context: Remediation execution context
429
+ operation_type: Type of remediation operation
430
+ resource_type: AWS resource type (e.g., 's3:bucket', 'ec2:instance')
431
+ resource_id: Unique resource identifier
432
+
433
+ Returns:
434
+ Initialized RemediationResult object
435
+ """
436
+ # Update context with operation details
437
+ context.operation_type = operation_type
438
+ if resource_type not in context.resource_types:
439
+ context.resource_types.append(resource_type)
440
+
441
+ result = RemediationResult(context=context, affected_resources=[f"{resource_type}:{resource_id}"])
442
+
443
+ logger.info(f"Created remediation result {result.operation_id} for {resource_type}:{resource_id}")
444
+ return result
445
+
446
+ def execute_aws_call(self, client: Any, method_name: str, **kwargs) -> Dict[str, Any]:
447
+ """
448
+ Execute AWS API call with enterprise error handling.
449
+
450
+ Args:
451
+ client: AWS service client
452
+ method_name: API method name
453
+ **kwargs: Method parameters
454
+
455
+ Returns:
456
+ AWS API response data
457
+
458
+ Raises:
459
+ ClientError: AWS service errors
460
+ BotoCoreError: Boto3 core errors
461
+ """
462
+ try:
463
+ method = getattr(client, method_name)
464
+ response = method(**kwargs)
465
+ logger.debug(f"AWS API call successful: {method_name}")
466
+ return response
467
+
468
+ except ClientError as e:
469
+ error_code = e.response.get("Error", {}).get("Code", "Unknown")
470
+ error_message = e.response.get("Error", {}).get("Message", str(e))
471
+ logger.error(f"AWS ClientError in {method_name}: {error_code} - {error_message}")
472
+ raise
473
+
474
+ except BotoCoreError as e:
475
+ logger.error(f"AWS BotoCoreError in {method_name}: {e}")
476
+ raise
477
+
478
+ except Exception as e:
479
+ logger.error(f"Unexpected error in {method_name}: {e}")
480
+ raise
481
+
482
+ def create_backup(self, context: RemediationContext, resource_id: str, backup_type: str = "configuration") -> str:
483
+ """
484
+ Create backup of resource configuration before remediation.
485
+
486
+ Args:
487
+ context: Remediation execution context
488
+ resource_id: Resource identifier
489
+ backup_type: Type of backup (configuration, snapshot, etc.)
490
+
491
+ Returns:
492
+ Backup location or identifier
493
+ """
494
+ if not context.backup_enabled:
495
+ logger.info("Backup disabled, skipping backup creation")
496
+ return ""
497
+
498
+ timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
499
+ backup_key = f"remediation_backup_{resource_id}_{timestamp}"
500
+
501
+ try:
502
+ # Implementation depends on resource type
503
+ backup_location = self._create_resource_backup(resource_id, backup_key, backup_type)
504
+ logger.info(f"Backup created for {resource_id} at {backup_location}")
505
+ return backup_location
506
+
507
+ except Exception as e:
508
+ logger.error(f"Failed to create backup for {resource_id}: {e}")
509
+ raise
510
+
511
+ @abstractmethod
512
+ def _create_resource_backup(self, resource_id: str, backup_key: str, backup_type: str) -> str:
513
+ """
514
+ Implementation-specific backup creation.
515
+
516
+ Must be implemented by each remediation class to handle
517
+ resource-specific backup procedures.
518
+ """
519
+ pass
520
+
521
+ def confirm_operation(self, context: RemediationContext, resource_id: str, operation_type: str) -> bool:
522
+ """
523
+ Confirm destructive operations with user interaction.
524
+
525
+ Args:
526
+ context: Remediation execution context
527
+ resource_id: Resource identifier
528
+ operation_type: Operation description
529
+
530
+ Returns:
531
+ True if operation confirmed, False otherwise
532
+ """
533
+ if context.force:
534
+ logger.info(f"Force mode enabled, skipping confirmation for {operation_type}")
535
+ return True
536
+
537
+ if context.dry_run:
538
+ logger.info(f"Dry-run mode, confirmation not required for {operation_type}")
539
+ return True
540
+
541
+ try:
542
+ confirmation = click.confirm(
543
+ f"⚠️ DESTRUCTIVE OPERATION: {operation_type} on {resource_id}. Continue?", default=False
544
+ )
545
+ if confirmation:
546
+ logger.info(f"User confirmed {operation_type} on {resource_id}")
547
+ else:
548
+ logger.info(f"User cancelled {operation_type} on {resource_id}")
549
+ return confirmation
550
+
551
+ except Exception as e:
552
+ logger.error(f"Confirmation prompt failed: {e}")
553
+ return False
554
+
555
+ def send_notification(self, context: RemediationContext, result: RemediationResult) -> None:
556
+ """
557
+ Send operation notification via SNS.
558
+
559
+ Args:
560
+ context: Remediation execution context
561
+ result: Remediation operation result
562
+ """
563
+ if not self.notification_enabled or not self.sns_topic_arn:
564
+ return
565
+
566
+ try:
567
+ sns_client = self.get_client("sns")
568
+
569
+ message = {
570
+ "operation_id": result.operation_id,
571
+ "operation_type": context.operation_type,
572
+ "status": result.status.value,
573
+ "account": context.account.account_id,
574
+ "region": context.region,
575
+ "affected_resources": result.affected_resources,
576
+ "duration_seconds": result.duration_seconds,
577
+ }
578
+
579
+ subject = f"Remediation {result.status.value}: {context.operation_type}"
580
+
581
+ self.execute_aws_call(
582
+ sns_client,
583
+ "publish",
584
+ TopicArn=self.sns_topic_arn,
585
+ Message=json.dumps(message, default=str),
586
+ Subject=subject,
587
+ )
588
+
589
+ logger.info(f"Notification sent for operation {result.operation_id}")
590
+
591
+ except Exception as e:
592
+ logger.error(f"Failed to send notification: {e}")
593
+
594
+ @abstractmethod
595
+ def execute_remediation(self, context: RemediationContext, **kwargs) -> List[RemediationResult]:
596
+ """
597
+ Execute specific remediation operation.
598
+
599
+ Must be implemented by each remediation class to provide
600
+ the actual remediation logic for their service area.
601
+
602
+ Args:
603
+ context: Remediation execution context
604
+ **kwargs: Operation-specific parameters
605
+
606
+ Returns:
607
+ List of remediation results
608
+ """
609
+ pass
610
+
611
+ def bulk_execute(self, contexts: List[RemediationContext], **kwargs) -> List[RemediationResult]:
612
+ """
613
+ Execute remediation across multiple accounts/resources.
614
+
615
+ Args:
616
+ contexts: List of remediation contexts
617
+ **kwargs: Operation-specific parameters
618
+
619
+ Returns:
620
+ Consolidated list of remediation results
621
+ """
622
+ all_results = []
623
+
624
+ for context in contexts:
625
+ try:
626
+ results = self.execute_remediation(context, **kwargs)
627
+ all_results.extend(results)
628
+
629
+ # Send notifications for each result
630
+ for result in results:
631
+ self.send_notification(context, result)
632
+
633
+ except Exception as e:
634
+ logger.error(f"Bulk execution failed for context {context.account.account_id}: {e}")
635
+ # Create failure result
636
+ error_result = self.create_remediation_result(
637
+ context, "bulk_execution", "account", context.account.account_id
638
+ )
639
+ error_result.mark_completed(RemediationStatus.FAILED, str(e))
640
+ all_results.append(error_result)
641
+
642
+ logger.info(f"Bulk execution completed: {len(all_results)} total results")
643
+ return all_results