【AWS config】設定変更時のみ独自の形式で通知を送る

# はじめに

### やりたかったこと
「AWS Configで検知したリソースの設定変更をAmazon SNSを使用してメールで通知」

![config通知.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/9d34aa54-db6d-3bf1-bd12-70883f35156a.png)


具体的には、監視対象のリソースの設定が「変更・削除・追加」されたときにSNSからメールが送られてくるような仕組みができないかなぁと考えていました。

実は変更を取得してSNSのトピックへ情報を送る仕組みは、すでにAWS Configにあり、設定も簡単で使用している方も多いかと思うのですが、デフォルトの通知機能を使用すると以下が行われたすべての場合に通知が来てしまいます。

- リソースの設定項目の変更。
- リソースの設定履歴がアカウントに配信された。
- 記録対象のリソースの設定スナップショットがアカウントで起動および配信された。
- リソースのコンプライアンス状態とリソースがルールに準拠するかどうか。
- リソースに対してルールの評価が開始された。
- AWS Config からアカウントに通知を配信できなかった。


(参照)-https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/notifications-for-AWS-Config.html


これらのメールをみて変更を確認することはできるのですが、通知の量が多くなってしまいわかりにくくなることがあります。

また、Json形式の「イベント」と呼ばれる変更情報がそのまま送られてくるため、Configをあまり知らない人がメールをみると「正直よくわからん...」ってなります。

通知メールを独自の形に編集できたりしたらいいですよね。

ということでカスタムルールを用いた方法を書きます。

### カスタムルールについて
AWS Configでは、カスタムルールを設定することで自らのルールに沿ってリソースを評価することができます。

例えば
>セキュリティゲートの○○番ポートと○○番ポートのみ開いている場合はいいけどそれら以外が開いていたらまずい!!!から状態を検知したい!!!

みたいな感じです。

変更が起こった場合や定期的な監視でイベントをLambdaに送り、Lambda内でそのイベントがルールに準拠しているか判断します。
準拠している場合、Configに「COMPLIANT」を返し、準拠していない場合「NON_COMPLIANT」を返します。

Configでは、Lambdaからの返答をもとに、「COMPLIANT」が返ってきた場合「準拠」、「NON_COMPLIANT」が返ってきた場合「非準拠」と表示することで一目でリソースの状態がわかるようになります。

これを使用すれば自分の好きな変更が起こったタイミングでユーザに通知をおくることができます。

# 設定方法

### Lambdaの作成(Python)

まずカスタムルールのLambdaの作成に移ります。

変更が行われた際にLambdaにConfigから送られてくるイベントのサンプルが以下となります。

```
{ 
    "invokingEvent": "{\"configurationItem\":{\"configurationItemCaptureTime\":\"2016-02-17T01:36:34.043Z\",\"awsAccountId\":\"123456789012\",\"configurationItemStatus\":\"OK\",\"resourceId\":\"i-00000000\",\"ARN\":\"arn:aws:ec2:us-east-2:123456789012:instance/i-00000000\",\"awsRegion\":\"us-east-2\",\"availabilityZone\":\"us-east-2a\",\"resourceType\":\"AWS::EC2::Instance\",\"tags\":{\"Foo\":\"Bar\"},\"relationships\":[{\"resourceId\":\"eipalloc-00000000\",\"resourceType\":\"AWS::EC2::EIP\",\"name\":\"Is attached to ElasticIp\"}],\"configuration\":{\"foo\":\"bar\"}},\"messageType\":\"ConfigurationItemChangeNotification\"}",
    "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}",
    "resultToken": "myResultToken",
    "eventLeftScope": false,
    "executionRoleArn": "arn:aws:iam::123456789012:role/config-role",
    "configRuleArn": "arn:aws:config:us-east-2:123456789012:config-rule/config-rule-0123456",
    "configRuleName": "change-triggered-config-rule",
    "configRuleId": "config-rule-0123456",
    "accountId": "123456789012",
    "version": "1.0"
}
```
(参照)-https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/evaluate-config_develop-rules_example-events.html

これをLambdaのテストに設定して作成していきます。
実際の変更ではないのでコンプライアンスをreturnするとエラーメッセージが出ますが、メールを送ったり、Jsonの中身を見る分には問題ないです。

Lambdaのアクセス権限周りなのですが、


- SNSへのアクセス権限
- Configにコンプライアンスを返すための「AWSConfigRulesExecutionRole」
- エラー出力などのためのCloudWatchLogsアクセス権限

あたりが必要となってきます。
ドキュメントは以下のようになりました。
そこまで煮詰められているわけではなく、不要なものもあると思うので自環境に合わせて設定ください。

・SNSのアクセスポリシー

