-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathec2_instance_patching.yaml
More file actions
186 lines (163 loc) · 5.83 KB
/
ec2_instance_patching.yaml
File metadata and controls
186 lines (163 loc) · 5.83 KB
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
---
# Copyright (c) 2024 Thomas Vincent
# SPDX-License-Identifier: MIT
description: Patches EC2 instances with security updates
schemaVersion: '0.3'
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
InstanceIds:
type: StringList
description: (Required) List of EC2 instance IDs to patch.
RebootOption:
type: String
description: (Optional) Whether to reboot instances after patching.
default: RebootIfNeeded
allowedValues:
- RebootIfNeeded
- NoReboot
PatchSeverity:
type: String
description: (Optional) The severity level of patches to apply.
default: Critical,Important
allowedValues:
- Critical
- Important
- Critical,Important
- Critical,Important,Moderate
- All
AutomationAssumeRole:
type: String
description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
default: ""
mainSteps:
- name: VerifyInstancesRunning
action: aws:executeScript
onFailure: Abort
inputs:
Runtime: python3.10
Handler: check_instances
Script: |-
import boto3
def check_instances(events, context):
"""
Verify that all instances are running before attempting to patch.
Args:
events (dict): Input parameters
context: Lambda context (not used)
Returns:
dict: Results of instance verification
"""
ec2 = boto3.client('ec2')
instance_ids = events['InstanceIds']
response = ec2.describe_instances(
InstanceIds=instance_ids
)
verified_instances = []
unverified_instances = []
for reservation in response['Reservations']:
for instance in reservation['Instances']:
instance_id = instance['InstanceId']
state = instance['State']['Name']
if state == 'running':
verified_instances.append(instance_id)
else:
unverified_instances.append({
'InstanceId': instance_id,
'CurrentState': state
})
return {
'VerifiedInstances': verified_instances,
'UnverifiedInstances': unverified_instances
}
InputPayload:
InstanceIds: '{{ InstanceIds }}'
outputs:
- Name: VerifiedInstances
Selector: $.Payload.VerifiedInstances
Type: StringList
- Name: UnverifiedInstances
Selector: $.Payload.UnverifiedInstances
Type: StringMap
- name: PatchInstances
action: aws:runCommand
onFailure: Continue
inputs:
DocumentName: AWS-RunPatchBaseline
InstanceIds: "{{ VerifyInstancesRunning.VerifiedInstances }}"
Parameters:
Operation: Install
RebootOption: "{{ RebootOption }}"
PatchSeverity: "{{ PatchSeverity }}"
- name: VerifyPatching
action: aws:executeScript
inputs:
Runtime: python3.10
Handler: verify_patching
Script: |-
import boto3
import json
from datetime import datetime, timezone
def verify_patching(events, context):
"""
Verify that instances have been successfully patched.
Args:
events (dict): Input parameters
context: Lambda context (not used)
Returns:
dict: Patching verification results
"""
ssm = boto3.client('ssm')
instance_ids = events['InstanceIds']
results = {
'PatchedInstances': [],
'FailedInstances': [],
'SummaryReport': {}
}
current_time = datetime.now(timezone.utc)
for instance_id in instance_ids:
try:
response = ssm.describe_instance_patches(
InstanceId=instance_id
)
patch_states = {}
for patch in response['Patches']:
state = patch.get('State', 'Unknown')
if state not in patch_states:
patch_states[state] = 0
patch_states[state] += 1
# Check if any patches are missing or failed
if patch_states.get('Missing', 0) > 0 or patch_states.get('Failed', 0) > 0:
results['FailedInstances'].append({
'InstanceId': instance_id,
'PatchStates': patch_states
})
else:
results['PatchedInstances'].append({
'InstanceId': instance_id,
'PatchStates': patch_states
})
except Exception as e:
results['FailedInstances'].append({
'InstanceId': instance_id,
'Error': str(e)
})
results['SummaryReport'] = {
'TotalInstances': len(instance_ids),
'SuccessfullyPatched': len(results['PatchedInstances']),
'FailedPatching': len(results['FailedInstances']),
'CompletionTime': current_time.isoformat()
}
return results
InputPayload:
InstanceIds: "{{ VerifyInstancesRunning.VerifiedInstances }}"
outputs:
- Name: PatchedInstances
Selector: $.Payload.PatchedInstances
Type: StringList
- Name: FailedInstances
Selector: $.Payload.FailedInstances
Type: StringList
- Name: SummaryReport
Selector: $.Payload.SummaryReport
Type: StringMap
isEnd: true