Discover in this blog post how to keep your Windows Servers Patched with AWS Systems Manager. Learn about best practices and deployment using PowerShell. Patching is one of the biggest pains for system administrators and one of the most important. Patching is like backups: nothing happens when you do them, but you’ll be glad to have them when you need them the most! There’s nothing worse than telling your CEO a worm broke out on your network because your team wasn’t keeping the servers patched.
How do organizations keep Windows servers patched anyway?
Most of them are probably familiar with Windows Server Update Services (WSUS), which is a free product provided by Microsoft. However, WSUS requires a fair amount of configuration: You need to setup a new server (if not sharing an existing one), install the feature, define and download patches etc. On top of that, you need to set all the clients to point to the WSUS server, either through a GPO (Group Policy Object) or directly via the registry. WSUS is yet another on-prem service to manage, but what if you could have WSUS functionality without all the fuss? It is possible with AWS Systems Manager (SSM) service, so keep reading to find out how to do it!
1. Install the AWS Systems Manager (SSM) agent
First off, there’s a bit of setup you’re going to have to do to get SSM up and running. Once you’ve got SSM set up and the SSM agent() installed on all of the Windows servers you’d like to patch, you’ve made some great progress, but you’ve got a little ways to go yet.
2. Define Maintenance Windows
SSM only installs patches during maintenance windows, and you must define one before starting on this journey. To do that, you first need to create an IAM role in controlling access to maintenance windows. I’ll start by creating a IAM policy via JSON and saving it to a text file C:\MaintenanceWindowsRole.json
.
```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 in different ways (I’m most comfortable with PowerShell so I’ll use that).
```PowerShell PS C:\Users\adam> New-IAMRole -RoleName 'mw-task-role' -AssumeRolePolicyDocument (Get-Content -Raw C:\MaintenanceWindowRole.json) Path RoleName RoleId CreateDate Description ---- -------- ------ ---------- ----------- / mw-task-role AROAIBV5ZRXZCOXC535PQ 4/27/2018 4:27:14 PM ```
3. Create a Patch Baseline
Once you’ve got the IAM role created for managing maintenance windows, the next step is to decide on a patch baseline. A patch baseline defines the types of patches you will be installing across your servers. You’ve got a lot of options here and will wildly vary based on your needs. Patch baselines allow you to define different patches that go to different servers based on various criteria. In our example, I’m creating a baseline only for Windows servers that have critical updates with an auto-approval rule of two days. I could do this via the AWS Systems Manager (SSM) console, but it’s easier to code so I can easily repeat it if needed. The PowerShell code for creating a baseline isn’t very intuitive, but you can simply execute the script to run it again once you’ve got the script created using the code below. The code will create our baseline with the Critical-Updates name.
```PowerShell $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 ```
4. Add the Managed Instances to Patch Groups
An optional, yet recommended step is to add your instances to patch groups. This gives you more granular control over which patches are delivered and installed to which servers. For AWS Systems Manager (SSM) to understand there’s a patch group, it must be defined with the tag key: Patch Group (which is case sensitive!). Let’ go ahead and add a tag to our managed instance that we added previously, called SRV1. Let’s say this server is in production, so I’ll assign it to a patch group called Production. To assign the tag, I first need to get the managed instance ID.
```PowerShell 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 ```
5. Create a Maintenance Window
Next step is to create a maintenance window that states the time when patches can be installed on our servers. To do that, we can use the `New-SSMMaintenanceWindow
` command. In the example below, I’m creating a maintenance window that occurs every Tuesday at 4 PM for four hours and cuts off any new tasks one hour before the maintenance window expires.
```PowerShell PS> New-SSMMaintenanceWindow -Name 'EveryTuesday' -Duration 4 -Cutoff 1 -Schedule 'cron(0 16 ? * TUE *)' ```
Register the Patch Group with the Maintenance Window
Now we need to associate the patch group with the maintenance window to specify which maintenance window is used by the instances in the group.
```PowerShell ## Find the maintenance window ID PS> Get-SSMMaintenanceWindowList Cutoff : 1 Description : Duration : 4 Enabled : True Name : EveryTuesday WindowId : mw-01d06df5638742bb4 ## Build the target query PS> $target = @{Key="tag:Patch Group";Values=@("Production")} ## Register the target PS> Register-SSMTargetWithMaintenanceWindow -WindowId 'mw-01d06df5638742bb4' -Target $target -ResourceType INSTANCE 70c354c1-3d71-4626-9742-8d962a9ce568 ```
Register the Install Task with the Maintenance Window
Finally, we need to set up an install task to scan and install all patches coming from the patch baseline we built. This baseline will be attached to the maintenance windows we just created. This section requires a bit of code preparation and execution but feel free to copy/paste!
```PowerShell 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 4999e319-b6cd-49f3-a526-23fe08c1f1cd ```
Summary
In this article, I covered a simple scenario of setting up the AWS Systems Manager (SSM) Patch Manager service to install patches to a Windows server on a regular basis. We performed all the configurations via PowerShell, but you can also use AWS CLI and the console to perform these tasks. This may seem a daunting process at first, due to all the various objects we had to manipulate, but I encourage you to copy the code described here and create your own scripts from it. You’ll see that once you have the base code working, you’ll be able to configure it much easier later.
Declan is a Channel & Alliance rep for N2WS. When he's not helping customers optimize their cloud environments and writing easy-to-understand technical content, you can find him spending time on the golf course, improving his game.