メトリクスフィルター設定後、自動的にCloudWatchAlarmが作成されるようにしてみた

# はじめに
こんにちは、山田です。
今回はメトリクスフィルターが設定されたら、自動的にCloudWatchAlarmが作成されるようにLambda関数を作成したので記載していきます。
よろしくお願いいたします。
# 概要図
概要図は以下の通りです。
![](https://storage.googleapis.com/zenn-user-upload/51c99428a733-20221225.png)
①：メトリクスフィルターとCLoudWatchAlarmの一覧を取得する。
②：取得した結果、CloudWatchAlarmに名前がないものに関しては、アラームを作成する。
**※前提条件※
メトリクスフィルター名とCLoudWatchAlarm名は同名とします。**
 # Lambda関数
 作成したLambda関数は以下になります。
 ```python
import json
import boto3
import time

cloudwatch_client = boto3.client('cloudwatch')
logs_client = boto3.client('logs')

def get_ClodWatchAlarm():
    response = cloudwatch_client.describe_alarms()
    cloudwatchalarm=[]
    count = 0
    for i in range(len(response['MetricAlarms'])):
        cloudwatchalarm.append(response['MetricAlarms'][i]['AlarmName'])
        count+=1
    count = 0
    while 'nextToken' in response:
        response = cloudwatch_client.describe_alarms(nextToken=response['nextToken'])
        for i in range(len(response['MetricAlarms'])):
            cloudwatchalarm.append(response['MetricAlarms'][i]['AlarmName'])
            count+=1
    return cloudwatchalarm
    
def get_MetricsFilter():
    response = logs_client.describe_metric_filters()
    metricsfilter=[]
    count = 0
    for i in range(len(response['metricFilters'])):
        metricsfilter.append(response['metricFilters'][i]['filterName'])
        count+=1
    count = 0
    while 'nextToken' in response:
        response = logs_client.describe_alarms(nextToken=response['nextToken'])
        for i in range(len(response['metricFilters'])):
            metricsfilter.append(response['metricFilters'][i]['filterName'])
            count+=1
    return metricsfilter
    
def get_MetricsFilterPattern():
    response = logs_client.describe_metric_filters()
    metricsfilterpattern=[]
    count = 0
    for i in range(len(response['metricFilters'])):
        metricsfilterpattern.append(response['metricFilters'][i]['filterPattern'])
        count+=1
    count = 0
    while 'nextToken' in response:
        response = logs_client.describe_alarms(nextToken=response['nextToken'])
        for i in range(len(response['metricFilters'])):
            metricsfilterpattern.append(response['metricFilters'][i]['filterPattern'])
            count+=1
    return metricsfilterpattern

def get_LogGroup():
    response = logs_client.describe_metric_filters()
    loggroupname = []
    count = 0
    for i in range(len(response['metricFilters'])):
        loggroupname.append(response['metricFilters'][i]['logGroupName'])
        count+=1
    return loggroupname

def put_LogEvent(metricsfilterpattern):
    cloudwatchalarm = get_ClodWatchAlarm()
    metricsfilter = get_MetricsFilter()
    loggroup = get_LogGroup()
    metricsfilter_2 = []
    count=0
    for item in metricsfilter:
        if item not in cloudwatchalarm:
            res = logs_client.describe_log_streams(
            logGroupName=(loggroup[metricsfilter.index(item)]),
            logStreamNamePrefix=(loggroup[metricsfilter.index(item)]),
            )
            seq_token=res['logStreams'][0]['uploadSequenceToken']
            response = logs_client.put_log_events(
            logGroupName=loggroup[metricsfilter.index(item)],
            logStreamName=loggroup[metricsfilter.index(item)],
            logEvents=[
                {
                'timestamp': int(time.time()) * 1000,
                'message': metricsfilterpattern[metricsfilter.index(item)]
                },
            ],
            sequenceToken = seq_token
            )
            metricsfilter_2.append(item)
    return metricsfilter_2

            
def lambda_handler(event, context):
    client = boto3.client('cloudwatch')
    list_cloudwatchalarm = get_ClodWatchAlarm()
    list_metricsfilter_1 = get_MetricsFilter()
    list_loggroup = get_LogGroup()
    list_metricsfilterpattern = get_MetricsFilterPattern()
    list_metricsfilter_2 = put_LogEvent(list_metricsfilterpattern)
    time.sleep(10)
    for item in list_metricsfilter_2:
        response=cloudwatch_client.put_metric_alarm(
        AlarmName=item,
        AlarmDescription=item,
        ActionsEnabled=True,
        AlarmActions=[
         'SNSのARN',
        ],
        MetricName=item,
        Namespace='Logs',
        Statistic='Sum',
        Period=300,
        Unit='Seconds',
        Threshold=1,
        EvaluationPeriods=1,
        ComparisonOperator='GreaterThanOrEqualToThreshold'
        )
 ```
 ## CloudWatchAlarm一覧取得
CloudWatchAlarm一覧を取得するコードは以下になります。
 ```python
 def get_ClodWatchAlarm():
    response = cloudwatch_client.describe_alarms()
    cloudwatchalarm=[]
    count = 0
    for i in range(len(response['MetricAlarms'])):
        cloudwatchalarm.append(response['MetricAlarms'][i]['AlarmName'])
        count+=1
    count = 0
    while 'nextToken' in response:
        response = cloudwatch_client.describe_alarms(nextToken=response['nextToken'])
        for i in range(len(response['MetricAlarms'])):
            cloudwatchalarm.append(response['MetricAlarms'][i]['AlarmName'])
            count+=1
    return cloudwatchalarm
```
CloudWacthAlarmの情報を取得します。
```python
response = cloudwatch_client.describe_alarms()
```
空の配列を用意します。変数countには「0」を代入します。
**len関数**を用いてCloudWatchAlarmの数を取得し、**range関数**を用いて連続数を取得します。
取得した連続数だけfor文にて繰り返し処理を実行します。処理の最後には、変数countの値が+1されるように設定します。
```python
cloudwatchalarm=[]
count = 0
for i in range(len(response['MetricAlarms'])):
　　cloudwatchalarm.append(response['MetricAlarms'][i]['AlarmName'])
　　count+=1

(例)test = ['a' , 'b' , 'c']
range(len(test))
--> 0,1,2
```
responseの中に、**「'nextToken'」** の記載がなくなるまで、for文にて繰り返し処理を実行します。
```python
count = 0
while 'nextToken' in response:
　　response = cloudwatch_client.describe_alarms(nextToken=response['nextToken'])
    for i in range(len(response['MetricAlarms'])):
    　　cloudwatchalarm.append(response['MetricAlarms'][i]['AlarmName'])
        count+=1
```
## メトリクスフィルター・フィルターパターン一覧取得
メトリクスフィルター・フィルターパターン一覧を取得するコードは以下になります。
```python
def get_MetricsFilter():
    response = logs_client.describe_metric_filters()
    metricsfilter=[]
    count = 0
    for i in range(len(response['metricFilters'])):
        metricsfilter.append(response['metricFilters'][i]['filterName'])
        count+=1
    count = 0
    while 'nextToken' in response:
        response = logs_client.describe_alarms(nextToken=response['nextToken'])
        for i in range(len(response['metricFilters'])):
            metricsfilter.append(response['metricFilters'][i]['filterName'])
            count+=1
    return metricsfilter
    
def get_MetricsFilterPattern():
    response = logs_client.describe_metric_filters()
    metricsfilterpattern=[]
    count = 0
    for i in range(len(response['metricFilters'])):
        metricsfilterpattern.append(response['metricFilters'][i]['filterPattern'])
        count+=1
    count = 0
    while 'nextToken' in response:
        response = logs_client.describe_alarms(nextToken=response['nextToken'])
        for i in range(len(response['metricFilters'])):
            metricsfilterpattern.append(response['metricFilters'][i]['filterPattern'])
            count+=1
    return metricsfilterpattern
```
メトリクスフィルター・フィルターパターン一覧取得に関しても、上記のCLoudWatchAlaram取得の仕組みは同様です。
## ロググループ一覧取得
ロググループ一覧を取得するコードは以下になります。
```python
def get_LogGroup():
    response = logs_client.describe_metric_filters()
    loggroupname = []
    count = 0
    for i in range(len(response['metricFilters'])):
        loggroupname.append(response['metricFilters'][i]['logGroupName'])
        count+=1
    return loggroupname
```
ロググループ一覧に関しては、メトリクスフィルタが設定済みのロググループのみを取得するようにしています。
## ログイベント発行
ログイベントを発行するコードは以下になります。
```python
def put_LogEvent(metricsfilterpattern):
   cloudwatchalarm = get_ClodWatchAlarm()
   metricsfilter = get_MetricsFilter()
   loggroup = get_LogGroup()
   metricsfilter_2 = []
   count=0
   for item in metricsfilter:
       if item not in cloudwatchalarm:
           res = logs_client.describe_log_streams(
           logGroupName=(loggroup[metricsfilter.index(item)]),
           logStreamNamePrefix=(loggroup[metricsfilter.index(item)]),
           )
           seq_token=res['logStreams'][0]['uploadSequenceToken']
           response = logs_client.put_log_events(
           logGroupName=loggroup[metricsfilter.index(item)],
           logStreamName=loggroup[metricsfilter.index(item)],
           logEvents=[
               {
               'timestamp': int(time.time()) * 1000,
               'message': metricsfilterpattern[metricsfilter.index(item)]
               },
           ],
           sequenceToken = seq_token
           )
           metricsfilter_2.append(item)
   return metricsfilter_2
```
CloudWatchAlarm一覧とメトリクスフィルター一覧を比較し、CloudWatchAlarm一覧に名前がないメトリクスフィルターが設定されているロググループにログを発行します。
発行するログのメッセージに関しては、メトリクスフィルターのフィルターパターンに記載してある文字列を発行します。
```python
cloudwatchalarm = get_ClodWatchAlarm()
metricsfilter = get_MetricsFilter()
loggroup = get_LogGroup()
```
CloudWatchAlarm、メトリクスフィルター、ロググループの一覧を取得します。
```python
metricsfilter_2 = []
```
空の配列を用意します。
```python
count=0
   for item in metricsfilter:
       if item not in cloudwatchalarm:
           res = logs_client.describe_log_streams(
           logGroupName=(loggroup[metricsfilter.index(item)]),
           logStreamNamePrefix=(loggroup[metricsfilter.index(item)]),
           )
           seq_token=res['logStreams'][0]['uploadSequenceToken']
           response = logs_client.put_log_events(
           logGroupName=loggroup[metricsfilter.index(item)],
           logStreamName=loggroup[metricsfilter.index(item)],
           logEvents=[
               {
               'timestamp': int(time.time()) * 1000,
               'message': metricsfilterpattern[metricsfilter.index(item)]
               },
           ],
           sequenceToken = seq_token
           )
```
CloudWatchAlarm一覧に名前がないメトリクスフィルターが設定されているロググループにログを発行します。
発行するログのメッセージに関しては、メトリクスフィルターのフィルターパターンに記載してある文字列を発行します。
```python
metricsfilter_2.append(item)
return metricsfilter_2
```
ログ発行後、空の配列にメトリクスフィルター名を追加します
戻り値として、**「metiricsfilter_2」** を返します。
## CloudWatchAlarm作成
CloudWatchAlarmを作成するコードは以下になります。
```python
def lambda_handler(event, context):
    list_cloudwatchalarm = get_ClodWatchAlarm()
    list_metricsfilter_1 = get_MetricsFilter()
    list_loggroup = get_LogGroup()
    list_metricsfilterpattern = get_MetricsFilterPattern()
    list_metricsfilter_2 = put_LogEvent(list_metricsfilterpattern)
    time.sleep(10)
    for item in list_metricsfilter_2:
        response=cloudwatch_client.put_metric_alarm(
        AlarmName=item,
        AlarmDescription=item,
        ActionsEnabled=True,
        AlarmActions=[
         'SNSのARN',
        ],
        MetricName=item,
        Namespace='Logs',
        Statistic='Sum',
        Period=300,
        Unit='Seconds',
        Threshold=1,
        EvaluationPeriods=1,
        ComparisonOperator='GreaterThanOrEqualToThreshold'
        )
```
上記に記載した関数をそれぞれ実行し、最後にCloudWatchAlarmを作成するコードを記載しています。
```python 
list_cloudwatchalarm = get_ClodWatchAlarm()
list_metricsfilter_1 = get_MetricsFilter()
list_loggroup = get_LogGroup()
list_metricsfilterpattern = get_MetricsFilterPattern()
list_metricsfilter_2 = put_LogEvent(list_metricsfilterpattern)
```
上記記載の関数をそれぞれ実行し、情報を変数に代入しています。
```python
time.sleep(10)
```
CloudWatchにメトリクスが反映されるのを待つため、CloudWatchAlarmの作成を10秒待機します。
```python
for item in list_metricsfilter_2:
    response=cloudwatch_client.put_metric_alarm(
    AlarmName=item,　#アラーム名
    AlarmDescription=item, #アラームの説明
    ActionsEnabled=True,　
    AlarmActions=[
     'SNSのARN',　#Alarm状態時の通知先
    ],
    MetricName=item, #メトリクス名
    Namespace='Logs', #名前空間
    Statistic='Sum', #合計
    Period=300, #時間
    Unit='Seconds', #単位(秒)
    Threshold=1, #しきい値
    EvaluationPeriods=1, #評価期間
    ComparisonOperator='GreaterThanOrEqualToThreshold' #しきい値以上の時にアラーム発行
    )
```
メトリクスフィルター名と同名のCLoudWatchAlarmを作成します。
# 動作確認
動作確認のためにメトリクスフィルター設定後に、作成したLambda関数を実行します。
![](https://storage.googleapis.com/zenn-user-upload/16d7626f2b12-20221225.png)
メトリクスフィルターを設定します。
![](https://storage.googleapis.com/zenn-user-upload/fad8fb26b41f-20221225.png)
Lambda関数を実行します。
![](https://storage.googleapis.com/zenn-user-upload/654ef3f34756-20221225.png)
CloudWatchAlarmが作成されていることが確認できれば完了です。
今回は手動で実行しましたが、EventBridgeにスケジューリングすれば定期的に実行できます。
# 終わりに
今回は、メトリクスフィルターが設定されたら、自動的にCloudWatchAlarmが作成されるLambda関数を作成したので記載していきました。
Lambda関数の練習がてらに、CloudWatchAlarmを自動化するコードを作成してみました。作成しみたはいいものの、自分の実力はまだまだなんだと実感しました。
これからも、自動化できる箇所についてはLambda関数を用いて自動化していこうと思います。
拝見いただきありがとうございました。