Skip to content

Instantly share code, notes, and snippets.

@ljmocic
Last active November 14, 2020 20:50
Show Gist options
  • Save ljmocic/f9c122fd6f75ffa38fae3fc6badd0b4f to your computer and use it in GitHub Desktop.
Save ljmocic/f9c122fd6f75ffa38fae3fc6badd0b4f to your computer and use it in GitHub Desktop.
import boto3
import json
from datetime import datetime, timedelta
REGION = 'us-east-1'
TAG = 'A'
ARN = 'arn:aws:sns:us-east-1:12345678:snsName'
UTILIZATION_PERCENTAGE = 0.5
TIME = 60 * 60 * 2
# if you are testing set to 1, if you want script to shut them down, set to 0
TEST_MODE = 1
ec2 = boto3.resource('ec2', region_name=REGION)
cloudwatch = boto3.client('cloudwatch', region_name=REGION)
sns = boto3.client('sns', region_name=REGION)
def notify(content):
message = {"log": content}
print(content)
'''
sns.publish(
TargetArn=ARN,
Message=json.dumps({'default': json.dumps(message)}),
MessageStructure='json'
)
'''
def get_tag_value(all_tags, temp_tag):
for tag in all_tags:
if tag['Key'] == temp_tag:
return tag['Value']
def get_instance_name(instance):
for tag in instance.tags:
if 'Name'in tag['Key']:
return tag['Value']
return 'empty_instance_name'
def get_avg_utilization(instance_id):
response = cloudwatch.get_metric_statistics(
Namespace='AWS/EC2',
MetricName='CPUUtilization',
Dimensions=[
{
'Name': 'InstanceId',
'Value': instance_id
},
],
StartTime=datetime.utcnow() - timedelta(seconds=TIME),
EndTime=datetime.utcnow(),
Period=TIME,
Statistics=[
'Average',
],
Unit='Percent'
)
for cpu in response['Datapoints']:
if 'Average' in cpu:
return float(cpu['Average'])
def get_group_shutdown_status(instances_from_tag_value):
no_of_idle = 0
for instance in instances_from_tag_value:
utilization = get_avg_utilization(instance.id)
print('[INFO] Instance: ' + get_tag_value(instance.tags, TAG) + ':' + get_instance_name(instance) + ':' + instance.id + ' has been active with ' + str(round(utilization, 2)) + '% CPU utilization.')
if utilization < UTILIZATION_PERCENTAGE:
no_of_idle += 1
# If all of them are idle, set the signal for shutdown
if no_of_idle == len(instances_from_tag_value):
return True
else:
return False
def get_instance_ids(instances):
instance_ids = []
for instance in instances:
instance_ids.append(instance.id)
return instance_ids
def stop_instances(instance_ids):
if TEST_MODE == 1:
for id in instance_ids:
print('[INFO] Shutting down instance: ' + TAG + ':' + id)
elif TEST_MODE == 0:
ec2.instances.filter(InstanceIds=instance_ids).stop()
def lambda_handler(event, context):
instances_map = {}
response = ''
filter = [
{
'Name': 'tag-key',
'Values': [TAG]
},
{
'Name': 'instance-state-name',
'Values': ['running']
}
]
# Group instances by tag_value, which in this case are users
groupingtag_instances = ec2.instances.filter(Filters=filter)
ins_count = 0
for ins in groupingtag_instances:
ins_count += 1
if ins_count == 0:
response += ('TAG: ' + TAG + ' instances has not been found.')
else:
response += ('Found ' + str(ins_count) + ' with ' + TAG + '.')
for instance in groupingtag_instances:
tag_value = get_tag_value(ec2.Instance(instance.id).tags, TAG)
print('[INFO] Found instance: ' + TAG + ':' + get_tag_value(instance.tags, TAG) + ':' + get_instance_name(instance) + ':' + instance.id)
if tag_value != None:
if tag_value not in instances_map:
instances_map[tag_value] = []
instances_map[tag_value].append(instance)
print(instances_map)
# Check for every group for shutdown and send the signal for shutdown if needed
for tag_value in instances_map:
shutdown_status = get_group_shutdown_status(instances_map[tag_value])
if shutdown_status == True:
instance_ids = get_instance_ids(instances_map[tag_value])
stop_instances(instance_ids)
response += (TAG + ' : ' + tag_value + ' is idle. Will be shutdown.\n')
else:
response += (TAG + ' : ' + tag_value + ' is busy. Will not shut down.\n')
notify(response)
return 'Success'
@edsna
Copy link

edsna commented Nov 14, 2020

Hi, thanks for sharing,
I tried this class with a role with lambda permissions to EC2, SNS, and cloudwatch but it did not seem to print anything on the logs neither shut the instances. Is there anything I'm missing?
Let me know

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment