How To Create AWS SSM Maintenance Windows
Hello Everyone
Welcome to CloudAffaire and this is Debjeet.
In the last blog post, we have discussed how to create AWS SSM Automation workflow.
https://cloudaffaire.com/how-to-create-aws-ssm-automation-workflow/
In this blog post, we will discuss how to create AWS SSM Maintenance Windows.
What Is AWS SSM Maintenance Windows:
AWS Systems Manager Maintenance Windows let you define a schedule for when to perform potentially disruptive actions on your instances such as patching an operating system, updating drivers, or installing software or patches. Maintenance Windows also lets you schedule actions on numerous other AWS resource types, such as S3 buckets, SQS queues, KMS keys, and many more.
Each maintenance window has a schedule, a maximum duration, a set of registered targets, and a set of registered tasks. You can add tags to your maintenance windows when you create or update them. You can also specify dates that a maintenance window should not run before or after, and you can specify the international time zone on which to base the maintenance window schedule.
SSM Maintenance Windows Schedule:
When you create an AWS Systems Manager maintenance window or a State Manager association, you specify a schedule for when the window or the association should run. You can specify a schedule as either a time-based entry, called a cron expression, or a frequency-based entry, called a rate expression.
Cron Expression:
Cron expressions for Systems Manager have six required fields. A seventh field, the Seconds field (the first in a cron expression), is optional. Fields are separated by a space.
cron(Seconds Minutes Hours Day_of_month Month Day_of_week Year)
Supported Values:
Field | Values | Wildcards |
Minutes | 0-59 | , – * / |
Hours | 0-23 | , – * / |
Day_of_month | 1-31 | , – * ? / L W |
Month | 1-12 or JAN-DEC | , – * / |
Day_of_week | 1-7 or SUN-SAT | , – * ? / L |
Year | 1970-2199 | , – * / |
Supported Wildcards:
Wildcard | Description |
, | The , (comma) wildcard includes additional values. In the Month field, JAN,FEB,MAR would include January, February, and March. |
– | The – (dash) wildcard specifies ranges. In the Day field, 1-15 would include days 1 through 15 of the specified month. |
* | The * (asterisk) wildcard includes all values in the field. In the Hours field, * would include every hour. |
/ | The / (forward slash) wildcard specifies increments. In the Minutes field, you could enter 1/10 to specify every tenth minute, starting from the first minute of the hour. So 1/10 specifies the first, 11th, 21st, and 31st minute, and so on. |
? | The ? (question mark) wildcard specifies one or another. In the Day-of-month field you could enter 7 and if you didn’t care what day of the week the 7th was, you could enter ? in the Day-of-week field. |
L | The L wildcard in the Day-of-month or Day-of-week fields specifies the last day of the month or week. |
W | The W wildcard in the Day-of-month field specifies a weekday. In the Day-of-month field, 3W specifies the day closest to the third weekday of the month. |
Note: You cannot specify a value in the Day-of-month and in the Day-of-week fields in the same cron expression. The time is in UTC format.
Rate Expression:
Rate expressions have two fields, Value, and Unit. The value field is any positive number and the Unit field is either in minutes or hours or days.
rate(Value Unit)
Supported Values:
Field | Values |
Value | positive number, such as 1 or 15 |
Unit | minute |
minutes | |
hour | |
hours | |
day | |
days |
AWS SSM Maintenance Window Registered Task:
Tasks are the actions (Registered Tasks) performed on a resource (Registered Targets) during a maintenance window (Schedule) execution. Once you create a maintenance window by defining a schedule, you need to register one task to the maintenance windows which defined the actual action the maintenance will perform during its schedule execution. AWS SSM Maintenance Windows support below four type of tasks
- Systems Manager Run Command commands
- Systems Manager Automation workflows
- AWS Lambda functions
- AWS Step Functions tasks
AWS SSM Maintenance Window Registered Target:
Apart from a schedule and a task, you also need to define the target for your maintenance windows. The maintenance windows target can be an EC2 instance or an AWS Resource Group. You can register a target to the maintenance window by specifying your instance tags or selecting your instance manually or selecting a resource group.
How To Create AWS SSM Maintenance Windows:
Requirements:
AWS CLI installed and configured. You can follow the below blog post to install and configure AWS CLI.
https://cloudaffaire.com/how-to-install-aws-cli/
https://cloudaffaire.com/how-to-configure-aws-cli/
Step 1: Create an EC2 SSM Managed Instance.
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 |
############################################### ## How To Create AWS SSM Maintenance Windows ## ############################################### ## -------------------------------------- ## Create An AWS EC2 SSM Managed Instance ## -------------------------------------- ## Create a directory for this demo mkdir ssmdemo && cd ssmdemo ## Create a VPC AWS_VPC_ID=$(aws ec2 create-vpc \ --cidr-block 10.0.0.0/16 \ --query 'Vpc.{VpcId:VpcId}' \ --output text) ## Enable DNS hostname for your VPC aws ec2 modify-vpc-attribute \ --vpc-id $AWS_VPC_ID \ --enable-dns-hostnames "{\"Value\":true}" ## Create a public subnet AWS_SUBNET_PUBLIC_ID=$(aws ec2 create-subnet \ --vpc-id $AWS_VPC_ID --cidr-block 10.0.1.0/24 \ --availability-zone ap-south-1a --query 'Subnet.{SubnetId:SubnetId}' \ --output text) ## Enable Auto-assign Public IP on Public Subnet aws ec2 modify-subnet-attribute \ --subnet-id $AWS_SUBNET_PUBLIC_ID \ --map-public-ip-on-launch ## Create an Internet Gateway AWS_INTERNET_GATEWAY_ID=$(aws ec2 create-internet-gateway \ --query 'InternetGateway.{InternetGatewayId:InternetGatewayId}' \ --output text) ## Attach Internet gateway to your VPC aws ec2 attach-internet-gateway \ --vpc-id $AWS_VPC_ID \ --internet-gateway-id $AWS_INTERNET_GATEWAY_ID ## Create a route table AWS_CUSTOM_ROUTE_TABLE_ID=$(aws ec2 create-route-table \ --vpc-id $AWS_VPC_ID \ --query 'RouteTable.{RouteTableId:RouteTableId}' \ --output text ) ## Create route to Internet Gateway aws ec2 create-route \ --route-table-id $AWS_CUSTOM_ROUTE_TABLE_ID \ --destination-cidr-block 0.0.0.0/0 \ --gateway-id $AWS_INTERNET_GATEWAY_ID ## Associate the public subnet with route table AWS_ROUTE_TABLE_ASSOID=$(aws ec2 associate-route-table \ --subnet-id $AWS_SUBNET_PUBLIC_ID \ --route-table-id $AWS_CUSTOM_ROUTE_TABLE_ID \ --output text) ## Create a security group aws ec2 create-security-group \ --vpc-id $AWS_VPC_ID \ --group-name myvpc-security-group \ --description 'My VPC non default security group' ## Get security group ID's AWS_DEFAULT_SECURITY_GROUP_ID=$(aws ec2 describe-security-groups \ --filters "Name=vpc-id,Values=$AWS_VPC_ID" \ --query 'SecurityGroups[?GroupName == `default`].GroupId' \ --output text) && AWS_CUSTOM_SECURITY_GROUP_ID=$(aws ec2 describe-security-groups \ --filters "Name=vpc-id,Values=$AWS_VPC_ID" \ --query 'SecurityGroups[?GroupName == `myvpc-security-group`].GroupId' \ --output text) ## Create security group ingress rules aws ec2 authorize-security-group-ingress \ --group-id $AWS_CUSTOM_SECURITY_GROUP_ID \ --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 22, "ToPort": 22, "IpRanges": [{"CidrIp": "0.0.0.0/0", "Description": "Allow SSH"}]}]' && aws ec2 authorize-security-group-ingress \ --group-id $AWS_CUSTOM_SECURITY_GROUP_ID \ --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "IpRanges": [{"CidrIp": "0.0.0.0/0", "Description": "Allow HTTP"}]}]' ## Get Amazon Linux 2 latest AMI ID AWS_AMI_ID=$(aws ec2 describe-images \ --owners 'amazon' \ --filters 'Name=name,Values=amzn2-ami-hvm-2.0.????????-x86_64-gp2' 'Name=state,Values=available' \ --query 'sort_by(Images, &CreationDate)[-1].[ImageId]' \ --output 'text') ## Create a key-pair aws ec2 create-key-pair \ --key-name myvpc-keypair \ --query 'KeyMaterial' \ --output text > myvpc-keypair.pem ## Create user data for a LAMP stack cat < #!/bin/bash sudo yum update -y sudo systemctl enable amazon-ssm-agent sudo systemctl start amazon-ssm-agent sudo systemctl status amazon-ssm-agent EOF ## Create an EC2 instance AWS_EC2_INSTANCE_ID=$(aws ec2 run-instances \ --image-id $AWS_AMI_ID \ --instance-type t2.micro \ --key-name myvpc-keypair \ --monitoring "Enabled=false" \ --security-group-ids $AWS_CUSTOM_SECURITY_GROUP_ID \ --subnet-id $AWS_SUBNET_PUBLIC_ID \ --user-data file://myuserdata.txt \ --private-ip-address 10.0.1.10 \ --query 'Instances[0].InstanceId' \ --output text) ## Create an Instance profile for SSM aws iam create-instance-profile \ --instance-profile-name "AmazonSSMInstanceProfileForInstances" ## Create a trust relation json file cat < { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF ## Create an IAM role for SSM aws iam create-role \ --role-name "AmazonSSMRoleForInstances" \ --assume-role-policy-document file://trust_policy.json ## Attach the required policy for SSM aws iam attach-role-policy \ --role-name "AmazonSSMRoleForInstances" \ --policy-arn "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" ## Add the role to the instance profile aws iam add-role-to-instance-profile \ --instance-profile-name "AmazonSSMInstanceProfileForInstances" \ --role-name "AmazonSSMRoleForInstances" ## Attach the Instance Profile to the EC2 instance aws ec2 associate-iam-instance-profile \ --instance-id "$AWS_EC2_INSTANCE_ID" \ --iam-instance-profile "Name=AmazonSSMInstanceProfileForInstances" ## Add a tag to the ec2 instance aws ec2 create-tags \ --resources $AWS_EC2_INSTANCE_ID \ --tags "Key=Name,Value=MYAPP" "Key=SSMTAG,Values=MYAPPinstance" |
Step 2: Create a new SSM Maintenance Window.
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 |
## ---------------------------------- ## Create AWS SSM Maintenance Windows ## ---------------------------------- ## create a new maintenence window AWS_SSM_MW_ID=$(aws ssm create-maintenance-window \ --name "MyMaintenanceWindow" \ --description "Maintenance Windows Created Through AWS CLI" \ --schedule "cron(0 0 0 ? * MON *)" \ --duration 5 --cutoff 1 \ --allow-unassociated-targets \ --tags "Key=Name,Value=MYAPP" \ --query 'WindowId' \ --output text) ## get details of all maintenance windows aws ssm describe-maintenance-windows ## get details of a particular maintenance window aws ssm describe-maintenance-windows \ --filters "Key=Name,Values=MyMaintenanceWindow" ## get details of a maintenance window schedule aws ssm describe-maintenance-window-schedule \ --window-id "$AWS_SSM_MW_ID" |
Step 3: Register a new target to the SSM Maintenance Window.
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 |
## ------------------------------------------------------ ## Register a target instance with the maintenance window ## ------------------------------------------------------ ## register a target by tag AWS_SSM_MW_TARGET_ID=$(aws ssm register-target-with-maintenance-window \ --window-id "$AWS_SSM_MW_ID" \ --resource-type "INSTANCE" \ --target "Key=tag:SSMTAG,Values=MYAPPinstance" \ --query 'WindowTargetId' \ --output text) ## register a target by instance-id ## AWS_SSM_MW_TARGET_ID=$(aws ssm register-target-with-maintenance-window \ ## --window-id "$AWS_SSM_MW_ID" \ ## --resource-type "INSTANCE" \ ## --target "Key=InstanceIds,Values=$AWS_EC2_INSTANCE_ID" \ ## --query 'WindowTargetId' \ ## --output text) ## Lists the targets registered with the maintenance window. aws ssm describe-maintenance-window-targets \ --window-id "$AWS_SSM_MW_ID" ## Get information about the maintenance window targets an instance is associated with. aws ssm describe-maintenance-windows-for-target \ --targets "Key=tag:SSMTAG,Values=MYAPPinstance" \ --resource-type "INSTANCE" |
Step 4: Register a new task for the SSM Maintenance Window.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
## ------------------------------------------- ## Register a task with the maintenance window ## ------------------------------------------- ## register a task for the maintenance window AWS_SSM_MW_TASK_ID=$(aws ssm register-task-with-maintenance-window \ --window-id "$AWS_SSM_MW_ID" \ --task-arn "AWS-RunShellScript" \ --max-concurrency 1 --max-errors 1 \ --priority 10 \ --targets "Key=WindowTargetIds,Values=$AWS_SSM_MW_TARGET_ID" \ --task-type "RUN_COMMAND" \ --task-invocation-parameters '{"RunCommand":{"Parameters":{"commands":["df"]}}}' \ --query 'WindowTaskId' \ --output text) ## Lists the tasks in a maintenance window. aws ssm describe-maintenance-window-tasks \ --window-id "$AWS_SSM_MW_ID" ## Get maintenance window task details aws ssm get-maintenance-window-task \ --window-id "$AWS_SSM_MW_ID" \ --window-task-id "$AWS_SSM_MW_TASK_ID" |
Step 5: Update the SSM Maintenance Window Schedule.
1 2 3 4 5 6 7 8 9 10 |
## ----------------------------- ## Update the maintenance window ## ----------------------------- ## update the maintenance window (every 5 minutes) aws ssm update-maintenance-window \ --window-id "$AWS_SSM_MW_ID" \ --schedule "rate(5 minutes)" ## Wait for 5 minutes to get the execution data |
Step 6: Get the Maintenance Window execution data.
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 |
## ------------------------------------- ## Get maintenance window execution data ## ------------------------------------- ## Get execution data aws ssm describe-maintenance-window-executions \ --window-id "$AWS_SSM_MW_ID" ## Get execution details AWS_SSM_MW_EXE_ID=$(aws ssm describe-maintenance-window-executions \ --window-id "$AWS_SSM_MW_ID" \ --query 'WindowExecutions[0].{WindowExecutionId:WindowExecutionId}' \ --output text) && aws ssm describe-maintenance-window-execution-tasks \ --window-execution-id "$AWS_SSM_MW_EXE_ID" ## Get details of maintenance window execution AWS_SSM_MW_EXE_TASK_ID=$(aws ssm get-maintenance-window-execution \ --window-execution-id "$AWS_SSM_MW_EXE_ID" \ --query 'TaskIds[0]' \ --output text) && aws ssm describe-maintenance-window-execution-task-invocations \ --window-execution-id "$AWS_SSM_MW_EXE_ID" \ --task-id "$AWS_SSM_MW_EXE_TASK_ID" ## Get details about a specific task run as part of a maintenance window execution. aws ssm get-maintenance-window-execution-task \ --window-execution-id "$AWS_SSM_MW_EXE_ID" \ --task-id "$AWS_SSM_MW_EXE_TASK_ID" |
Step 7: Cleanup.
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 |
## ------- ## Cleanup ## ------- ## deregister the maintenance window task aws ssm deregister-task-from-maintenance-window \ --window-id "$AWS_SSM_MW_ID" \ --window-task-id "$AWS_SSM_MW_TASK_ID" ## deregister the maintenance window target aws ssm deregister-target-from-maintenance-window \ --window-id "$AWS_SSM_MW_ID" \ --window-target-id "$AWS_SSM_MW_TARGET_ID" ## delete the maintenence window aws ssm delete-maintenance-window \ --window-id "$AWS_SSM_MW_ID" ## Terminate the ec2 instance aws ec2 terminate-instances \ --instance-ids $AWS_EC2_INSTANCE_ID && rm -f myuserdata.txt ## wait for some time sleep 30 ## Delete key pair aws ec2 delete-key-pair \ --key-name myvpc-keypair && rm -f myvpc-keypair.pem ## Delete custom security group aws ec2 delete-security-group \ --group-id $AWS_CUSTOM_SECURITY_GROUP_ID ## Delete internet gateway aws ec2 detach-internet-gateway \ --internet-gateway-id $AWS_INTERNET_GATEWAY_ID \ --vpc-id $AWS_VPC_ID && aws ec2 delete-internet-gateway \ --internet-gateway-id $AWS_INTERNET_GATEWAY_ID ## Delete the custom route table aws ec2 disassociate-route-table \ --association-id $AWS_ROUTE_TABLE_ASSOID && aws ec2 delete-route-table \ --route-table-id $AWS_CUSTOM_ROUTE_TABLE_ID ## Delete the public subnet aws ec2 delete-subnet \ --subnet-id $AWS_SUBNET_PUBLIC_ID ## Delete the vpc aws ec2 delete-vpc \ --vpc-id $AWS_VPC_ID ## Remove the IAM role from Instance Profile aws iam remove-role-from-instance-profile \ --instance-profile-name "AmazonSSMInstanceProfileForInstances" \ --role-name "AmazonSSMRoleForInstances" ## Dettach policy from the role aws iam detach-role-policy \ --role-name "AmazonSSMRoleForInstances" \ --policy-arn "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" ## Delete the IAM role aws iam delete-role \ --role-name "AmazonSSMRoleForInstances" ## Delete the IAM Instance Profile aws iam delete-instance-profile \ --instance-profile-name "AmazonSSMInstanceProfileForInstances" ## Delete the directory created for this demo cd .. && rm -rf ssmdemo |
Hope you have enjoyed this blog post, to get more details on AWS SSM, please refer below AWS documentation
https://docs.aws.amazon.com/systems-manager/index.html