Skip to content

Commit 070bca7

Browse files
authored
Merge pull request #1 from zype/INF-2383
INF-2383 feat: Added support for System Manager Run Command notification format
2 parents 96153e3 + 623b860 commit 070bca7

File tree

7 files changed

+335
-1
lines changed

7 files changed

+335
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Doing serverless with Terraform? Check out [serverless.tf framework](https://ser
1717
- AWS CloudWatch Alarms
1818
- AWS CloudWatch LogMetrics Alarms
1919
- AWS GuardDuty Findings
20-
20+
- AWS System Manager Run Command Notifications
2121

2222
## Usage
2323

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"commandId": "6f69316a-e502-5a20-99bc-2408825b6dee",
3+
"documentName": "AWS-RunPatchBaseline",
4+
"instanceId": "i-99999999999999999",
5+
"requestedDateTime": "2023-12-14T10:48:47.127Z",
6+
"status": "Failed",
7+
"detailedStatus": "Failed",
8+
"eventTime": "2023-12-14T10:52:22.114Z"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"commandId": "6f69316a-e502-5a20-99bc-2408825b6dee",
3+
"documentName": "AWS-RunPatchBaseline",
4+
"instanceId": "i-99999999999999999",
5+
"requestedDateTime": "2023-12-14T10:48:47.127Z",
6+
"status": "Success",
7+
"detailedStatus": "Success",
8+
"eventTime": "2023-12-14T10:52:22.114Z"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"commandId": "6f69316a-e502-5a20-99bc-2408825b6dee",
3+
"documentName": "AWS-RunPatchBaseline",
4+
"instanceId": "i-99999999999999999",
5+
"requestedDateTime": "2023-12-14T10:48:47.127Z",
6+
"status": "TimedOut",
7+
"detailedStatus": "DeliveryTimedOut",
8+
"eventTime": "2023-12-14T10:52:22.114Z"
9+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"Records": [
3+
{
4+
"EventSource": "aws:sns",
5+
"EventVersion": "1.0",
6+
"EventSubscriptionArn": "arn:aws:sns:us-east-1::ExampleTopic",
7+
"Sns": {
8+
"Type": "Notification",
9+
"MessageId": "d3cece60-336d-5967-844b-9394d3cb44ba",
10+
"TopicArn": "arn:aws:sns:us-east-1:123456789012:ExampleTopic",
11+
"Subject": "EC2 Run Command Notification us-east-1",
12+
"Message": "{\"commandId\":\"6f69316a-e502-5a20-99bc-2408825b6dee\",\"documentName\":\"AWS-RunPatchBaseline\",\"instanceId\":\"i-99999999999999999\",\"requestedDateTime\":\"2023-12-14T10:48:47.127Z\",\"status\":\"Success\",\"detailedStatus\":\"Success\",\"eventTime\":\"2023-12-14T10:52:22.114Z\"}",
13+
"Timestamp": "2023-12-14T10:52:22.173Z",
14+
"SignatureVersion": "1",
15+
"Signature": "EXAMPLE",
16+
"SigningCertUrl": "EXAMPLE",
17+
"UnsubscribeUrl": "EXAMPLE",
18+
"MessageAttributes": {}
19+
}
20+
}
21+
]
22+
}

functions/notify_slack.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class AwsService(Enum):
3131

3232
cloudwatch = "cloudwatch"
3333
guardduty = "guardduty"
34+
systems_manager = "systems-manager"
3435

3536

3637
def decrypt_url(encrypted_url: str) -> str:
@@ -266,6 +267,77 @@ def format_aws_health(message: Dict[str, Any], region: str) -> Dict[str, Any]:
266267
}
267268

268269

270+
class SSMRunCommandStatus(Enum):
271+
"""Maps System Manager Run Command status to Slack message format color"""
272+
273+
Success = "good"
274+
TimedOut = "danger"
275+
Failed = "danger"
276+
277+
278+
def format_ssm_run_command(message: Dict[str, Any], region: str) -> Dict[str, Any]:
279+
"""
280+
Format AWS System Manager Run Document event into Slack message format
281+
282+
:params message: SNS message body containing AWS Health event
283+
:params region: AWS region where the event originated from
284+
:returns: formatted Slack message payload
285+
"""
286+
287+
systems_manager_url = get_service_url(region=region, service="systems_manager")
288+
run_command_url = systems_manager_url.replace(
289+
"/home", f"/run-command/{message.get('commandId')}"
290+
)
291+
292+
return {
293+
"color": SSMRunCommandStatus[message.get("status")].value,
294+
"text": f"EC2 Run Command Notification {region}",
295+
"fallback": f"EC2 Run Command Notification {region}",
296+
"fields": [
297+
{
298+
"title": "Command Id",
299+
"value": f"`{message.get('commandId')}`",
300+
"short": False,
301+
},
302+
{
303+
"title": "Document Name",
304+
"value": f"`{message.get('documentName')}`",
305+
"short": True,
306+
},
307+
{
308+
"title": "Instance Id",
309+
"value": f"`{message.get('instanceId')}`",
310+
"short": True,
311+
},
312+
{
313+
"title": "Requested Time",
314+
"value": f"`{message.get('requestedDateTime')}`",
315+
"short": True,
316+
},
317+
{
318+
"title": "Event Time",
319+
"value": f"`{message.get('eventTime')}`",
320+
"short": True,
321+
},
322+
{
323+
"title": "Status",
324+
"value": f"`{message.get('status')}`",
325+
"short": True,
326+
},
327+
{
328+
"title": "Detailed Status",
329+
"value": f"`{message.get('detailedStatus')}`",
330+
"short": True,
331+
},
332+
{
333+
"title": "Link to Event",
334+
"value": f"{run_command_url}",
335+
"short": False,
336+
},
337+
],
338+
}
339+
340+
269341
def format_default(
270342
message: Union[str, Dict], subject: Optional[str] = None
271343
) -> Dict[str, Any]:
@@ -344,6 +416,10 @@ def get_slack_message_payload(
344416
notification = format_aws_health(message=message, region=message["region"])
345417
attachment = notification
346418

419+
elif "documentName" in message:
420+
notification = format_ssm_run_command(message=message, region=region)
421+
attachment = notification
422+
347423
elif "attachments" in message or "text" in message:
348424
payload = {**payload, **message}
349425

functions/snapshots/snap_notify_slack_test.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from snapshottest import Snapshot
66

7+
78
snapshots = Snapshot()
89

910
snapshots[
@@ -231,6 +232,163 @@
231232
}
232233
]
233234

