forked from epsagon/list-lambdas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlist_lambdas.py
222 lines (190 loc) Β· 6.53 KB
/
list_lambdas.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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
"""
Enumerates Lambda functions from every region with interesting metadata
"""
from __future__ import print_function
from datetime import datetime
import argparse
import boto3
from boto3.session import Session
from terminaltables import AsciiTable
import progressbar
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
BYTE_TO_MB = 1024.0 * 1024.0
ALL_TABLE_HEADERS = [
'Region',
'Function',
'Memory (MB)',
'Code Size (MB)',
'Timeout (seconds)',
'Runtime',
'Description',
'Last Modified',
'Last Invocation',
]
SORT_KEYS = ['region', 'last-modified', 'last-invocation']
def list_available_lambda_regions():
"""
Enumerates list of all Lambda regions
:return: list of regions
"""
session = Session()
return session.get_available_regions('lambda')
def init_boto_client(client_name, region, args):
"""
Initiates boto's client object
:param client_name: client name
:param region: region name
:param args: arguments
:return: Client
"""
if args.token_key_id and args.token_secret:
boto_client = boto3.client(
client_name,
aws_access_key_id=args.token_key_id,
aws_secret_access_key=args.token_secret,
region_name=region
)
else:
boto_client = boto3.client(client_name, region_name=region)
return boto_client
def get_days_ago(datetime_obj):
"""
Converts a datetime object to "time ago" string
:param datetime_obj: Datetime
:return: "time ago" string
"""
days_ago = (datetime.now() - datetime_obj).days
datetime_str = 'Today'
if days_ago == 1:
datetime_str = 'Yesterday'
elif days_ago > 1:
datetime_str = '{0} days ago'.format(days_ago)
return datetime_str
def create_tables(lambdas_data, args):
"""
Create the output tables
:param lambdas_data: a list of the Lambda functions and their data
:param args: argparse arguments
:return: textual table-format information about the Lambdas
"""
all_table_data = [ALL_TABLE_HEADERS]
for lambda_data in lambdas_data:
function_data = lambda_data['function-data']
all_table_data.append([
lambda_data['region'],
str(function_data['FunctionName']),
str(function_data['MemorySize']),
'%.2f' % (function_data['CodeSize'] / BYTE_TO_MB),
str(function_data['Timeout']),
str(function_data['Runtime']),
str(function_data['Description']),
get_days_ago(lambda_data['last-modified']),
get_days_ago(datetime.fromtimestamp(lambda_data['last-invocation'] / 1000))
])
if args.should_print_all:
min_table_data = all_table_data
else:
# Get only the region, function, last modified and last invocation
min_table_data = [
[
lambda_data[0], lambda_data[1], lambda_data[-2], lambda_data[-1]
]
for lambda_data in all_table_data
]
return min_table_data, all_table_data
def print_lambda_list(args):
"""
Main function
:return: None
"""
regions = list_available_lambda_regions()
progress_bar = progressbar.ProgressBar(max_value=len(regions))
lambdas_data = []
for region in progress_bar(regions):
lambda_client = init_boto_client('lambda', region, args)
logs_client = init_boto_client('logs', region, args)
functions = lambda_client.list_functions()['Functions']
if not functions:
continue
for function_data in functions:
# Extract last modified time
last_modified = datetime.strptime(
function_data['LastModified'].split('.')[0],
DATETIME_FORMAT
)
# Extract last invocation time from logs
logs = logs_client.describe_log_streams(
logGroupName='/aws/lambda/{0}'.format(function_data['FunctionName']),
orderBy='LastEventTime',
descending=True
)
last_invocation = max(
[log.get('lastEventTimestamp', 0) for log in logs['logStreams']]
)
inactive_days = (datetime.now() - datetime.fromtimestamp(last_invocation / 1000)).days
if args.inactive_days_filter > inactive_days:
continue
lambdas_data.append({
'region': region,
'function-data': function_data,
'last-modified': last_modified,
'last-invocation': last_invocation
})
# Sort data by the given key (default: by region)
lambdas_data.sort(key=lambda x: x[args.sort_by])
min_table_data, all_table_data = create_tables(lambdas_data, args)
table = AsciiTable(min_table_data)
print(table.table)
if not args.csv:
return
with open(args.csv, 'wt') as output_file:
for table_row in all_table_data:
output_file.writelines('{0}\n'.format(','.join(table_row)))
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Enumerates Lambda functions from every region with interesting metadata.'
)
parser.add_argument(
'--all',
dest='should_print_all',
default=False,
action='store_true',
help='Print all the information to the screen (default: print summarized information).'
)
parser.add_argument(
'--csv',
type=str,
help='CSV filename to output full table data.',
metavar='output_filename'
)
parser.add_argument(
'--token-key-id',
type=str,
help='AWS access key id. Must provide AWS secret access key as well (default: from local configuration).',
metavar='token-key-id'
)
parser.add_argument(
'--token-secret',
type=str,
help='AWS secret access key. Must provide AWS access key id as well (default: from local configuration.',
metavar='token-secret'
)
parser.add_argument(
'--inactive-days-filter',
type=int,
help='Filter only Lambda functions with minimum days of inactivity.',
default=0,
metavar='minimum-inactive-days'
)
parser.add_argument(
'--sort-by',
type=str,
help='Column name to sort by. Options: region, last-modified, last-invocation (default: region).',
default='region',
metavar='sort_by'
)
args = parser.parse_args()
if args.sort_by not in SORT_KEYS:
print('ERROR: Illegal column name: {0}.'.format(args.sort_by))
exit(1)
print_lambda_list(args)