''' Find and delete AWS resources matching the provided --match string. Unless --yes|-y is provided, the prompt for confirmation prior to deleting resources. Please use caution, you can easily delete you're *ENTIRE* EC2 infrastructure. ''' import os import re import sys import boto import optparse import yaml import os.path import boto.ec2.elb import time def delete_aws_resources(get_func, attr, opts): for item in get_func(): val = getattr(item, attr) if re.search(opts.match_re, val): prompt_and_delete(item, "Delete matching %s? [y/n]: " % (item,), opts.assumeyes) def delete_autoscaling_group(get_func, attr, opts): assumeyes = opts.assumeyes group_name = None for item in get_func(): group_name = getattr(item, attr) if re.search(opts.match_re, group_name): if not opts.assumeyes: assumeyes = raw_input("Delete matching %s? [y/n]: " % (item).lower()) == 'y' break if assumeyes and group_name: groups = asg.get_all_groups(names=[group_name]) if groups: group = groups[0] group.max_size = 0 group.min_size = 0 group.desired_capacity = 0 group.update() instances = True while instances: tmp_groups = asg.get_all_groups(names=[group_name]) if tmp_groups: tmp_group = tmp_groups[0] if not tmp_group.instances: instances = False time.sleep(10) group.delete() while len(asg.get_all_groups(names=[group_name])): time.sleep(5) print ("Terminated ASG: %s" % group_name) def delete_aws_eips(get_func, attr, opts): # the file might not be there if the integration test wasn't run try: eip_log = open(opts.eip_log, 'r').read().splitlines() except IOError: print('%s not found.' % opts.eip_log) return for item in get_func(): val = getattr(item, attr) if val in eip_log: prompt_and_delete(item, "Delete matching %s? [y/n]: " % (item,), opts.assumeyes) def delete_aws_instances(reservation, opts): for list in reservation: for item in list.instances: prompt_and_delete(item, "Delete matching %s? [y/n]: " % (item,), opts.assumeyes) def prompt_and_delete(item, prompt, assumeyes): if not assumeyes: assumeyes = raw_input(prompt).lower() == 'y' assert hasattr(item, 'delete') or hasattr(item, 'terminate') , "Class <%s> has no delete or terminate attribute" % item.__class__ if assumeyes: if hasattr(item, 'delete'): item.delete() print ("Deleted %s" % item) if hasattr(item, 'terminate'): item.terminate() print ("Terminated %s" % item) def parse_args(): # Load details from credentials.yml default_aws_access_key = os.environ.get('AWS_ACCESS_KEY', None) default_aws_secret_key = os.environ.get('AWS_SECRET_KEY', None) if os.path.isfile('credentials.yml'): credentials = yaml.load(open('credentials.yml', 'r')) if default_aws_access_key is None: default_aws_access_key = credentials['ec2_access_key'] if default_aws_secret_key is None: default_aws_secret_key = credentials['ec2_secret_key'] parser = optparse.OptionParser(usage="%s [options]" % (sys.argv[0],), description=__doc__) parser.add_option("--access", action="store", dest="ec2_access_key", default=default_aws_access_key, help="Amazon ec2 access id. Can use EC2_ACCESS_KEY environment variable, or a values from credentials.yml.") parser.add_option("--secret", action="store", dest="ec2_secret_key", default=default_aws_secret_key, help="Amazon ec2 secret key. Can use EC2_SECRET_KEY environment variable, or a values from credentials.yml.") parser.add_option("--eip-log", action="store", dest="eip_log", default = None, help = "Path to log of EIPs created during test.") parser.add_option("--integration-config", action="store", dest="int_config", default = "integration_config.yml", help = "path to integration config") parser.add_option("--credentials", "-c", action="store", dest="credential_file", default="credentials.yml", help="YAML file to read cloud credentials (default: %default)") parser.add_option("--yes", "-y", action="store_true", dest="assumeyes", default=False, help="Don't prompt for confirmation") parser.add_option("--match", action="store", dest="match_re", default="^ansible-testing-", help="Regular expression used to find AWS resources (default: %default)") (opts, args) = parser.parse_args() for required in ['ec2_access_key', 'ec2_secret_key']: if getattr(opts, required) is None: parser.error("Missing required parameter: --%s" % required) return (opts, args) if __name__ == '__main__': (opts, args) = parse_args() int_config = yaml.load(open(opts.int_config).read()) if not opts.eip_log: output_dir = os.path.expanduser(int_config["output_dir"]) opts.eip_log = output_dir + '/' + opts.match_re.replace('^','') + '-eip_integration_tests.log' # Connect to AWS aws = boto.connect_ec2(aws_access_key_id=opts.ec2_access_key, aws_secret_access_key=opts.ec2_secret_key) elb = boto.connect_elb(aws_access_key_id=opts.ec2_access_key, aws_secret_access_key=opts.ec2_secret_key) asg = boto.connect_autoscale(aws_access_key_id=opts.ec2_access_key, aws_secret_access_key=opts.ec2_secret_key) try: # Delete matching keys delete_aws_resources(aws.get_all_key_pairs, 'name', opts) # Delete matching security groups delete_aws_resources(aws.get_all_security_groups, 'name', opts) # Delete matching ASGs delete_autoscaling_group(asg.get_all_groups, 'name', opts) # Delete matching launch configs delete_aws_resources(asg.get_all_launch_configurations, 'name', opts) # Delete ELBs delete_aws_resources(elb.get_all_load_balancers, 'name', opts) # Delete recorded EIPs delete_aws_eips(aws.get_all_addresses, 'public_ip', opts) # Delete temporary instances filters = {"tag:Name":opts.match_re.replace('^',''), "instance-state-name": ['running', 'pending', 'stopped' ]} delete_aws_instances(aws.get_all_instances(filters=filters), opts) except KeyboardInterrupt as e: print("\nExiting on user command.")