235+
snapshots[
236+
"test_event_get_slack_message_payload_snapshots event_ssm_run_command_event_failed.json"
237+
] = [
238+
{
239+
"attachments": [
240+
{
241+
"color": "danger",
242+
"fallback": "EC2 Run Command Notification us-east-1",
243+
"fields": [
244+
{
245+
"short": False,
246+
"title": "Command Id",
247+
"value": "`6f69316a-e502-5a20-99bc-2408825b6dee`",
248+
},
249+
{
250+
"short": True,
251+
"title": "Document Name",
252+
"value": "`AWS-RunPatchBaseline`",
253+
},
254+
{
255+
"short": True,
256+
"title": "Instance Id",
257+
"value": "`i-99999999999999999`",
258+
},
259+
{
260+
"short": True,
261+
"title": "Requested Time",
262+
"value": "`2023-12-14T10:48:47.127Z`",
263+
},
264+
{
265+
"short": True,
266+
"title": "Event Time",
267+
"value": "`2023-12-14T10:52:22.114Z`",
268+
},
269+
{"short": True, "title": "Status", "value": "`Failed`"},
270+
{"short": True, "title": "Detailed Status", "value": "`Failed`"},
271+
{
272+
"short": False,
273+
"title": "Link to Event",
274+
"value": "https://console.aws.amazon.com/systems-manager/run-command/6f69316a-e502-5a20-99bc-2408825b6dee?region=us-east-1",
275+
},
276+
],
277+
"text": "EC2 Run Command Notification us-east-1",
278+
}
279+
],
280+
"channel": "slack_testing_sandbox",
281+
"icon_emoji": ":aws:",
282+
"username": "notify_slack_test",
283+
}
284+
]
285+
286+
snapshots[
287+
"test_event_get_slack_message_payload_snapshots event_ssm_run_command_event_success.json"
288+
] = [
289+
{
290+
"attachments": [
291+
{
292+
"color": "good",
293+
"fallback": "EC2 Run Command Notification us-east-1",
294+
"fields": [
295+
{
296+
"short": False,
297+
"title": "Command Id",
298+
"value": "`6f69316a-e502-5a20-99bc-2408825b6dee`",
299+
},
300+
{
301+
"short": True,
302+
"title": "Document Name",
303+
"value": "`AWS-RunPatchBaseline`",
304+
},
305+
{
306+
"short": True,
307+
"title": "Instance Id",
308+
"value": "`i-99999999999999999`",
309+
},
310+
{
311+
"short": True,
312+
"title": "Requested Time",
313+
"value": "`2023-12-14T10:48:47.127Z`",
314+
},
315+
{
316+
"short": True,
317+
"title": "Event Time",
318+
"value": "`2023-12-14T10:52:22.114Z`",
319+
},
320+
{"short": True, "title": "Status", "value": "`Success`"},
321+
{"short": True, "title": "Detailed Status", "value": "`Success`"},
322+
{
323+
"short": False,
324+
"title": "Link to Event",
325+
"value": "https://console.aws.amazon.com/systems-manager/run-command/6f69316a-e502-5a20-99bc-2408825b6dee?region=us-east-1",
326+
},
327+
],
328+
"text": "EC2 Run Command Notification us-east-1",
329+
}
330+
],
331+
"channel": "slack_testing_sandbox",
332+
"icon_emoji": ":aws:",
333+
"username": "notify_slack_test",
334+
}
335+
]
336+
337+
snapshots[
338+
"test_event_get_slack_message_payload_snapshots event_ssm_run_command_event_timedout.json"
339+
] = [
340+
{
341+
"attachments": [
342+
{
343+
"color": "danger",
344+
"fallback": "EC2 Run Command Notification us-east-1",
345+
"fields": [
346+
{
347+
"short": False,
348+
"title": "Command Id",
349+
"value": "`6f69316a-e502-5a20-99bc-2408825b6dee`",
350+
},
351+
{
352+
"short": True,
353+
"title": "Document Name",
354+
"value": "`AWS-RunPatchBaseline`",
355+
},
356+
{
357+
"short": True,
358+
"title": "Instance Id",
359+
"value": "`i-99999999999999999`",
360+
},
361+
{
362+
"short": True,
363+
"title": "Requested Time",
364+
"value": "`2023-12-14T10:48:47.127Z`",
365+
},
366+
{
367+
"short": True,
368+
"title": "Event Time",
369+
"value": "`2023-12-14T10:52:22.114Z`",
370+
},
371+
{"short": True, "title": "Status", "value": "`TimedOut`"},
372+
{
373+
"short": True,
374+
"title": "Detailed Status",
375+
"value": "`DeliveryTimedOut`",
376+
},
377+
{
378+
"short": False,
379+
"title": "Link to Event",
380+
"value": "https://console.aws.amazon.com/systems-manager/run-command/6f69316a-e502-5a20-99bc-2408825b6dee?region=us-east-1",
381+
},
382+
],
383+
"text": "EC2 Run Command Notification us-east-1",
384+
}
385+
],
386+
"channel": "slack_testing_sandbox",
387+
"icon_emoji": ":aws:",
388+
"username": "notify_slack_test",
389+
}
390+
]
391+
234392
snapshots[
235393
"test_sns_get_slack_message_payload_snapshots message_cloudwatch_alarm.json"
236394
] = [
@@ -406,6 +564,57 @@
406564
}
407565
]
408566

567+
snapshots[
568+
"test_sns_get_slack_message_payload_snapshots message_ssm_run_command_event.json"
569+
] = [
570+
{
571+
"attachments": [
572+
{
573+
"color": "good",
574+
"fallback": "EC2 Run Command Notification us-east-1",
575+
"fields": [
576+
{
577+
"short": False,
578+
"title": "Command Id",
579+
"value": "`6f69316a-e502-5a20-99bc-2408825b6dee`",
580+
},
581+
{
582+
"short": True,
583+
"title": "Document Name",
584+
"value": "`AWS-RunPatchBaseline`",
585+
},
586+
{
587+
"short": True,
588+
"title": "Instance Id",
589+
"value": "`i-99999999999999999`",
590+
},
591+
{
592+
"short": True,
593+
"title": "Requested Time",
594+
"value": "`2023-12-14T10:48:47.127Z`",
595+
},
596+
{
597+
"short": True,
598+
"title": "Event Time",
599+
"value": "`2023-12-14T10:52:22.114Z`",
600+
},
601+
{"short": True, "title": "Status", "value": "`Success`"},
602+
{"short": True, "title": "Detailed Status", "value": "`Success`"},
603+
{
604+
"short": False,
605+
"title": "Link to Event",
606+
"value": "https://console.aws.amazon.com/systems-manager/run-command/6f69316a-e502-5a20-99bc-2408825b6dee?region=us-east-1",
607+
},
608+
],
609+
"text": "EC2 Run Command Notification us-east-1",
610+
}
611+
],
612+
"channel": "slack_testing_sandbox",
613+
"icon_emoji": ":aws:",
614+
"username": "notify_slack_test",
615+
}
616+
]
617+
409618
snapshots["test_sns_get_slack_message_payload_snapshots message_text_message.json"] = [
410619
{
411620
"attachments": [

0 commit comments

Comments
 (0)