AWS のリソースを監視して Slack に通知する方法 (または cloudwatch-alarm-to-slack の使い方)

# これは何？

![ss.png](https://qiita-image-store.s3.amazonaws.com/0/66421/e7272b9c-5de2-7be8-bd0c-a45dea46edd3.png)

* AWS のリソース監視を行い、アラートを Slack に通知する方法をまとめました
* いろいろやりようはあると思いますが、今回はすべて AWS の機能でまかないます
    * 具体的には、Cloudwatch でアラートを設定し、Lambda から Slack に通知します
        * さらにいうと、cloudwatch-alarm-to-slack という既存の Serverless Application Repository を使います

# 前提

* Serverless Application Repository （以下 SAR）という、あらかじめ構築された Lambda スタックをカタログから選ぶだけで簡単にデプロイできる機能がある
* 今回はこの SAR の中から、AWS 謹製の cloudwatch-alarm-to-slack というスタックを使う
* ただしこの Lambda は、通知先の Slack Webhook URL を暗号化したものを事前に設定する必要がある（それを関数内で復号している）
* その暗号化 / 復号には AWS の KMS で作った鍵を使うので、その準備も必要となる
    * KMS についてはこちらがわかりやすい : https://dev.classmethod.jp/cloud/aws/10minutes-kms/

# 手順

## Slack の Incoming Webhook を作る

* すでに説明記事がたくさんあると思うので割愛します

## KMS で鍵をつくる

* IAM にアクセス
* 画面左下の `Encryption keys` を開き、 `Create Key` をクリック
    * おそらく東京リージョンで使うと思うので、ちゃんとそうなっているか確認すること
* 下記の 5 ステップに従って鍵を作る

### Step 1 : Create Alias and Description

* `Alias`
    * わかりやすい名前をつける
* `Description`
    * わかりやすい説明を書く

### Step 2 : Add Tags 
* 必要ならタグを設定する

### Step 3 : Define Key Administrative Permissions

* 鍵を管理するユーザーを選択
    * ここでユーザーではなくグループを選べるとよいのだが、なぜか選べない
    * 仕方なくシステム担当者を1人ずつ設定した

### Step 4 : Define Key Usage Permissions

* 鍵を使うユーザーを選択するのだが、このあと作るロールを設定したいので、今は何もせず次に進んでよい

###  Step 5 : Preview Key Policy 

* ぼんやり眺めたら `Finish` をクリック

## Lambda を作る

* Lambda にアクセス
* `Create function` をクリック
* `AWS Serverless Application Repository` を選択し、 `cloudwatch-alarm-to-slack` を検索
    * 無印（js）と python のものがヒットするが、今回は無印をチョイス

![image.png](https://qiita-image-store.s3.amazonaws.com/0/66421/72acdf1c-0ca9-7666-5250-491ecb8641bf.png)


### パラメータの設定

* `Configure application parameters` を埋めていく
    * `Application name`
        * そのままでいいと思う
    * `KeyIdParameter`
        * 先ほど作った鍵の Key ID を入力
    * `kmsEncryptedHookUrlParameter`
        * 自分が何か間違えたのかもしれないが、 **あとで入力し直すハメになるので一旦適当でよい**
    * `slackChannelParameter`
        * これも **一旦適当でよい**

![image.png](https://qiita-image-store.s3.amazonaws.com/0/66421/2526c79a-91c0-80c5-a7e9-43977d10c4a9.png)

* `Deploy` をクリック
    * 内部的に CloudFormation が実行され、下記の 5 つのリソースが作成される
        * AWS::SNS::Topic
        * AWS::SNS::Subscription
        * AWS::Lambda::Function
        * AWS::Lambda::Permission
        * AWS::IAM::Role
            * このロールを、次の手順で鍵のユーザーに設定します

## 鍵のユーザーを設定する

* KMS のページに戻り、最初に作った鍵のページを開く
* `Key Users` -> `Add` から、ついさっき作られた IAM Role を設定
    * `aws-serverless-repository-cloudwatchalarmtoslack......` みたいな名前のはず

## Lambda を設定する

* 作成された Function のページを開く

### 暗号化関連の設定

* `Environment variables` 欄の `Encryption configuration` を開き、暗号化関連の設定を行う
    * `Enable helpers for encryption in transit`
        * チェックを入れる
        * これによって、Slack の Webhook URL をブラウザ操作で簡単に暗号化できるようになる
    * `AWS KMS key to encrypt in transit`
        * 今回作った鍵を設定
        * 上述したブラウザ操作での暗号化は、ここで設定した鍵が使われる
    * `AWS KMS key to encrypt at rest`
        * `Use a customer master key` を選んだうえで、今回作った鍵を設定
        * Lambda 関数内での復号処理には、ここで設定した鍵が使われる

### 環境変数を設定

* 初期状態では `kmsEncryptedHookUrlParameter` `slackChannelParameter` といった値になっているはずだが、これだと動かない
* `kmsEncryptedHookUrl`
    * Slack の Webhook URL を、 **`https://` を抜いて** 入力する
    * `Encrypt` ボタンをクリックすると暗号化される
* `slackChannel`
    * 通知したい Slack チャンネル名を入力する
    * こちらは平文でよいので暗号化しなくて OK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/66421/9a92415a-ecec-1771-f224-e18cc57988ea.png)

* 終わったら、画面右上の `Save` ボタンをクリック

## CloudWatch の Alarm を作成

* CloudWatch を開く
* 画面左のメニューから `Alarms` を開き、 `Create Alarm` ボタンをクリック
* Alarm を作っていく
    * 監視できる項目が大量にあって面食らうかもしれないが、設定自体は難しいところはないので詳細は割愛
    * ステップ2 `Define Alarm` の `Actions` で、`Send notification to:` から今回作られた SNS Topic を選べば OK
* Alarm が発火するのを待ち、通知が飛んでくれば OK
    * ダミーのアラートを発火する機能があればよいのだが、残念ながら無いので、閾値をめちゃくちゃ下げるなどして対応する
    * 通知がこない場合、CloudWatch にログが保存されているはずなのでそれを確認する

![ss.png](https://qiita-image-store.s3.amazonaws.com/0/66421/e7272b9c-5de2-7be8-bd0c-a45dea46edd3.png)

# 参考

* https://qiita.com/hayao_k/items/13608ca88645fe8477ae
    * そんなに前の記事ではないのだが、早くも一部変更があったので、私の記事の寿命も長くなさそう