-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathaws-delete-orphaned-cloudwatch-logs.py
203 lines (157 loc) · 7.62 KB
/
aws-delete-orphaned-cloudwatch-logs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#--------------------------------------------------------------------------------------------------
# Function: delete-orphaned-cloudwatch-logs
# Purpose: Deletes CloudWatch log groups that were generated by service resources that no longer exist.
# Inputs:
#
# {
# "view_only": "true|false",
# "regions": ["us-east-1", ...]
# }
#
# Leave the regions sections blank to apply to all regions
#
# Supported services:
# * Lambda functions
# * VPC flow logs
# * SageMaker endpoints
#--------------------------------------------------------------------------------------------------
import json
import boto3
from botocore.exceptions import ClientError
from botocore.exceptions import EndpointConnectionError
#--------------------------------------------------------------------------------------------------
# Function handler
#--------------------------------------------------------------------------------------------------
def lambda_handler(event, context):
regions = []
# Determine whether the user just wants to view the orphaned logs.
view_only = ('view_only' in event and event['view_only'].lower() == 'true')
#--------------------------------------------------
# Determine which regions to include. Apply to all regions by default.
#--------------------------------------------------
if 'regions' in event and type(event['regions']) == list:
regions = event['regions']
# Get all regions if not otherwise specified.
if not regions:
region_response = boto3.client('ec2').describe_regions()
regions = [region['RegionName'] for region in region_response['Regions']]
#--------------------------------------------------
# Iterate through the specified regions.
#--------------------------------------------------
for region in regions:
print('REGION: {}'.format(region))
# Get the relevant service resources.
services = get_service_resources(region)
services['region'] = region
print(json.dumps(services))
#--------------------------------------------------
# Iterate through the CloudWatch log groups and look
# for log name signatures that match names created
# as defaults by the services
#--------------------------------------------------
logs_client = boto3.client('logs', region_name=region)
orphaned_count = 0
paginator = logs_client.get_paginator('describe_log_groups')
for response in paginator.paginate():
for log_group in response.get('logGroups'):
# Based on the log name, determine whether there are associated
# resources.
orphaned_count += process_log_group(logs_client, log_group['logGroupName'], services)
return_message = 'There were {0} orphaned log groups in {1}.\n'.format(orphaned_count, region)
print (return_message)
#--------------------------------------------------
# function: get_service_resources
#--------------------------------------------------
def get_service_resources(region):
services = {}
#--------------------------------------------------
# Collect the list of existing Lambda functions.
#--------------------------------------------------
functions = []
try:
paginator = boto3.client('lambda', region_name=region).get_paginator('list_functions')
for response in paginator.paginate():
for function in response.get('Functions'):
functions.append(function['FunctionName'])
except EndpointConnectionError as e:
print(e)
except ClientError as e:
print(e)
services['lambda'] = sorted(functions)
#--------------------------------------------------
# Collect the list of existing Flow Log logs.
#--------------------------------------------------
vpcs = []
try:
for resource in boto3.client('ec2', region_name=region).describe_vpcs().get('Vpcs'):
vpcs.append(resource['VpcId'])
except EndpointConnectionError as e:
print(e)
except ClientError as e:
print(e)
services['vpc'] = sorted(vpcs)
#--------------------------------------------------
# Collect the list of existing SageMaker notebook instances.
#--------------------------------------------------
sagemaker_instances = []
try:
# Account for the possibility that SageMaker isn't supported in this region.
for resource in boto3.client('sagemaker', region_name=region).list_notebook_instances().get('NotebookInstances'):
vpcs.append(resource['NotebookInstanceName'])
except EndpointConnectionError as e:
print(e)
except ClientError as e:
print(e)
services['sagemaker'] = sorted(sagemaker_instances)
return services
#--------------------------------------------------
# function: process_log_group
#--------------------------------------------------
def process_log_group(logs_client, log_group_name, services):
orphaned_count = 0
#--------------------------------------------------
# Lambda functions
#--------------------------------------------------
if log_group_name.startswith('/aws/lambda/'):
function_name = log_group_name.split("/")[3]
if function_name in services['lambda']:
print ("Function {} exists, not deleting.".format(function_name))
else:
orphaned_count += 1
if view_only:
print('Function {} does not exist.'.format(function_name))
else:
# There is no associated Lambda function, so delete the log group.
print('Function {} does not exist, DELETING.'.format(function_name))
logs_client.delete_log_group(logGroupName = log_group_name)
#--------------------------------------------------
# VPC flow logs
#--------------------------------------------------
elif log_group_name.startswith('vpc-flow-logs-'):
vpc_name = 'vpc-' + log_group_name.split("-")[4]
if vpc_name in services['vpc']:
print ("VPC {} exists, not deleting.".format(vpc_name))
else:
orphaned_count += 1
if view_only:
print('VPC {} does not exist.'.format(vpc_name))
else:
# There is no associated VPC, so delete the log group.
print('VPC {} does not exist, DELETING.'.format(vpc_name))
logs_client.delete_log_group(logGroupName = log_group_name)
#--------------------------------------------------
# SageMaker notebook instances
#--------------------------------------------------
elif log_group_name.startswith('/aws/sagemaker/Endpoints/'):
notebook_name = log_group_name.split("/")[4]
if notebook_name in services['sagemaker']:
print ("Notebook {} exists, not deleting.".format(notebook_name))
else:
orphaned_count += 1
if view_only:
print('Notebook {} does not exist.'.format(notebook_name))
else:
# There is no associated notebook instance, so delete the log group.
print('Notebook {} does not exist, DELETING.'.format(notebook_name))
logs_client.delete_log_group(logGroupName = log_group_name)
return orphaned_count