Windows patching is an unfortunate requirement for all Windows administrators. Patch Tuesdays keep coming with no end in sight. Many people use Windows Server Update Services (WSUS) to keep their Windows servers patched; it's free and works for the most part. The problem is it's yet another server you have to manage. It also usually depends on Active Directory and relies on GPOs to configure the servers to point to WSUS. Using AWS's Systems Manager alleviates these and many other issues admins have with WSUS.

AWS Systems Manager or SSM is a free service provided by AWS allows you to manage AWS EC2 instances as well as on-premises nodes through a lightweight agent. For patching, it brings together both EC2 and on-premises nodes so you can keep everything patched in one console.

In this article, let's go over how to set it setup and running on your first Windows server. To do that, you'll first need to ensure you have the agent already setup. You can refer to the AWS documentation for instructions on how to make that happen. Once you've got an SSM agent setup on your Windows servers you're then ready to setup patching.

Creating a Patch Baseline

We'll first create a patch baseline. The patch baseline allows you to set which kinds of patches get installed on your servers. Patch baselines allow you to set different criteria depending on the category, severity and so on of the various patches.

In this article, I'll be creating a baseline that will only be for Windows servers that will only have critical updates with an auto-approval of two days. Let's make this happen in PowerShell.

Unfortunately, the PowerShell code to do this isn't exactly pretty but this is what I'm here for. Simply copy this out and run it! The code below creates our baseline with the name of Critical-Updates.

$autoApproveInDays = 7

$rule = New-Object Amazon.SimpleSystemsManagement.Model.PatchRule
$rule.ApproveAfterDays = $autoApproveInDays

$ruleFilters = New-Object Amazon.SimpleSystemsManagement.Model.PatchFilterGroup
$patchFilter = New-Object Amazon.SimpleSystemsManagement.Model.PatchFilter

$severityFilter = New-Object Amazon.SimpleSystemsManagement.Model.PatchFilter
$severityFilter.Key = 'MSRC_SEVERITY'
$severityFilter.Values.Add('Critical')

$classificationFilter = New-Object Amazon.SimpleSystemsManagement.Model.PatchFilter
$classificationFilter.Key = 'CLASSIFICATION'
$classificationFilter.Values.Add( 'CriticalUpdates' )

$ruleFilters.PatchFilters.Add($severityFilter)
$ruleFilters.PatchFilters.Add($classificationFilter)
$rule.PatchFilterGroup = $ruleFilters

New-SSMPatchBaseline -Name 'Critical-Updates' -Description 'Baseline containing all critical update' -ApprovalRules_PatchRule $rule

pb-07fadfc632e491110

Adding Managed Instances to Patch Groups

Next, we can add the instances to patch groups. Creating patch groups allows you to deliver different patches to different instances more easily. To use patch groups, we need to tag our instances. For this example, perhaps I have a server that's in production so I'll assign it to a patch group called Production.

I first need to get the managed instance ID, and I can then use that to assign the tag.

PS C:\Users\adam> Get-SSMInstanceInformation

ActivationId : 1c6f3fac-0cdd-4d9e-a32e-c7cae4231495
AgentVersion : 2.2.493.0
AssociationOverview :
AssociationStatus :
ComputerName : SRV1.WORKGROUP
IamRole : SSMServiceRole
InstanceId : mi-07fae420a558c8281
IPAddress : 10.0.0.9
IsLatestVersion : True
LastAssociationExecutionDate : 1/1/0001 12:00:00 AM
LastPingDateTime : 4/27/2018 5:01:07 PM
LastSuccessfulAssociationExecutionDate : 1/1/0001 12:00:00 AM
Name : SRV1
PingStatus : Online
PlatformName : Microsoft Windows Server 2016 Datacenter
PlatformType : Windows
PlatformVersion : 10.0.14393
RegistrationDate : 4/27/2018 4:53:27 PM
ResourceType : ManagedInstance

PS> $tag = New-Object Amazon.SimpleSystemsManagement.Model.Tag
>> $tag.Key = 'Patch Group'
>> $tag.Value = 'Production'
>> Add-SSMResourceTag -ResourceType 'ManagedInstance' -ResourceId 'mi-07fae420a558c8281' -Tag $tag

Creating a Maintenance Window

A maintenance window is required to install patches via SSM. To do that, we'll need an IAM role to control access to the maintenance window. I'll create one with PowerShell using the below JSON file which I'll call C:\MaintenanceWindowsRole.json.

{
	"Version":"2012-10-17",
	"Statement":[
		{
			"Effect":"Allow",
			"Principal":{
			"Service":[
				"ssm.amazonaws.com",
				"ec2.amazonaws.com",
				"sns.amazonaws.com"
			]
		},
		"Action":"sts:AssumeRole"
		}
	]
}

Next, I'll create the role. I can do this a number of different ways but I'm most comfortable with PowerShell so I'll use that.

PS> New-IAMRole -RoleName 'mw-task-role' -AssumeRolePolicyDocument (Get-Content -Raw C:\MaintenanceWindowRole.json)


Next, we need to create a maintenance window. I'll use the New-SSMMaintenanceWindow command to do that. Below I'm creating a maintenance window that triggers every Tuesday at 4PM for four hours that cuts off any new tasks one hour before the maintenance window expires.

PS> New-SSMMaintenanceWindow -Name 'EveryTuesday' -Duration 4 -Cutoff 1 -Schedule 'cron(0 16 ? * TUE *)'

Registering the Patch Group with the Maintenance Window

Once we've got the maintenance window created, we need to associate the patch group with the maintenance window.

PS> Get-SSMMaintenanceWindowList

Cutoff : 1
Description :
Duration : 4
Enabled : True
Name : EveryTuesday
WindowId : mw-01d06df5638742bb4

PS> $target = @{Key="tag:Patch Group";[email protected]("Production")}

Registering the Install Task with the Maintenance Window

Once we've got the patch group associated with the maintenance window, we can setup a task to scan for and install the necessary updates.

PS> $maintenanceWindowId = (Get-SSMMaintenanceWindowList | Where-Object {$_.Name -eq 'EveryTuesday'}).WindowId
PS> $windowTargetId = (Get-SSMMaintenanceWindowTarget -WindowId $maintenanceWindowId).WindowTargetId
PS> $windowRoleArn = (Get-IAMRole -RoleName mw-task-role).Arn
PS> $parameters = @{}
PS> $parameterValues = New-Object Amazon.SimpleSystemsManagement.Model.MaintenanceWindowTaskParameterValueExpression
PS> $parameterValues.Values = @("Install")
PS> $parameters.Add("Operation", $parameterValues)
PS> Register-SSMTaskWithMaintenanceWindow -WindowId $maintenanceWindowId -TaskArn 'AWS-ApplyPatchBaseline' -Target @{ Key="WindowTargetIds";Values=$windowTargetId } -TaskType "RUN_COMMAND" -TaskParameter $parameters -ServiceRoleArn $windowRoleArn -MaxConcurrency 1 -MaxError 1

Summary

Once you've got the install task registered, you're done! SSM can seem daunting at first especially if you don't have a lot of AWS experience but hopefully with this tutorial, you'll now be able to copy/paste some of this PowerShell code to give you a jump start.

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!