Lambdaは無いけど残り続けているCloudWatch Logsのロググループ一覧を取得する

「Lambdaが無い、かつ、ロググループがある」を満たすロググループの一覧を調べるスクリプトを作ってみました。
2020.08.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

手動ポチポチやAWS SAM、AWS CDKなどでLambdaを作成するとき、CloudWatch Logのロググループを一緒に作らないと次のような状態になります。

  • Lambda関数: CloudFormation管理下である
  • ロググループ: CLoudFormation管理下ではない

このとき、CloudFormationからスタック削除すると、Lambda関数は無いけどログは残り続けている状態になってしまいます。 あえて残しているならともかく、意図せず残り続けているなら見にくい&課金対象にもなるし消しちゃいましょう。 (課金額はかなり少ないですが。東京:0.033USD/GB)

そこで「Lambdaが無い、かつ、ロググループがある」を満たすロググループの一覧を調べるスクリプトを作ってみました。

おすすめの方

  • Lambdaの一覧を取得したい方
  • CloudWatch Logsのロググループの一覧を取得したい方
  • Lambdaは無いけど、残り続けているロググループの一覧を取得したい方
  • ロググループを削除したい方

環境

項目 バージョン
Python 3.7

Lambda無いけど存在するロググループ一覧を取得するスクリプト

下記です。ついでにロググループを削除する処理もあります(コメントアウト)。

app.py

import boto3
from typing import Dict, List

cw_logs_client = client = boto3.client('logs')
lambda_client = boto3.client('lambda')

def main():
    log_groups = get_log_groups()
    log_groups_name = convert(log_groups, 'logGroupName')

    lambda_functions = get_lambda_functions()
    lambda_functions_name = convert(lambda_functions, 'FunctionName')

    # ロググループ名から /aws/lambda/ (12文字)を取り除く
    log_group_name_only = list(map(lambda x: x[12:], log_groups_name))

    # ロググループにあって、Lambdaに無い値を求める
    different = set(log_group_name_only) - set(lambda_functions_name)

    for item in sorted(different):
        print(f'/aws/lambda/{item}')

    print('-----')
    print(f'Log Group: {len(log_group_name_only)}')
    print(f'Lambda   : {len(lambda_functions_name)}')
    print(f'different: {len(different)}')

    # 削除する
    # delete_log_groups(list(different))


def get_log_groups(token: str=None) -> List[Dict]:
    option = {
        'logGroupNamePrefix': '/aws/lambda/'
    }
    if token is not None:
        option['nextToken'] = token

    resp = cw_logs_client.describe_log_groups(**option)
    result = resp.get('logGroups', [])

    if 'nextToken' in resp:
        result += get_log_groups(resp['nextToken'])
    return result


def get_lambda_functions(token: str=None) -> List[Dict]:
    option = {}
    if token is not None:
        option['Marker'] = token

    resp = lambda_client.list_functions(**option)
    result = resp.get('Functions', [])

    if 'NextMarker' in resp:
        result += get_lambda_functions(resp['NextMarker'])
    return result


def convert(data: List[Dict], name: str) -> List[str]:
    """
    [{'xxx': 'apple', 'yyy': 'mac'}, {'xxx': 'orange', 'yyy': 'windows'}]
    みたいなDictの配列から
    ['apple', 'orange']
    みたいな特定のKeyの値だけのリストに変換する
    """
    return [x.get(name) for x in data]


def delete_log_groups(log_groups: List[str]) -> None:
    for item in log_groups:
        cw_logs_client.delete_log_group(
            logGroupName=f'/aws/lambda/{item}'
        )


if __name__ == "__main__":
    main()

使ってみる

実行するだけです。

python app.py

結果は下記です。ロググループ名は省略しています。

xxx
yyy
zzz
-----
Log Group: 56
Lambda   : 31
different: 27

さいごに

そこそこの数がありました。ついでに削除してスッキリしました。

参考