CloudwatchのEC2自動復旧をlambdaで自動一括設定

Cloudwatchの機能を使ってEC2の自動復旧（オートリカバリー）を設定することができますが、管理しているEC2が増えてくるとアラーム設定自体がとても面倒で、かつ設定漏れのリスクも出てきます
そこで、EC2自動復旧の自動一括設定ができるようにします

#前提条件
+ 自動復旧がサポートされているEC2インスタンスタイプであること
 [サポートされているEC2を確認](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/UsingAlarmActions.html)
+ Cloudwatch利用

#lambdaを使ってEC2のオートリカバリーを自動設定する
Cloudwatchで、EC2の「StatusCheckFailed_System」を検知し自動再起動する設定ができます
lambdaを使って、特定のタグを設定したEC2に対して、オートリカバリーのCloudwatchアラームを自動一括設定できるようにします

##まずは、lambdaを作成します
python 3.9でlambda関数を作成しました

```python:putcloudwatchalarm.py
import json
import boto3
import config

# Cloudwatchアラーム設定関数
def put_cwalarm(instanceId, ec2_name):
    
    print ("instanceId = ", instanceId)
    print ("ec2 name tag = ", ec2_name)
    cloudWatch   = boto3.client('cloudwatch')
    result = cloudWatch.put_metric_alarm(
        AlarmName          = config.ALARM_NAME + "_" + instanceId,
        AlarmDescription   = ec2_name + config.ALARM_DESC,
        AlarmActions       = [
            config.ALARM_TOPIC,
            config.ALARM_ACTION
        ],
        OKActions       = [
            config.ALARM_TOPIC
        ],
        MetricName         = config.METRIC_NAME,
        Namespace          = config.NAMESPACE ,
        Statistic          = config.STATISTICS,
        Dimensions         = [{"Name": "InstanceId", "Value": instanceId}],
        Period             = config.ALARM_PERIOD,
        EvaluationPeriods  = config.EVALUATION_PERIOD,
        Threshold          = config.THRESHOLD,
        ComparisonOperator = config.COMPARISON
    )
    
    print ("responce = ", result)
    #put metric alarmに対する戻り値の中のHTTPStatusCodeのみをリターンする
    return result["ResponseMetadata"]["HTTPStatusCode"]

def lambda_handler(event, context):
    
    msg = ""
    err_msg = ""
    ins_id = ""
    
    #TAG KEYが設定されているEC2に対しCloudwatchアラーム設定関数を実行する
    for reservation in boto3.client('ec2').describe_instances(Filters=[{'Name': 'instance-state-name','Values': ['running']}])["Reservations"]:
        for instance in reservation["Instances"]:
            for tag in instance["Tags"]:
                if(tag["Key"] == config.TAG_KEY and tag["Value"] == config.TAG_VALUE):
                    #InstanceIdを取得する
                    ins_id  = instance["InstanceId"]
                    #Name tagを取得する
                    ec2_tags = dict([(tag['Key'], tag['Value']) for tag in instance['Tags']])
                    name_tag = ec2_tags['Name']
                    #alarmを設定する
                    res = put_cwalarm(ins_id, name_tag)

                    #200OK以外はエラーをメッセージに入れておく
                    if(res == 200) :
                        msg = "[OK]" + ins_id + " : created/updated alert" + msg + " / "
                    else :
                        err_msg = "[ERROR]" + ins_id + " : failed alert create" + err_msg + " / "
   
    return {
        'statusCode': 200,
        'body': msg,
        'err': err_msg 
    }
```

環境設定値は別ファイルにしました
対象のタグ名、閾値等はお好みで設定してください、下記は参考です

```python:config.py
 ALARM_ACTION       = "arn:aws:automate:ap-northeast-1:ec2:recover"
 METRIC_NAME        = "StatusCheckFailed_System"
 NAMESPACE          = "AWS/EC2"
 STATISTICS         = "Maximum"
 ALARM_PERIOD       = 60
 EVALUATION_PERIOD  = 2
 THRESHOLD          = 1
 COMPARISON         = "GreaterThanOrEqualToThreshold"
```

##lambdaの設定
+ 対象のEC2が多い場合は実行時間がかかるので、lambdaのタイムアウトを長く設定しておいた方がタイムアウトエラーを回避できます
+ EC2とCloudwatchへのアクセスが必要なので、ロールを付与しておいてください
+ アラーム通知先のSNSを先に作成しておいてください

##EC2の設定
+ TAGを設定しておいてください

##実行
上記のlambda関数に引数は必要ないので、トリガー設定なしでそのまま実行すればOK
lambdaのコンソールからTestで実行してしまいましょう

#結果
Cloudwatchコンソールを確認し、アラームが設定されていれば成功です
![スクリーンショット 2022-02-10 20.51.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1989777/3d958a62-c544-4680-c77a-da2de5911cfc.png)

#閾値等の設定値を変更したい時、対象のEC2が増えた時
lambdaの設定値を変更して、再度lambdaを実行するだけでOKです
既存のアラームは更新され、そうではない場合は新規アラームが作成されます
アラーム名などCloudwatchで変更不可能の要素は変更できないので注意です