1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
| # drift_remediation.py
import subprocess
import json
from enum import Enum
from typing import Optional
class RemediationStrategy(Enum):
ALERT_ONLY = "alert" # Just notify
AUTO_PLAN = "plan" # Create PR with plan
AUTO_APPLY = "apply" # Automatically apply (dangerous!)
REFRESH_ONLY = "refresh" # Update state to match reality
class DriftRemediator:
def __init__(self, workspace: str, strategy: RemediationStrategy):
self.workspace = workspace
self.strategy = strategy
def remediate(self, drift_report: dict) -> dict:
"""Remediate detected drift based on strategy."""
if not drift_report.get('has_drift'):
return {"action": "none", "reason": "no drift"}
if self.strategy == RemediationStrategy.ALERT_ONLY:
return self._alert(drift_report)
elif self.strategy == RemediationStrategy.REFRESH_ONLY:
return self._refresh_state()
elif self.strategy == RemediationStrategy.AUTO_PLAN:
return self._create_remediation_pr(drift_report)
elif self.strategy == RemediationStrategy.AUTO_APPLY:
return self._auto_apply()
return {"action": "unknown", "error": "invalid strategy"}
def _alert(self, drift_report: dict) -> dict:
"""Send alert without taking action."""
# Send to Slack, PagerDuty, etc.
return {
"action": "alert",
"changes": len(drift_report['changes'])
}
def _refresh_state(self) -> dict:
"""Refresh state to match reality (accept drift)."""
result = subprocess.run(
["terraform", "apply", "-refresh-only", "-auto-approve"],
capture_output=True,
text=True,
cwd=self.workspace
)
return {
"action": "refresh",
"success": result.returncode == 0,
"output": result.stdout
}
def _create_remediation_pr(self, drift_report: dict) -> dict:
"""Create a PR to remediate drift."""
# Generate plan
subprocess.run(
["terraform", "plan", "-out=remediation.tfplan"],
cwd=self.workspace
)
# Create branch and PR (pseudo-code)
branch = f"fix/drift-{self.workspace}-{datetime.now().strftime('%Y%m%d')}"
# In practice, you'd use GitHub API or similar
return {
"action": "pr_created",
"branch": branch,
"changes": len(drift_report['changes'])
}
def _auto_apply(self) -> dict:
"""Automatically apply to fix drift. USE WITH CAUTION."""
result = subprocess.run(
["terraform", "apply", "-auto-approve"],
capture_output=True,
text=True,
cwd=self.workspace
)
return {
"action": "auto_apply",
"success": result.returncode == 0,
"output": result.stdout if result.returncode == 0 else result.stderr
}
# Configuration per workspace
WORKSPACE_STRATEGIES = {
"networking": RemediationStrategy.ALERT_ONLY, # Critical, manual only
"compute": RemediationStrategy.AUTO_PLAN, # Create PRs
"monitoring": RemediationStrategy.REFRESH_ONLY, # Accept drift
"dev-sandbox": RemediationStrategy.AUTO_APPLY, # Auto-fix okay
}
|