For automatically deployed instances it’s impossible to setup CloudWatch Alarm as you do not know the instance ID.
The only way to setup an alarm was to create an AWS Lambda function that poles all the running instances and compares their launch time to a specified timeout.
The lambda function is periodically triggered by a CloudWatch - Event – Rule.
Use tags to specify different run durations to different machines. For example your launch tool should tag the instance with key value “Test”
Please note this code comes with NO warranties at all! This is more of an example.
import boto3
import datetime
import json
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
ec2_client = boto3.client('ec2')
INSTANCE_TIMEOUT = 24
MAX_PERMITTED_INSTANCES = 5
MAILING_LIST = "jon.doh@simpsons.duf, drunk@beer.com"
def parse_tag(tags, keyValuePair):
for tag in tags:
if tag['Key'] == keyValuePair[0] and tag['Value'] == keyValuePair[1]:
return True
return False
def runtimeExceeded(instance, timeOutHours):
timeNow = datetime.datetime.utcnow()
instanceRuntime = timeNow - instance.launch_time.replace(tzinfo=None)
print instanceRuntime
if instanceRuntime > datetime.timedelta(hours=timeOutHours):
return True
else:
return False
def sendAlert(instance, message):
msg = MIMEMultipart()
msg['From'] = 'AWS_Notification@sourcevertex.net'
msg['To'] = MAILING_LIST
msg['Subject'] = "AWS Alert: " + message
bodyText = '\n\nThis message was sent by the AWS Monitor ' + \
'Lambda. For details see AwsConsole-Lambdas. \n\nIf you want to ' + \
'exclude an instance from this monitor, tag it ' + \
'with Key=RuntimeMonitor Value=False'
messageBody = MIMEText( message + '\nInstance ID: ' +
str(instance.instance_id) + '\nIn Availability zone: '
+ str(instance.placement['AvailabilityZone']) + bodyText)
msg.attach(messageBody)
ses = boto3.client('ses')
ses.send_raw_email(RawMessage={'Data' : msg.as_string()})
def lambda_handler(event, context):
aws_regions = ec2_client.describe_regions()['Regions']
for region in aws_regions:
runningInstancesCount = 0
try:
ec2 = boto3.client('ec2', region_name=region['RegionName'])
ec2_resource = boto3.resource('ec2',
region_name=region['RegionName'])
aws_region = region['RegionName']
instances = ec2_resource.instances.all()
for i in instances:
if i.state['Name'] == 'running':
runningInstancesCount +=1
if i.tags != None:
if parse_tag(i.tags, ('RuntimeMonitor', 'False')):
pass
else:
if runtimeExceeded(i, INSTANCE_TIMEOUT):
sendAlert(i, "An EC2 instance has been running " + \
"for over {0} hours".format(INSTANCE_TIMEOUT))
else:
print "Untagged instence"
if runtimeExceeded(i, UNKNOWN_INSTANCE_TIMEOUT):
sendAlert(i, "An EC2 instance has been running " + \
"for over {0} hours".format(UNKNOWN_INSTANCE_TIMEOUT))
except Exception as e:
print e
continue
if runningInstancesCount > MAX_PERMITTED_INSTANCES:
sendAlert(i, "Number of running instances exceeded threshold " + \
"{0} running instances".format(runningInstancesCount))
return True