This article use AWS Auto Scaling Group (ASG) custom termination policies to consistently terminate the oldest instance, addressing limitations in default ASG settings where instance distribution across Availability Zones can override the oldest termination logic. It presents a solution integrating a Lambda function with CloudFormation for streamlined deployment and operational efficiency, ensuring predictable and manageable scaling activities.
Background:
AWS Auto Scaling Groups (ASGs) offer various termination policies, including the OldestInstance policy, designed to retire the oldest instances first. However, ASGs prioritize an equal distribution of instances across Availability Zones (AZs). This behavior can sometimes lead to newer instances in one AZ being terminated before older instances in another AZ.
Needs:
Customers often require a more predictable scaling and resource management approach that consistently terminates the oldest instance, regardless of AZ distribution.
Solution:
To meet this need, a custom Lambda function was developed to implement a bespoke termination policy. This function leverages the EC2 DescribeInstances API to accurately identify the oldest instance in an ASG. Deployment is streamlined through a CloudFormation template, which automatically sets up the Lambda function and necessary IAM permissions, simplifying integration of this custom termination policy.
CloudFormation YAML File Content
The following YAML content outlines the CloudFormation template used to deploy this solution:
AWSTemplateFormatVersion: '2010-09-09'
Resources:
LambdaExecutionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: 'sts:AssumeRole'
Policies:
- PolicyName: LambdaExecutionPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: 'arn:aws:logs:*:*:*'
- Effect: Allow
Action:
- 'ec2:DescribeInstances'
Resource: '*'
MyLambdaFunction:
Type: 'AWS::Lambda::Function'
Properties:
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: |
import boto3
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ec2 = boto3.client('ec2')
def lambda_handler(event, context):
logger.info("Received event: " + str(event))
instance_ids = [instance['InstanceId'] for instance in event.get('Instances', [])]
logger.info("Instance IDs: " + str(instance_ids))
if instance_ids:
descriptions = ec2.describe_instances(InstanceIds=instance_ids)
instances = [i for r in descriptions['Reservations'] for i in r['Instances']]
oldest_instance = sorted(instances, key=lambda x: x['LaunchTime'])[0]['InstanceId']
logger.info("Oldest instance ID: " + oldest_instance)
return {'InstanceIDs': [oldest_instance]}
else:
logger.info("No instances to process.")
return {'InstanceIDs': []}
Runtime: python3.9
Timeout: 30
LambdaInvokePermission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt MyLambdaFunction.Arn
Principal: !Sub 'arn:aws:iam::${AWS::AccountId}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling'
Steps to Deploy the CloudFormation Template:
-
Save the Template:
- Save the above YAML content to a file named
terminate_oldest_template.yaml
.
-
Access AWS Management Console:
- Log into your AWS account and navigate to the AWS CloudFormation console.
-
Create a New Stack:
- Select "Create stack" -> "With new resources (standard)".
- Choose "Upload a template file", click "Choose file", and upload the
terminate_oldest_template.yaml
file.
- Click "Next".
-
Specify Stack Details:
- Enter a stack name, such as
ASGTerminateOldestInstanceStack
.
- Click "Next".
-
Configure Stack Options:
- Configure any necessary options, like tags or permissions. For most cases, default options are sufficient.
- Click "Next".
-
Review and Launch:
- Review all settings to ensure they are correct.
- Acknowledge that AWS CloudFormation might create IAM resources, then click "Create stack".
-
Monitor Deployment:
- Stay on the CloudFormation console to monitor the stack creation. Wait until the status changes to "CREATE_COMPLETE".
Change the termination policy for an Auto Scaling group:
Disclaimer:
This script has been tested multiple times in a controlled lab environment and has consistently demonstrated satisfactory performance. However, it is provided on an "as-is" basis, and while we have made every effort to ensure its reliability, we cannot guarantee its accuracy or functionality in other environments. It is the user's responsibility to thoroughly evaluate and test this script in a non-production environment before deploying it in production. Use of this script is at your own risk, and AWS assumes no liability for any damages or issues arising from its use. For assistance or inquiries, please contact AWS support team.
References: