AWS DocumentDBのステータスを通知するLambda関数

タイトル通りのLambda関数を作ろうと思いましたがPythonだとrequestsモジュール等をレイヤーにして
読み込まないといけないとか色々面倒に感じたのでボツに。
(結局のところ、Rubyで書きました、aws-sdk読み込むだけで特に面倒な手順はないので・・・)

自分の思い出としてこちらに残します。

※動作確認はしていないので、参考程度にお願いします。
※ご指摘、改善点あればコメントください。

# 仕様
大雑把に説明するとDocumentDBクラスターとDocumentDBクラスターに紐づくDocumentDBインスタンスのステータスをチェックし、「available」以外のステータスのものがあればメールを送信します。

# 前提
監視対象のAWS DocumentDBクラスターが存在していること

# 事前準備
本筋ではないので割愛します。

- Lambda関数の作成
- Lambda関数のロールにポリシー追加(AmazonDocDBReadOnlyAccess, AmazonSNSFullAccess)
- requestsモジュール等を動作させるためにレイヤーを作る([こちら](https://qiita.com/afukuma/items/b7191025700a7829967c)の方の記事が参考になります)
- Lambda関数の環境変数の設定(TOPIC_ARN, DB_CLUSTER_IDENTIFIER)
- Amazon SNSのトピック作成
- Amazon SNSのトピックにサブスクリプション追加(メールの送信先)

今時メールじゃなくてもSlackやTeams連携の方が利便性は高いかもしれませんね。

# 実際のソース
lambda_function.py

```Python
# -*- coding: utf-8 -*-

import os
import boto3
import requests
import logging

# Lambda関数の環境変数に設定した値を取得
# AWS SNSトピックのARN(メール送信先)
TOPIC_ARN = os.environ['TOPIC_ARN']
# DocumentDBクラスター識別子(ステータス取得対象のクラスターを指定)
DB_CLUSTER_IDENTIFIER = os.environ['DB_CLUSTER_IDENTIFIER']

def lambda_handler(event, context):
    # ロガーの設定
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    # DocumentDBのクライアント
    docdb_client = boto3.client('docdb')
    # AWS SNSのクライアント
    sns_client = boto3.client('sns')

    # ステータスの結果保存のための配列
    db_statuses = []

    try:
        logger.info('Start >>> get DocumentDB status.')
        # dbクラスターの情報取得
        cluster_info = docdb_client.describe_db_clusters(DBClusterIdentifier=DB_CLUSTER_IDENTIFIER)
        cluster_status = cluster_info['DBClusters'][0]['Status']
        db_statuses.append(
            {
                DB_CLUSTER_IDENTIFIER : cluster_status
            }
        )

        instances = cluster_info['DBClusters'][0]['DBClusterMembers']

        for instance in instances:
            instance_id = instance['DBInstanceIdentifier']
            instance_info = docdb_client.describe_db_instances(DBInstanceIdentifier=instance_id)
            status = instance_info['DBInstances'][0]['DBInstanceStatus']

            db_statuses.append(
                {
                    instance_id : status
                }
            )

        # 1件でも'available'以外の場合、通知処理実行
        # ※'available'以外にも通知しないステータスを追加する場合は、in以降の配列にステータスの文字列を追加する
        is_notify = not all(list(i.values())[0] in ['available'] for i in db_statuses)

        if is_notify:
            # 通知処理の実行

            # メールに記載する内容を作成
            subject = '【AWS DocumentDB】An instance with an abnormal status has been detected.'
            message = ''
            for status_info in db_statuses:
                key = list(status_info.keys())[0]
                value = status_info[key]
                message += f'{key}: {value}\n'

            # メール送信
            sns_client.publish(
                TopicArn = TOPIC_ARN,
                Subject = subject,
                Message = message,
            )

            logger.info('DocumentDB status list↓')
            logger.info(message)
            logger.info('Completed successfully.')

    except Exception as e:
        logger.error(e)

    finally:
        logger.info('End >>> get DocumentDB status.')



```
※作成したレイヤーの追加は忘れずに！
# あとはCloudWatch Eventsにルール追加
例えばcron式に「*/10 * * * ? *」を設定することで10分毎にDocumentDBのステータスをチェックします。
もし異常があればメールが飛んでくるはずです。

ターゲットとして、監視対象のクラスター識別子を渡してあげるようにすれば、Lambda関数内の引数eventで参照することができるのでLambda関数はそのままにルールを追加して環境毎に監視することも可能です。

# 「available」以外の監視でいいのか？
「available」以外という条件では、定期メンテナンスの場合もメールが飛んでくることがあると思うので
定期メンテナンスの場合の「maintenance」も足して

```Python
is_notify = not all(list(i.values())[0] in ['available', 'maintenance'] for i in db_statuses)
```

とするのもいいかもしれません。



# 参考
[Amazon DocumentDB インスタンスのステータスのモニタリング](https://docs.aws.amazon.com/ja_jp/documentdb/latest/developerguide/monitoring_docdb-instance_status.html)
[Boto3/Docs/Available services/DocDB](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/docdb.html)
[Boto3/Docs/Available services/SNS](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html)