```
{
    "Version": "2012-10-17",
    "Statement": [
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "arn:aws:sns:<Region>:<AccountID>:<TOPIC名>"
        }
    ]
}

```
・コンプライアンス返却用のConfigアクセスポリシー(AWS管理ポリシーなので検索窓から探してアタッチしてください。)

![rule3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/f9043ef7-6adf-babf-4981-e71ec4181a6b.png)

・CloudWatchLogsへのアクセスポリシー

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:<Region>:<AccountID>:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:<Region>:<AccountID>:log-group:/aws/lambda/<関数名>:*"
            ]
        }
    ]
}
```


ここから本文を書いていきます。

Lambdaにイベントが送られてきたらLambda_handlerでパースなどを行ってメールに添付する情報を抜き出すようにします。

`invokingEvent`に変更情報などが載っているので、抜き出します。

```
import json
import boto3

def lambda_handler(event, context):
   type = event['invokingEvent']
   data = json.loads(type)
   msg_main=json.dumps(data, indent=3)

```

このように書けば整形したJsonを取得できます。

この情報から変更内容と正常な状態を比較してコンプライアンスに準拠しているか判断してください。
この情報をメッセージ本文にしても割といい感じな通知ができます。


次にConfigにコンプライアンスをreturnする形式を紹介します。

```
config = boto3.client("config")
config.put_evaluations(
    Evaluations=[
        {
            "ComplianceResourceType":[対象のリソースタイプ],
            "ComplianceResourceId":[対象のリソースID],
            "ComplianceType":["COMPLIANT"を返すとルール準拠と判断],
            "Annotation":"This resource is compliant with the rule.",
            "OrderingTimestamp":data['configurationItem']["configurationItemCaptureTime"]
        },
    ],
    ResultToken=event["resultToken"]
)
```
(参照)-https://docs.aws.amazon.com/config/latest/APIReference/API_PutEvaluations.html

ただカスタムルールを試したいだけの時や、すべての変更で「コンプライアンス準拠」になっても問題ない方は、

```
 "ComplianceType":"COMPLIANT",
```
とすると、**どんな変更でもコンプライアンス準拠が返ります。(非推奨)**

また、Lambda内でreturnを行わないと、AWS Configが**エラーと検知して、数時間後に再度Lambdaの呼び出しがかかります。**
なので、必ず返してください。

(COMPLIANTとNON_COMPLIANT以外にもタイプはいくつかあります。詳しくは以下を参照下さい)

(参照)-https://docs.aws.amazon.com/ja_jp/config/latest/APIReference/API_Compliance.html

最後にメッセージをSNSに送ります。

```
def sent_message(sbj,message):
    request = {
    'TopicArn': TOPIC_ARN[Emailが設定してあるTOPICのARN],
    'Message': message,
    'Subject': sbj
    }
    
    response = sns.publish(**request)
```

設定タイムラインのURLを本文に載せるとかなりいい感じになります。

```
"https://console.aws.amazon.com/config/home?region="+str(data['configurationItem']['awsRegion'])+"#/timeline/"+str(data['configurationItem']['resourceType'])+"/"+str(data['configurationItem']['resourceId'])+"/configuration\n"
```
### Configの作成
 
次にConfigを立ち上げ、カスタムルール作成に移ります。

カスタムルール設定欄のARNには、先ほど作成したLambdaのARNを載せてください。

![rule2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/c0d618a4-1036-04af-c421-dcbfcdacb213.png)


カスタムルールのトリガーは今回の場合「追加・変更・削除が起こった場合」となるため、設定変更を選択します。![rule1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/ff084f8e-4537-9cbe-7c1c-0ea675e41604.png)

変更範囲は監視したいリソースが含まれるようにして下さい。

(余談ですが、ここでタグを選択すると「特定のタグが付いているリソースのみ」の監視が可能になります。
同じアカウントに別システムのインスタンスなどが立ち上がっていると、分けて監視したい際にはかなり困りますが、タグを利用すると解決できます。)


カスタムルールを作成すると、リソース変更時にメールで通知されるようになります。
通知されない場合はCloudWatchLogsでLambdaのログを確認し、エラーが出ていないか確認してください。

# まとめ
「リソース変更を検知したら独自の形式のメールを送りたい」ということを実装しました。

Configの通知内容をカスタマイズできるようなものが公式で出るといいですね。

#参照
[AWS Config Rules カスタムルールを作成してみよう][link-1]
[link-1]:https://qiita.com/kempe/items/52ff78b65fc04209f734

[AWS Configの通知内容をLambdaで整形][link-2]
[link-2]:https://qiita.com/tomoki_s/items/0d3b249da6ce9cbafdeb