EC2の起動直後のCloudWatchアラームを抑止する

起動直後のCloudWatchアラームの通知を抑止したいという要望があったので、AWSサービスを組み合わせて実現してみました。

#構成
EventBridge + Lambdaで実現します。
EC2起動直後の一定時間を待つために、起動時にはStep FunctionsからLambdaを呼び出します。

EventBridgeやStep Functionsは応用の幅が広いので、その一例としても役立ちそうです。

# Lambdaコード
まずはLambdaコードから見ていきます。

```python
import boto3

target = {
    "test-instance" : ['test-alarm']
}

def lambda_handler(event, context):
    
    client = boto3.client('cloudwatch')
    
    if event["Action"] == 'enable':
        response = client.enable_alarm_actions(
            AlarmNames=target[event["Target"]]
        )
    
    if event["Action"] == 'disable':
        response = client.disable_alarm_actions(
            AlarmNames=target[event["Target"]]
        )
```
EventBridgeからキーバリューを受け取ることを前提としたコードです。
Actionキーでアラームの有効or無効を切り替えます。TargetキーでEC2を識別します。ここではインスタンス名(test-instance)とします。あらかじめインスタンス名に対応したアラーム名(test-alarm)を辞書型のtargetで定義しておきます。
これにより、EventBridgeで指定したインスタンスに対応するアラームの有効化or無効化を実行することができます。

##IAMポリシー
Lambda用のIAMポリシーです。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "cloudwatch:EnableAlarmActions",
                "cloudwatch:DisableAlarmActions",
                "logs:CreateLogGroup",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}
```
標準で付与されるログ周りを除くと、EnableAlarmActionsとDisableAlarmActionsが必要です。
これらのアクションはAWSコンソールからは実行できず、LambdaやCLIで実行する必要があるようです。

#Step Functionsステートマシン
EC2起動時に実行するステートマシンを定義します。

```json
{
  "StartAt": "Wait",
  "States": {
    "Wait": {
      "Type": "Wait",
      "Seconds": 60,
      "Next": "MainTask"
    },
    "MainTask": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:リージョン名:アカウントID:function:AlarmSwitch",
      "End": true
    }
  }
}
```
リージョン名とアカウントIDは置き換えてください。ステートマシン内で前述のLambdaを呼び出します。ここでは実行までに1分間待つことにします。

## IAMポリシー
ステートマシン用のIAMポリシーです。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                "arn:aws:lambda:リージョン名:アカウントID:function:AlarmSwitch:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                "arn:aws:lambda:リージョン名:アカウントID:function:AlarmSwitch"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "xray:PutTraceSegments",
                "xray:PutTelemetryRecords",
                "xray:GetSamplingRules",
                "xray:GetSamplingTargets"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```
リージョン名とアカウントIDは置き換えてください。xray周りは標準で付与されるものです。前述のLambdaの実行権限を付与しておきます。

# EventBridge

あとはEventBridgeで対象のEC2の起動停止をトリガーにして、先ほどのLambdaとステートマシンを実行します。

## EC2起動
### イベントパターン

```json
{
  "source": ["aws.ec2"],
  "detail-type": ["EC2 Instance State-change Notification"],
  "detail": {
    "state": ["running"],
    "instance-id": ["インスタンスID"]
  }
}
```
インスタンスIDは置き換えてください。

### ターゲット
![SnapCrab_NoName_2021-9-10_10-50-11_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/996764/2ca7bab4-cc6f-f7d7-5968-e28ebf7553d6.png)
先ほどのステートマシンを選択します。Lambdaに引き渡すキーバリューをJSONで指定します。

### IAMポリシー
EC2起動EventBridge用のIAMポリシーです。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "states:StartExecution"
            ],
            "Resource": [
                "arn:aws:states:リージョン名:アカウントID:stateMachine:AlarmSwitchStateMachine"
            ]
        }
    ]
}
```
リージョン名とアカウントIDは置き換えてください。先ほどのステートマシンの実行権限が必要です。

## EC2停止
### イベントパターン

```json
{
  "source": ["aws.ec2"],
  "detail-type": ["EC2 Instance State-change Notification"],
  "detail": {
    "state": ["stopped"],
    "instance-id": ["インスタンスID"]
  }
}
```

### ターゲット
![SnapCrab_NoName_2021-9-10_11-3-17_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/996764/644c7ac8-7b2f-1d3a-ff48-cd8de17a4cc7.png)
停止時は待機しないので、直接Lambdaを呼び出します。AWSコンソールで作成すれば、実行権限はLambdaパーミッションで自動的に付与されます。CloudFormationで作成する際はLambdaパーミッションの設定に注意です。キーバリューもEC2起動と同じ形式で入力します。

# 実行結果
準備が整ったら、対象のEC2インスタンスを起動および停止します。

停止すると、インスタンスに対応したアラームが無効になります。
![SnapCrab_NoName_2021-9-10_11-11-24_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/996764/5c49dab7-253b-caaa-95e7-32b403bb6948.png)

起動すると、ステートマシンが実行され、指定した時間(1分間)待機した後、Lambdaを実行してアラームを有効化します。
![SnapCrab_NoName_2021-9-10_11-12-48_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/996764/94181e42-4530-d6df-5f6a-4b004676c653.png)
![SnapCrab_NoName_2021-9-10_11-14-34_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/996764/e0d63cca-5f08-0f50-5a78-3fa07d426222.png)
![SnapCrab_NoName_2021-9-10_11-18-0_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/996764/915e3b00-336e-2ded-f646-9af30e38a853.png)
これにより、起動後の任意の一定時間、CloudWatchアラームを無効化することができるので、アラームの抑止を実現できました。

今回は停止と起動のセットで動作する構成になりますが、再起動時にも抑止したい場合を考えると、起動時のみをトリガーとしてアラームの有効無効を切り替えるようにするなどの工夫が必要そうです。