Advertisement
Guest User

Untitled

a guest
Jul 19th, 2019
106
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.86 KB | None | 0 0
  1. #!/usr/bin/python3
  2. # finds orphaned items from stacks
  3.  
  4. import os
  5. import sys
  6. sys.path.insert(0,os.environ["LAMBDA_TASK_ROOT"]+"/packages")
  7. import boto3
  8. from botocore.exceptions import ClientError
  9. import datetime
  10. from dateutil.tz import tzutc
  11. from botocore.config import Config
  12.  
  13. config = Config(
  14. retries = dict(
  15. max_attempts = 10
  16. )
  17. )
  18.  
  19. types = {
  20. 'AWS::EC2::SecurityGroup': {
  21. 'type': "boto3.client('ec2')",
  22. 'call': "client.describe_security_groups(GroupIds=[resource['PhysicalResourceId']])"
  23. },
  24. 'AWS::ECR::Repository': {
  25. 'type': "boto3.client('ecr')",
  26. 'call': "client.describe_repositories(repositoryNames=[resource['PhysicalResourceId']])"
  27. },
  28. 'AWS::DMS::Endpoint': {
  29. 'type': "boto3.client('dms')",
  30. 'call': "client.describe_endpoints(Filters=[{'Name': 'endpoint-arn', 'Values': [resource['PhysicalResourceId']]}])"
  31. },
  32. 'AWS::RDS::DBCluster': {
  33. 'type': "boto3.client('rds')",
  34. 'call': "client.describe_db_clusters(DBClusterIdentifier=resource['PhysicalResourceId'])"
  35. },
  36. 'AWS::RDS::DBInstance': {
  37. 'type': "boto3.client('rds')",
  38. 'call': "client.describe_db_instances(DBInstanceIdentifier=resource['PhysicalResourceId'])"
  39. },
  40. 'AWS::RDS::DBClusterParameterGroup': {
  41. 'type': "boto3.client('rds')",
  42. 'call': "client.describe_db_cluster_parameter_groups(DBClusterParameterGroupName=resource['PhysicalResourceId'])"
  43. },
  44. 'AWS::RDS::DBSubnetGroup': {
  45. 'type': "boto3.client('rds')",
  46. 'call': "client.describe_db_subnet_groups(DBSubnetGroupName=resource['PhysicalResourceId'])"
  47. },
  48. 'AWS::Glue::DevEndpoint': {
  49. 'type': "boto3.client('glue')",
  50. 'call': "client.get_dev_endpoint(EndpointName=resource['PhysicalResourceId'])"
  51. },
  52. 'AWS::IAM::Role': {
  53. 'type': "boto3.client('iam')",
  54. 'call': "client.get_role(RoleName=resource['PhysicalResourceId'])"
  55. },
  56. 'Custom::EventStreamSubscription': {
  57. 'type': "boto3.client('sns')",
  58. 'call': "client.get_subscription_attributes(SubscriptionArn=resource['PhysicalResourceId'])"
  59. }
  60. }
  61.  
  62. def main(event,context):
  63. # find all DELETE_COMPLETE stacks, these are from the last 30 days
  64. # page through the stacks and look for any resources which had the DELETE_SKIPPED status
  65. # create a list of these resources
  66. resources = []
  67. cfClient = boto3.client('cloudformation', config=config)
  68. cf_paginator = cfClient.get_paginator('list_stacks')
  69. cf_pages = cf_paginator.paginate(StackStatusFilter=['DELETE_COMPLETE'])
  70. for page in cf_pages:
  71. for stack in page['StackSummaries']:
  72. try:
  73. sr_paginator = cfClient.get_paginator('list_stack_resources')
  74. sr_pages = sr_paginator.paginate(StackName=stack['StackId'])
  75. except Exception as e:
  76. print("Exception in list_stack_resources:", e)
  77. for sr_page in sr_pages:
  78. for resource in sr_page['StackResourceSummaries']:
  79. # ignore remnant lambda versions
  80. if resource['ResourceStatus'] == 'DELETE_SKIPPED' and resource['ResourceType'] != 'AWS::Lambda::Version':
  81. resources.append(resource)
  82.  
  83. # for each DELETE_SKIPPED resource, check what kind it is and if it still exists
  84. # if it still exists, then add it to the found list
  85. found = []
  86. missing = []
  87. for resource in resources:
  88. # print(resource)
  89. if resource['ResourceType'] in types:
  90. # print(types[resource['ResourceType']])
  91. client = eval(types[resource['ResourceType']]['type'])
  92. try:
  93. print("Trying:", resource['ResourceType'], resource['PhysicalResourceId'])
  94. response = eval(types[resource['ResourceType']]['call'])
  95. found.append(resource)
  96. except ClientError as e:
  97. print("ClientError:", e.response['Error']['Message'])
  98. except KeyError as e:
  99. print("KeyError for resource:", resource['PhysicalResourceId'], e)
  100. elif resource['ResourceType'] == 'AWS::S3::Bucket':
  101. try:
  102. client = boto3.client('s3')
  103. print("Trying:", resource['ResourceType'], resource['PhysicalResourceId'])
  104. response = client.list_buckets()
  105. for bucket in response['Buckets']:
  106. if bucket['Name'] == resource['PhysicalResourceId']:
  107. found.append(resource)
  108. except ClientError as e:
  109. print("ClientError:", e.response['Error']['Message'])
  110. except KeyError as e:
  111. print("KeyError for resource:", resource, e)
  112. else:
  113. try:
  114. print(resource)
  115. except Exception as e:
  116. print("Exception for resource:", resource['PhysicalResourceId'], e)
  117. missing.append(resource)
  118.  
  119. # if there are items in the found list, send a message with what these are to Slack
  120. if len(found) > 0:
  121. message = "CloudFormation DELETE_SKIPPED items found in account " + os.environ['ACCOUNT'] + "!\n"
  122. for item in found:
  123. message = message + "\nResource Type: " + item['ResourceType'] + ", with name: " + item['PhysicalResourceId'] + ", exists but referencing stack is gone."
  124. try:
  125. print(message)
  126. snsclient = boto3.client('sns', region_name='us-east-2')
  127. response = snsclient.publish(TopicArn=os.environ['TOPIC_ARN'], Message=message, Subject='CloudFormation DELETE_SKIPPED items found!')
  128. print(response)
  129. except ClientError as e:
  130. print("ClientError:", e.response['Error']['Message'])
  131. elif len(found) == 0:
  132. print("Nothing found with DELETE_SKIPPED.")
  133.  
  134. if len(missing) > 0:
  135. message = "CloudFormation DELETE_SKIPPED items found in account " + os.environ['ACCOUNT'] + "!\n"
  136. for item in missing:
  137. message = message + "\nResource Type: " + item['ResourceType'] + ", with name: " + item['PhysicalResourceId'] + ", exists but referencing stack is gone. Unlisted ResourceType."
  138. try:
  139. print(message)
  140. snsclient = boto3.client('sns', region_name='us-east-2')
  141. response = snsclient.publish(TopicArn=os.environ['TOPIC_ARN'], Message=message, Subject='CloudFormation DELETE_SKIPPED items found!')
  142. print(response)
  143. except ClientError as e:
  144. print("ClientError:", e.response['Error']['Message'])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement