Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python3
- # finds orphaned items from stacks
- import os
- import sys
- sys.path.insert(0,os.environ["LAMBDA_TASK_ROOT"]+"/packages")
- import boto3
- from botocore.exceptions import ClientError
- import datetime
- from dateutil.tz import tzutc
- from botocore.config import Config
- config = Config(
- retries = dict(
- max_attempts = 10
- )
- )
- types = {
- 'AWS::EC2::SecurityGroup': {
- 'type': "boto3.client('ec2')",
- 'call': "client.describe_security_groups(GroupIds=[resource['PhysicalResourceId']])"
- },
- 'AWS::ECR::Repository': {
- 'type': "boto3.client('ecr')",
- 'call': "client.describe_repositories(repositoryNames=[resource['PhysicalResourceId']])"
- },
- 'AWS::DMS::Endpoint': {
- 'type': "boto3.client('dms')",
- 'call': "client.describe_endpoints(Filters=[{'Name': 'endpoint-arn', 'Values': [resource['PhysicalResourceId']]}])"
- },
- 'AWS::RDS::DBCluster': {
- 'type': "boto3.client('rds')",
- 'call': "client.describe_db_clusters(DBClusterIdentifier=resource['PhysicalResourceId'])"
- },
- 'AWS::RDS::DBInstance': {
- 'type': "boto3.client('rds')",
- 'call': "client.describe_db_instances(DBInstanceIdentifier=resource['PhysicalResourceId'])"
- },
- 'AWS::RDS::DBClusterParameterGroup': {
- 'type': "boto3.client('rds')",
- 'call': "client.describe_db_cluster_parameter_groups(DBClusterParameterGroupName=resource['PhysicalResourceId'])"
- },
- 'AWS::RDS::DBSubnetGroup': {
- 'type': "boto3.client('rds')",
- 'call': "client.describe_db_subnet_groups(DBSubnetGroupName=resource['PhysicalResourceId'])"
- },
- 'AWS::Glue::DevEndpoint': {
- 'type': "boto3.client('glue')",
- 'call': "client.get_dev_endpoint(EndpointName=resource['PhysicalResourceId'])"
- },
- 'AWS::IAM::Role': {
- 'type': "boto3.client('iam')",
- 'call': "client.get_role(RoleName=resource['PhysicalResourceId'])"
- },
- 'Custom::EventStreamSubscription': {
- 'type': "boto3.client('sns')",
- 'call': "client.get_subscription_attributes(SubscriptionArn=resource['PhysicalResourceId'])"
- }
- }
- def main(event,context):
- # find all DELETE_COMPLETE stacks, these are from the last 30 days
- # page through the stacks and look for any resources which had the DELETE_SKIPPED status
- # create a list of these resources
- resources = []
- cfClient = boto3.client('cloudformation', config=config)
- cf_paginator = cfClient.get_paginator('list_stacks')
- cf_pages = cf_paginator.paginate(StackStatusFilter=['DELETE_COMPLETE'])
- for page in cf_pages:
- for stack in page['StackSummaries']:
- try:
- sr_paginator = cfClient.get_paginator('list_stack_resources')
- sr_pages = sr_paginator.paginate(StackName=stack['StackId'])
- except Exception as e:
- print("Exception in list_stack_resources:", e)
- for sr_page in sr_pages:
- for resource in sr_page['StackResourceSummaries']:
- # ignore remnant lambda versions
- if resource['ResourceStatus'] == 'DELETE_SKIPPED' and resource['ResourceType'] != 'AWS::Lambda::Version':
- resources.append(resource)
- # for each DELETE_SKIPPED resource, check what kind it is and if it still exists
- # if it still exists, then add it to the found list
- found = []
- missing = []
- for resource in resources:
- # print(resource)
- if resource['ResourceType'] in types:
- # print(types[resource['ResourceType']])
- client = eval(types[resource['ResourceType']]['type'])
- try:
- print("Trying:", resource['ResourceType'], resource['PhysicalResourceId'])
- response = eval(types[resource['ResourceType']]['call'])
- found.append(resource)
- except ClientError as e:
- print("ClientError:", e.response['Error']['Message'])
- except KeyError as e:
- print("KeyError for resource:", resource['PhysicalResourceId'], e)
- elif resource['ResourceType'] == 'AWS::S3::Bucket':
- try:
- client = boto3.client('s3')
- print("Trying:", resource['ResourceType'], resource['PhysicalResourceId'])
- response = client.list_buckets()
- for bucket in response['Buckets']:
- if bucket['Name'] == resource['PhysicalResourceId']:
- found.append(resource)
- except ClientError as e:
- print("ClientError:", e.response['Error']['Message'])
- except KeyError as e:
- print("KeyError for resource:", resource, e)
- else:
- try:
- print(resource)
- except Exception as e:
- print("Exception for resource:", resource['PhysicalResourceId'], e)
- missing.append(resource)
- # if there are items in the found list, send a message with what these are to Slack
- if len(found) > 0:
- message = "CloudFormation DELETE_SKIPPED items found in account " + os.environ['ACCOUNT'] + "!\n"
- for item in found:
- message = message + "\nResource Type: " + item['ResourceType'] + ", with name: " + item['PhysicalResourceId'] + ", exists but referencing stack is gone."
- try:
- print(message)
- snsclient = boto3.client('sns', region_name='us-east-2')
- response = snsclient.publish(TopicArn=os.environ['TOPIC_ARN'], Message=message, Subject='CloudFormation DELETE_SKIPPED items found!')
- print(response)
- except ClientError as e:
- print("ClientError:", e.response['Error']['Message'])
- elif len(found) == 0:
- print("Nothing found with DELETE_SKIPPED.")
- if len(missing) > 0:
- message = "CloudFormation DELETE_SKIPPED items found in account " + os.environ['ACCOUNT'] + "!\n"
- for item in missing:
- message = message + "\nResource Type: " + item['ResourceType'] + ", with name: " + item['PhysicalResourceId'] + ", exists but referencing stack is gone. Unlisted ResourceType."
- try:
- print(message)
- snsclient = boto3.client('sns', region_name='us-east-2')
- response = snsclient.publish(TopicArn=os.environ['TOPIC_ARN'], Message=message, Subject='CloudFormation DELETE_SKIPPED items found!')
- print(response)
- except ClientError as e:
- print("ClientError:", e.response['Error']['Message'])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement