Lambda+Aurora serverless+WebhookでTeamsに毎日analyticsを通知するようにしてみた

社内で新しいサービスがローンチしたのですが、どれぐらい伸びてるか毎日統計情報を取りたいとなり、LambdaでAurora Serverlessに接続しSQLを発行して分析し、その結果をWebhookでTeamsに通知するようなものを作ったので作り方を記事にまとめてみます。

以下の手順で作成していきます。

  1. Temasチャンネルにwebhook用コネクタを追加
  2. Aurora serverlessのData API設定
  3. Lambda関数作成
  4. CloudWatch Eventsをトリガーに追加
  5. チャンネルに通知

Temasチャンネルにwebhook用コネクタを追加

通知をしたいTeamsチャンネルの右上の3点ボタンをクリックし、メニューを表示して「コネクタ」を選択します。

f:id:okiyasi:20200802231435p:plain:w200
コネクタの中から「Incoming Webhook」を探し「構成」をクリックします。すると以下のような画面が出てくるので適当な名前を入力して「作成」でWebhookを作成します。
f:id:okiyasi:20200802232108p:plain:w600

作成するとこのようにWebhookのURLが表示されるのでこのURLをコピーしておきます。(後に使うのでどこかに保存しておいてください。)

f:id:okiyasi:20200802232602p:plain:w600

Aurora serverlessのData API設定

Aurora Serverlessとは(簡単に)

Aurora ServerlessはAuroraをオンデマンドで自動スケーリングしてくれるようなもので、一定時間リクエストがないときはDBインスタンスが停止状態になり、逆に負荷が高まったりした時は自動でスケーリングしてくれるので普通のAuroraに比べてとてもコストパフォーマンスが高いです。まだユーザーの少ない新規サービスや開発環境などの場合はServerlessを使うとAuroraに比べてかなりコストを抑えられます。Auroraへの移行も比較的簡単です。

Data APIとは(簡単に)

Data APIはAurora Serverless の エンドポイントとして機能するもので、Lambdaなどを使用する場合にはAurora Serverlessに直接接続せずに、Data APIを介して接続することでRDBの最大接続数の問題などを回避することができたり、VPC内にLambdaを設置しなくてもAurora Serverlessに接続できたりするのでとても便利な機能になります。

設定するには

AWSのコンソールで接続したいAurora Serverlessのクラスターを選択して「変更」を行います。変更の中の「ネットワーク & セキュリティ」という箇所があるのでその中にわかりにくく、Data APIチェックボックスがあるのでそれをオンにして、変更を適用して終わりです。設定するとシークレットマネージャーに自動で接続情報が登録されます。
MySQL Version 5.6以上・PostgreSQL Version 10.7以上と互換性のあるAuroraで使用可能です ※2020年8月時点)

f:id:okiyasi:20200803001012p:plain

Lambda関数作成

次にLambda関数を作成します。 まず通常のコンソール画面から、Lambdaデフォルトのポリシーがアタッチされたロールを付与されている新しい関数を作成します。その次にIAMロールの編集を行います。

ポリシーの作成

Data APIに接続するためのポリシーの作成を行います。
ポリシーについてはこののクラスメソッドさんの記事を参考に作成しました。 (※ちなみに2020年7月時点ではもうLayerはなくてもData APIは叩けました)
https://dev.classmethod.jp/articles/aurora-sl-dataapi-with-lambda-layer/

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SecretsManagerDbCredentialsAccess",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:PutResourcePolicy",
                "secretsmanager:PutSecretValue",
                "secretsmanager:DeleteSecret",
                "secretsmanager:DescribeSecret",
                "secretsmanager:TagResource"
            ],
            "Resource": [
                "arn:aws:secretsmanager:*:*:secret:rds-db-credentials/*",
                "--ここに自動作成されたのシークレットマネージャーのarnを記入(シークレットマネージャーのコンソールに載っている)--"
            ]
        },
        {
            "Sid": "RDSDataServiceAccess",
            "Effect": "Allow",
            "Action": [
                "dbqms:CreateFavoriteQuery",
                "dbqms:DescribeFavoriteQueries",
                "dbqms:UpdateFavoriteQuery",
                "dbqms:DeleteFavoriteQueries",
                "dbqms:GetQueryString",
                "dbqms:CreateQueryHistory",
                "dbqms:DescribeQueryHistory",
                "dbqms:UpdateQueryHistory",
                "dbqms:DeleteQueryHistory",
                "dbqms:DescribeQueryHistory",
                "rds-data:ExecuteSql",
                "rds-data:ExecuteStatement",
                "rds-data:BatchExecuteStatement",
                "rds-data:BeginTransaction",
                "rds-data:CommitTransaction",
                "rds-data:RollbackTransaction",
                "secretsmanager:CreateSecret",
                "secretsmanager:ListSecrets",
                "secretsmanager:GetRandomPassword",
                "tag:GetResources"
            ],
            "Resource": "*"
        }
    ]
}

このポリシーを作成したLambdaのロールにアタッチします。

Lambdaのコードを作成

今回はzipで外部ライブラリをLambdaに持ってくるとかの説明は省きたいので、pythonの標準ライブラリだけでできるような、ユーザーの数だけを取得して通知する簡単なコードを紹介します。

import json
import boto3
import urllib
from datetime import date

def lambda_handler(event, context):

    rdsData = boto3.client('rds-data')

    # Aurora ServerlessのクラスターのARN(RDSのコンソールから取得できる)
    cluster_arn = 'arn:aws:rds〜〜〜〜〜'
    # シークレットマネージャーのARN
    secret_arn = 'arn:aws:secretsmanager〜〜〜〜〜'

    response = rdsData.execute_statement(
                resourceArn = cluster_arn, 
                secretArn = secret_arn, 
                database = '自分の作ったデータベースの名前', 
                # ユーザーの数を取得するだけのSQL
                sql = 'select count(*) from user情報のテーブル名') 

    user_number = response['records'][0][0]['longValue']
    today = date.today().strftime('%Y年%m月%d日')
    message = f'{today}の累計ユーザー数:{user_number}人'
    
    print(message)
    
    # 1で作成したwebhookのURL
    webhook_url = 'https://outlook.office.com〜〜〜〜〜〜'

    data = {
        "text": message,
    }
    headers = {'Content-Type': 'application/json'}
    request = urllib.request.Request(
        webhook_url,
        json.dumps(data).encode("utf-8"), 
        headers
    )
    with urllib.request.urlopen(request) as response:
        response_body = response.read().decode("utf-8")

Lambdaで外部ライブラリを使ってもっと複雑なことをしたい場合はこちらの記事をお読みください。 【AWS・Lambda】Python外部ライブラリ読み込み方法 - Qiita

CloudWatch Eventsをトリガーに追加

Lambdaの画面にある「デザイナー」の中の「トリガーを追加」を選択し、トリガーを選択で「Event Bridge(CloudWatch Events)」を選択します。

f:id:okiyasi:20200803011039p:plain:w300
ルールで「新規のルールの作成」を選び名前を記入し、ルールタイプはスケジュールタイプを選択します。
f:id:okiyasi:20200803012009p:plain
スケジュール式にcron(0 0 ? * MON-FRI *)と記入することによって平日のUTC時間の午前0時0分にLambdaを実行することが可能です。 cron式については詳しくはこちらをご覧ください。 docs.aws.amazon.com 最後に「追加」を押すとトリガーの追加が完了します。

テスト

以上の作業でTeamsのチャンネルに毎日通知を行うことが可能になりました! ちゃんとできてるかUTCの午前0時0分まで待てないので一旦Lambdaのテストを使って、通知を実施してみます。今回はパラメータは使わないのでデフォルトのhello worldのテストをそのまま使ってテストをすると以下のように指定したチャンネルに通知がくると思います。

f:id:okiyasi:20200803013403p:plain