あまり使われていないLambda関数を一覧化してみた

Lambda関数を色々と作っていたが、煩雑になって来たので自動で「最近編集していない＝使っていない」Lambda関数を検知してみた。
※「実行されていない」ではないので注意。

# TL;DR
https://github.com/kojiisd/aws-lambda-old-resource-detector

# 処理の流れ
1. Lambda関数の一覧を取得する
2. CloudFormationのスタック一覧を取得し、ResourceTypeが「AWS::Lambda::Function」のデータから、Lambda関数名を抽出し、関数名とスタック名の辞書を作成する。
3. No.1で作成したLambda関数一覧から、「LastModified」が指定された期間以上前の関数一覧を作成する。この際、No.2で取得したCloudFormationのスタック名と一致する場合はそのスタック情報も結果に残す。

自動削除、、、も考えましたが、CloudFormationが絡んでいると色々と厄介なので、今回は検知までとしています。

# CloudFormationのスタック一覧を取得

boto3のdescribe_stacksメソッドをコールしてスタック一覧を取得した後、そのスタック名を利用してlist_stack_resourcesメソッドでLambda関数が含まれるであろう箇所までスタック情報を取得します。

一応NextTokenにも対応していますが、未検証。

```py
def create_stack_lambda_map():
    
    next_token = None
    
    stack_map = cf_client.describe_stacks()
    stack_lambda_map = {}
    
    add_stack_lambda_map(stack_map, stack_lambda_map)
    
    while next_token:
        stack_map = cf_client.describe_stacks(NextToken=next_token)
        add_stack_lambda_map(stack_map, stack_lambda_map)

    return stack_lambda_map

def add_stack_lambda_map(stack_map, stack_lambda_map):
    for stack_info in stack_map['Stacks']:
        stack_resources = cf_client.list_stack_resources(StackName = stack_info['StackName'])
        for stack_resource in stack_resources['StackResourceSummaries']:
            if stack_resource['ResourceType'] == "AWS::Lambda::Function":
                stack_lambda_map[stack_resource['PhysicalResourceId']] = stack_info['StackName']

    return stack_lambda_map
```

# 指定された期間以上前の関数一覧を作成
環境変数で```DURATION_DAYS```というのを指定しているので、その日にち以上経過している「変更されていないLambda関数」の一覧を抜き出します。この際、CloudFormation側で作成されたLambda関数かどうかは、前述の処理で取得した「Lambda関数：スタック名」の辞書から探し出します。

ちなみに覧化を回避したい（変更はしていないけど利用している）時用に、今回Lambda関数のTags情報も一緒に残してます。


```py
def detector(event, context):
    now_time = datetime.now(timezone('UTC'))
    checked_date = now_time - timedelta(days = int(os.environ['DURATION_DAYS']))
    
    lambda_function_list = lambda_client.list_functions()
    stack_lambda_map = create_stack_lambda_map()
    function_time_map = {}
    
    for function in lambda_function_list['Functions']:
        if checked_date > parser.parse(function['LastModified']):
            stack_name = ""
            if function['FunctionName'] in stack_lambda_map:
                stack_name = stack_lambda_map[function['FunctionName']]
                
            function_time_map[function['FunctionName']] = {
                'FunctionArn': function['FunctionArn'],
                'LastModified': function['LastModified'],
                'Tags': lambda_client.list_tags(Resource=function['FunctionArn'])['Tags'],
                'StackName': stack_name
                }
            
    
    return function_time_map
```

# 結果
7日間変更していない関数の一覧を出してみましたが、過去作ってそのままにしていたものが全部検出されました。。。まぁ動いているということでよしとしましょう。

変更はしていないけど現在利用しているLambda関数もバッチリ検出されているので、やはりTagsに特定の文字列を入れた場合は検知しない、などの処理は必要そうですね。

```json
{
  "LambdaAthenaAccessor": {
    "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:LambdaAthenaAccessor",
    "LastModified": "2017-04-23T13:53:14.708+0000",
    "Tags": {},
    "StackName": ""
  },
  "lambda-dynamodb-aggregator-dev-run": {
    "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:lambda-dynamodb-aggregator-dev-run",
    "LastModified": "2018-01-03T19:40:35.227+0000",
    "Tags": {
      "STAGE": "dev"
    },
    "StackName": "lambda-dynamodb-aggregator-dev"
  },
:
#ずらずらっとここに一覧が並んだ
:
  },
  "aws-lambda-line-helloworld-dev-run": {
    "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:aws-lambda-line-helloworld-dev-run",
    "LastModified": "2018-05-20T10:41:49.877+0000",
    "Tags": {
      "STAGE": "dev"
    },
    "StackName": "aws-lambda-line-helloworld-dev"
  }
}
```

# まとめ
どこかで一覧化したくないものを除けるようにする処理を追加しよう。