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 = "[email protected], [email protected]" 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): # Working in to UTC to avoid time-travel during daylight-saving changeover 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'] = '[email protected]' 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')): # Ignore these instances 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