Check if EC2 Instances Managed by Systems Manager Are Compliant with Patching Requirements
Overview
This check verifies that EC2 instances managed by AWS Systems Manager (SSM) have all required security patches installed according to your patch baseline. Proper patch management ensures your servers stay protected against known vulnerabilities.
Risk
Unpatched instances are vulnerable to cyberattacks. Even well-designed software cannot anticipate every future threat. Key risks include:
- Remote code execution - Attackers can exploit known vulnerabilities to run malicious code
- Ransomware attacks - Unpatched systems are prime targets for ransomware
- Data breaches - Vulnerabilities may allow unauthorized access to sensitive data
- Compliance violations - Many regulatory frameworks require timely patching
Remediation Steps
Prerequisites
- AWS account access with permissions to use Systems Manager
- EC2 instances must be managed by SSM (SSM Agent installed and IAM role configured)
Setting up SSM Agent on EC2 instances
If your instances are not yet managed by SSM, you need to:
- Ensure SSM Agent is installed - Most Amazon Linux, Ubuntu, and Windows AMIs have it pre-installed
- Attach an IAM instance profile with the
AmazonSSMManagedInstanceCorepolicy - Verify network connectivity - Instances need outbound access to SSM endpoints (or VPC endpoints)
To check if an instance is managed by SSM:
aws ssm describe-instance-information \
--region us-east-1 \
--query "InstanceInformationList[*].[InstanceId,PingStatus]" \
--output table
AWS Console Method
- Open Systems Manager in the AWS Console
- Go to Node Management > Run Command
- Click Run command
- In the search box, find and select AWS-RunPatchBaseline
- Under Command parameters:
- Set Operation to
Install - Set Reboot Option to
RebootIfNeeded(orNoRebootif you prefer manual reboots)
- Set Operation to
- Under Target selection, choose your non-compliant instances:
- Select Choose instances manually and pick the specific instances, OR
- Select Choose instances by tag and use the
PatchGrouptag
- Click Run
- Monitor the command status until it shows Success
For ongoing compliance, set up automated patching:
- Go to Node Management > Patch Manager
- Click Configure patching
- Select your instances or patch groups
- Choose a patching schedule (e.g., weekly maintenance window)
- Select Scan and install for the patching operation
- Complete the wizard to enable automated patching
AWS CLI (optional)
Install patches on a specific instance:
aws ssm send-command \
--region us-east-1 \
--document-name "AWS-RunPatchBaseline" \
--instance-ids "i-0123456789abcdef0" \
--parameters "Operation=Install,RebootOption=RebootIfNeeded"
Install patches on multiple instances by tag:
aws ssm send-command \
--region us-east-1 \
--document-name "AWS-RunPatchBaseline" \
--targets "Key=tag:PatchGroup,Values=production-servers" \
--parameters "Operation=Install,RebootOption=RebootIfNeeded" \
--max-concurrency "50%" \
--max-errors "25%"
Check patch compliance status for instances:
aws ssm describe-instance-patch-states \
--region us-east-1 \
--instance-ids "i-0123456789abcdef0" \
--query "InstancePatchStates[*].[InstanceId,PatchGroup,InstalledCount,MissingCount,FailedCount]" \
--output table
List all patch baselines:
aws ssm describe-patch-baselines \
--region us-east-1 \
--query "BaselineIdentities[*].[BaselineId,BaselineName,OperatingSystem,DefaultBaseline]" \
--output table
CloudFormation (optional)
This template creates a complete patch management setup with a custom patch baseline and automated maintenance window:
AWSTemplateFormatVersion: '2010-09-09'
Description: SSM Patch Manager configuration for EC2 instance patching compliance
Parameters:
PatchGroupName:
Type: String
Default: 'production-servers'
Description: Name of the patch group to target
MaintenanceWindowName:
Type: String
Default: 'weekly-patching-window'
Description: Name for the maintenance window
MaintenanceWindowSchedule:
Type: String
Default: 'cron(0 2 ? * SUN *)'
Description: Cron expression for maintenance window (default - Sundays at 2 AM UTC)
Resources:
PatchBaseline:
Type: AWS::SSM::PatchBaseline
Properties:
Name: !Sub '${AWS::StackName}-patch-baseline'
Description: Custom patch baseline for security updates
OperatingSystem: AMAZON_LINUX_2
PatchGroups:
- !Ref PatchGroupName
ApprovalRules:
PatchRules:
- PatchFilterGroup:
PatchFilters:
- Key: CLASSIFICATION
Values:
- Security
- Key: SEVERITY
Values:
- Critical
- Important
ApproveAfterDays: 7
ComplianceLevel: CRITICAL
EnableNonSecurity: false
- PatchFilterGroup:
PatchFilters:
- Key: CLASSIFICATION
Values:
- Bugfix
ApproveAfterDays: 14
ComplianceLevel: HIGH
EnableNonSecurity: false
MaintenanceWindow:
Type: AWS::SSM::MaintenanceWindow
Properties:
Name: !Ref MaintenanceWindowName
Description: Maintenance window for automated patching
Schedule: !Ref MaintenanceWindowSchedule
Duration: 3
Cutoff: 1
AllowUnassociatedTargets: false
ScheduleTimezone: UTC
MaintenanceWindowTarget:
Type: AWS::SSM::MaintenanceWindowTarget
Properties:
WindowId: !Ref MaintenanceWindow
ResourceType: INSTANCE
Targets:
- Key: tag:PatchGroup
Values:
- !Ref PatchGroupName
Name: !Sub '${AWS::StackName}-patch-target'
Description: Target instances in the patch group
MaintenanceWindowTask:
Type: AWS::SSM::MaintenanceWindowTask
Properties:
WindowId: !Ref MaintenanceWindow
Targets:
- Key: WindowTargetIds
Values:
- !Ref MaintenanceWindowTarget
TaskArn: AWS-RunPatchBaseline
TaskType: RUN_COMMAND
Priority: 1
MaxConcurrency: '50%'
MaxErrors: '25%'
Name: !Sub '${AWS::StackName}-patch-task'
Description: Run patch baseline on target instances
TaskInvocationParameters:
MaintenanceWindowRunCommandParameters:
Parameters:
Operation:
- Install
RebootOption:
- RebootIfNeeded
Outputs:
PatchBaselineId:
Description: ID of the created patch baseline
Value: !Ref PatchBaseline
MaintenanceWindowId:
Description: ID of the maintenance window
Value: !Ref MaintenanceWindow
Deploy the stack:
aws cloudformation deploy \
--region us-east-1 \
--template-file patch-manager.yaml \
--stack-name ssm-patch-management \
--parameter-overrides PatchGroupName=production-servers
Important: Tag your EC2 instances with PatchGroup=production-servers (or your chosen group name) for them to be included in automated patching.
Terraform (optional)
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}
variable "patch_group_name" {
description = "Name of the patch group to target"
type = string
default = "production-servers"
}
variable "maintenance_window_schedule" {
description = "Cron expression for maintenance window"
type = string
default = "cron(0 2 ? * SUN *)"
}
variable "operating_system" {
description = "Operating system for the patch baseline"
type = string
default = "AMAZON_LINUX_2"
}
# Patch Baseline
resource "aws_ssm_patch_baseline" "this" {
name = "security-patch-baseline"
description = "Patch baseline for security updates"
operating_system = var.operating_system
approval_rule {
approve_after_days = 7
compliance_level = "CRITICAL"
patch_filter {
key = "CLASSIFICATION"
values = ["Security"]
}
patch_filter {
key = "SEVERITY"
values = ["Critical", "Important"]
}
}
approval_rule {
approve_after_days = 14
compliance_level = "HIGH"
patch_filter {
key = "CLASSIFICATION"
values = ["Bugfix"]
}
}
}
# Register patch group with baseline
resource "aws_ssm_patch_group" "this" {
baseline_id = aws_ssm_patch_baseline.this.id
patch_group = var.patch_group_name
}
# Maintenance Window
resource "aws_ssm_maintenance_window" "this" {
name = "patching-maintenance-window"
description = "Maintenance window for automated patching"
schedule = var.maintenance_window_schedule
duration = 3
cutoff = 1
schedule_timezone = "UTC"
}
# Maintenance Window Target
resource "aws_ssm_maintenance_window_target" "this" {
window_id = aws_ssm_maintenance_window.this.id
resource_type = "INSTANCE"
name = "patch-target"
description = "Target instances with PatchGroup tag"
targets {
key = "tag:PatchGroup"
values = [var.patch_group_name]
}
}
# Maintenance Window Task
resource "aws_ssm_maintenance_window_task" "this" {
window_id = aws_ssm_maintenance_window.this.id
task_type = "RUN_COMMAND"
task_arn = "AWS-RunPatchBaseline"
priority = 1
max_concurrency = "50%"
max_errors = "25%"
name = "run-patch-baseline"
description = "Run AWS-RunPatchBaseline on target instances"
targets {
key = "WindowTargetIds"
values = [aws_ssm_maintenance_window_target.this.id]
}
task_invocation_parameters {
run_command_parameters {
parameter {
name = "Operation"
values = ["Install"]
}
parameter {
name = "RebootOption"
values = ["RebootIfNeeded"]
}
}
}
}
output "patch_baseline_id" {
description = "ID of the patch baseline"
value = aws_ssm_patch_baseline.this.id
}
output "maintenance_window_id" {
description = "ID of the maintenance window"
value = aws_ssm_maintenance_window.this.id
}
Deploy:
terraform init
terraform apply -var="patch_group_name=production-servers"
Important: Tag your EC2 instances with PatchGroup=production-servers for them to be targeted by the maintenance window.
Verification
After running the patching operation:
- Go to Systems Manager > Node Management > Compliance
- Check that your instances show Compliant status for patch compliance
- Click on an instance to see detailed patch information
CLI verification commands
Check patch compliance for specific instances:
aws ssm describe-instance-patch-states \
--region us-east-1 \
--instance-ids "i-0123456789abcdef0" \
--query "InstancePatchStates[*].[InstanceId,PatchGroup,InstalledCount,MissingCount,FailedCount,OperationEndTime]" \
--output table
List non-compliant instances:
aws ssm list-compliance-items \
--region us-east-1 \
--filters "Key=ComplianceType,Values=Patch,Type=EQUAL" "Key=Status,Values=NON_COMPLIANT,Type=EQUAL" \
--query "ComplianceItems[*].[ResourceId,Status,Severity]" \
--output table
View detailed patch information for an instance:
aws ssm describe-instance-patches \
--region us-east-1 \
--instance-id "i-0123456789abcdef0" \
--filters "Key=State,Values=Missing" \
--query "Patches[*].[Title,Severity,State]" \
--output table
Additional Resources
- AWS Systems Manager Patch Manager
- Working with patch baselines
- Working with maintenance windows
- Integrating Patch Manager with AWS Security Hub
Notes
- Reboot consideration: Some patches require a reboot to take effect. Plan patching during maintenance windows to minimize disruption.
- Test first: Consider using a staged rollout - patch development/test environments first, then production.
- Multiple operating systems: You need separate patch baselines for different operating systems (Amazon Linux, Windows, Ubuntu, etc.).
- Security Hub integration: Enable Patch Manager integration with AWS Security Hub to centralize security findings across your organization.
- Compliance frameworks: This check maps to multiple compliance frameworks including PCI-DSS, HIPAA, SOC2, NIST 800-53, and FedRAMP. Maintaining patch compliance helps satisfy these regulatory requirements.