既存のロググループ用の「CloudWatch Logsの異常検出を設定するCloudFormationテンプレート」を作成するスクリプトを作ってみた #AWSreInvent

既存のロググループに簡単に適用していきましょう。
2023.12.08

Amazon CloudWatch Logsの異常検出ができるようになりました。 とはいえ、既存のロググループがたくさんあるとき、ひとつひとつ設定するのは大変です。 というわけで、「既存のロググループの一覧を取得して、CloudWatch Logsの異常検出を設定するCloudFormationテンプレート」を作成するスクリプトを作ってみました。

おすすめの方

  • CloudWatch Logsの異常検出をCloudFormationで設定したい方
  • CloudWatch Logsの異常検出を既存のロググループになるべく簡単に適用したい方

今回のお試し対象

今回は、ロググループのprefixが「/aws-glue」である3つのロググループで試してみます。

01_cloudwatch_logs

「CloudWatch Logsの異常検出を設定するCloudFormationテンプレート」を作成するスクリプト

次のパラメータをいい感じにすると便利です。(どちらかひとつだけを指定できます)

  • logGroupNamePrefix
  • logGroupNamePattern

なお事前に次のライブラリをインストールしておきます。

app.py

# pip install pyyaml
# pip install boto3

import yaml
import boto3

logs = boto3.client("logs")

base = {
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "CloudWatch Logs Anomaly Detector Sample made by Python",
    "Resources": {},
}


RESOURCE_NAME_SUFFIX = "LogAnomalyDetector"

RESOURCE_NAME_MAX_LENGTH = 255

OUTPUT_FILE_NAME = "cloudwatch_log_anomaly_detector.yaml"


def main():
    log_groups = get_log_groups()

    for log_group in log_groups:
        resource = make_cloudwatch_logs_anomaly_detector(log_group)
        base["Resources"].update(resource)

    with open(OUTPUT_FILE_NAME, "w") as f:
        yaml.dump(base, f, default_flow_style=False, sort_keys=False)


def get_log_groups():
    log_groups = []
    options = {
        "logGroupNamePrefix": "/aws-glue",
        # "logGroupNamePrefix": "/aws/lambda",
        # "logGroupNamePattern": ".*",
        "logGroupClass": "STANDARD",
        # "limit": 5,
    }
    while True:
        resp = logs.describe_log_groups(**options)
        log_groups += resp["logGroups"]
        if "nextToken" not in resp:
            break
        options["nextToken"] = resp["nextToken"]
    return log_groups


def make_cloudwatch_logs_anomaly_detector(log_group: dict[str, str]):
    log_group_name = log_group["logGroupName"]
    log_group_arn = log_group["arn"]
    return {
        convert_resource_name(log_group_name): {
            "Type": "AWS::Logs::LogAnomalyDetector",
            "Properties": {
                "DetectorName": log_group_name,
                "AnomalyVisibilityTime": 21,
                "EvaluationFrequency": "FIFTEEN_MIN",
                "LogGroupArnList": [convert_arn(log_group_arn)],
            },
        }
    }


def convert_resource_name(log_group_name: str):
    # https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html
    resource_name = (
        log_group_name.replace("_", "")
        .replace("-", "")
        .replace("/", "")
        .replace(".", "")
        .replace("#", "")
    )

    return resource_name[: RESOURCE_NAME_MAX_LENGTH - len(RESOURCE_NAME_SUFFIX) - 1] + RESOURCE_NAME_SUFFIX


def convert_arn(arn: str) -> str:
    # 末尾の「:*」を削る
    return arn[:-2]


if __name__ == "__main__":
    main()

スクリプトを実行して、CloudFormationテンプレートを作成する

python app.py

作成されたCloudFormationテンプレート

cloudwatch_log_anomaly_detector.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: CloudWatch Logs Anomaly Detector Sample made by Python
Resources:
  awsgluecrawlersLogAnomalyDetector:
    Type: AWS::Logs::LogAnomalyDetector
    Properties:
      DetectorName: /aws-glue/crawlers
      AnomalyVisibilityTime: 21
      EvaluationFrequency: FIFTEEN_MIN
      LogGroupArnList:
      - arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws-glue/crawlers
  awsgluejobserrorLogAnomalyDetector:
    Type: AWS::Logs::LogAnomalyDetector
    Properties:
      DetectorName: /aws-glue/jobs/error
      AnomalyVisibilityTime: 21
      EvaluationFrequency: FIFTEEN_MIN
      LogGroupArnList:
      - arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws-glue/jobs/error
  awsgluejobsoutputLogAnomalyDetector:
    Type: AWS::Logs::LogAnomalyDetector
    Properties:
      DetectorName: /aws-glue/jobs/output
      AnomalyVisibilityTime: 21
      EvaluationFrequency: FIFTEEN_MIN
      LogGroupArnList:
      - arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws-glue/jobs/output

ロググループのARNが複数指定できるように見えますが、ひとつのみ指定できます。 そのため、ロググループの数だけリソースを作成しています。

The ARN of the log group that is associated with this anomaly detector. You can specify only one log group ARN.

AWS::Logs::LogAnomalyDetector - AWS CloudFormation

デプロイする

aws cloudformation deploy \
    --template-file cloudwatch_log_anomaly_detector.yaml \
    --stack-name CloudWatch-Logs-Anomaly-Detector-glue-Sample-Stack \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

CloudWatch Logsの様子を見る

しっかりデプロイされていました。

11_cloudwatch_logs_anomaly_detector

12_cloudwatch_logs_anomaly_detector

13_cloudwatch_logs_anomaly_detector

AWS CLIで異常ディテクターの一覧を取得する

今回デプロイした3つの異常ディテクターがあります。 他の2つは以前にデプロイした異常ディテクターです。

aws logs list-log-anomaly-detectors \
    --query 'anomalyDetectors[*].detectorName'

[
    "lambda-2-log-anomaly-detector-sample",
    "/aws-glue/jobs/output",
    "/aws-glue/jobs/error",
    "/aws-glue/crawlers",
    "lambda-1-log-anomaly-detector-sample"
]

参考