8000 Add CloudFormation models for SSM maintenance window support (#8377) · localstack/localstack@3908ec6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3908ec6

Browse files
Add CloudFormation models for SSM maintenance window support (#8377)
Co-authored-by: Dominik Schubert <dominik.schubert91@gmail.com>
1 parent 75ad307 commit 3908ec6

File tree

6 files changed

+577
-4
lines changed

6 files changed

+577
-4
lines changed

localstack/services/cloudformation/models/ssm.py

Lines changed: 187 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
select_parameters,
66
)
77
from localstack.services.cloudformation.service_models import GenericBaseModel
8-
from localstack.utils.aws import aws_stack
98
from localstack.utils.collections import select_attributes
109
from localstack.utils.common import short_uid
1110

@@ -17,7 +16,7 @@ def cloudformation_type():
1716

1817
def fetch_state(self, stack_name, resources):
1918
param_name = self.props.get("Name") or self.logical_resource_id
20-
return aws_stack.connect_to_service("ssm").get_parameter(Name=param_name)["Parameter"]
19+
return connect_to().ssm.get_parameter(Name=param_name)["Parameter"]
2120

2221
@staticmethod
2322
def add_defaults(resource, stack_name: str):
@@ -80,8 +79,7 @@ def update_resource(self, new_resource, stack_name, resources):
8079

8180
@staticmethod
8281
def get_deploy_templates():
83-
def _handle_result(result, resource_id, resources, resource_type):
84-
resource = resources[resource_id]
82+
def _handle_result(result, logical_resource_id, resource):
8583
resource["PhysicalResourceId"] = resource["Properties"]["Name"]
8684

8785
return {
@@ -104,3 +102,188 @@ def _handle_result(result, resource_id, resources, resource_type):
104102
},
105103
"delete": {"function": "delete_parameter", "parameters": ["Name"]},
106104
}
105+
106+
107+
class SSMMaintenanceWindow(GenericBaseModel):
108+
@staticmethod
109+
def cloudformation_type():
110+
return "AWS::SSM::MaintenanceWindow"
111+
112+
def fetch_state(self, stack_name, resources):
113+
if not self.physical_resource_id:
114+
return None
115+
maintenance_windows = connect_to().ssm.describe_maintenance_windows()["WindowIdentities"]
116+
for maintenance_window in maintenance_windows:
117+
if maintenance_window["WindowId"] == self.physical_resource_id:
118+
return maintenance_window
119+
120+
@staticmethod
121+
def get_deploy_templates():
122+
def _delete_window(logical_resource_id, resource, stack_name):
123+
connect_to().ssm.delete_maintenance_window(WindowId=resource["PhysicalResourceId"])
124+
125+
def _handle_result(result, logical_resource_id, resource):
126+
resource["PhysicalResourceId"] = result["WindowId"]
127+
128+
return {
129+
"create": {
130+
"function": "create_maintenance_window",
131+
"parameters": select_parameters(
132+
"AllowUnassociatedTargets",
133+
"Cutoff",
134+
"Duration",
135+
"Name",
136+
"Schedule",
137+
"ScheduleOffset",
138+
"ScheduleTimezone",
139+
"StartDate",
140+
"EndDate",
141+
"Description",
142+
"Tags",
143+
),
144+
"result_handler": _handle_result,
145+
},
146+
"delete": {"function": _delete_window},
147+
}
148+
149+
57AE
150+
class SSMMaintenanceWindowTarget(GenericBaseModel):
151+
@staticmethod
152+
def cloudformation_type():
153+
return "AWS::SSM::MaintenanceWindowTarget"
154+
155+
def fetch_state(self, stack_name, resources):
156+
targets = connect_to().ssm.describe_maintenance_window_targets(
157+
WindowTargetId=self.props.get("WindowTargetId")
158+
)["Targets"]
159+
targets = [
160+
target for target in targets if target["WindowTargetId"] == self.physical_resource_id
161+
]
162+
return targets[0] if targets else None
163+
164 B41A +
@staticmethod
165+
def get_deploy_templates():
166+
def _delete_window_target(logical_resource_id, resource, stack_name):
167+
connect_to().ssm.deregister_target_from_maintenance_window(
168+
WindowId=resource["Properties"]["WindowId"],
169+
WindowTargetId=resource["PhysicalResourceId"],
170+
)
171+
172+
def _handle_result(result, logical_resource_id, resource):
173+
resource["PhysicalResourceId"] = result["WindowTargetId"]
174+
175+
return {
176+
"create": {
177+
"function": "register_target_with_maintenance_window",
178+
"parameters": select_parameters(
179+
"Description",
180+
"Name",
181+
"OwnerInformation",
182+
"ResourceType",
183+
"Targets",
184+
"WindowId",
185+
),
186+
"result_handler": _handle_result,
187+
},
188+
"delete": {"function": _delete_window_target},
189+
}
190+
191+
192+
class SSMMaintenanceTask(GenericBaseModel):
193+
@staticmethod
194+
def cloudformation_type():
195+
return "AWS::SSM::MaintenanceWindowTask"
196+
197+
def fetch_state(self, stack_name, resources):
198+
return connect_to().ssm.describe_maintenance_window_task(
199+
WindowTaskId=self.props.get("WindowTaskId")
200+
)["WindowTaskId"]
201+
202+
@staticmethod
203+
def get_deploy_templates():
204+
def _delete_window_task(logical_resource_id, resource, stack_name):
205+
connect_to().ssm.deregister_task_from_maintenance_window(
206+
WindowId=resource["Properties"]["WindowId"],
207+
WindowTaskId=resource["PhysicalResourceId"],
208+
)
209+
210+
def _handle_result(result, logical_resource_id, resource):
211+
resource["PhysicalResourceId"] = result["WindowTaskId"]
212+
213+
def _params(properties, logical_resource_id, resource_def, stack_name):
214+
kwargs = {
215+
"Description": properties.get("Description"),
216+
"Name": properties.get("Name"),
217+
"OwnerInformation": properties.get("OwnerInformation"),
218+
"Priority": properties.get("Priority"),
219+
"ServiceRoleArn": properties.get("ServiceRoleArn"),
220+
"Targets": properties.get("Targets"),
221+
"TaskArn": properties.get("TaskArn"),
222+
"TaskParameters": properties.get("TaskParameters"),
223+
"TaskType": properties.get("TaskType"),
224+
"WindowId": properties.get("WindowId"),
225+
}
226+
227+
if invocation_params := properties.get("TaskInvocationParameters"):
228+
task_type_map = {
229+
"MaintenanceWindowAutomationParameters": "Automation",
230+
"MaintenanceWindowLambdaParameters": "Lambda",
231+
"MaintenanceWindowRunCommandParameters": "RunCommand",
232+
"MaintenanceWindowStepFunctionsParameters": "StepFunctions",
233+
}
234+
kwargs["TaskInvocationParameters"] = {
235+
task_type_map[k]: v for k, v in invocation_params.items()
236+
}
237+
238+
return kwargs
239+
240+
return {
241+
"create": {
242+
"function": "register_task_with_maintenance_window",
243+
"parameters": _params,
244+
"result_handler": _handle_result,
245+
},
246+
"delete": {"function": _delete_window_task},
247+
}
248+
249+
250+
class SSMPatchBaseline(GenericBaseModel):
251+
@staticmethod
252+
def cloudformation_type():
253+
return "AWS::SSM::PatchBaseline"
254+
255+
def fetch_state(self, stack_name, resources):
256+
return connect_to().ssm.describe_patch_baselines(BaselineId=self.props.get("BaselineId"))[
257+
"BaselineId"
258+
]
259+
260+
@staticmethod
261+
def get_deploy_templates():
262+
def _delete_patch_baseline(logical_resource_id, resource, stack_name):
263+
connect_to().ssm.delete_patch_baseline(BaselineId=resource["PhysicalResourceId"])
264+
265+
def _handle_result(result, logical_resource_id, resource):
266+
resource["PhysicalResourceId"] = result["BaselineId"]
267+
268+
return {
269+
"create": {
270+
"function": "create_patch_baseline",
271+
"parameters": select_parameters(
272+
"OperatingSystem",
273+
"Name",
274+
"GlobalFilters",
275+
"ApprovalRules",
276+
"ApprovedPatches",
277+
"ApprovedPatchesComplianceLevel",
278+
"ApprovedPatchesEnableNonSecurity",
279+
"RejectedPatches",
280+
"RejectedPatchesAction",
281+
"Description",
282+
"Sources",
283+
"ClientToken",
284+
"Tags",
285+
),
286+
"result_handler": _handle_result,
287+
},
288+
"delete": {"function": _delete_patch_baseline},
289+
}

0 commit comments

Comments
 (0)
0