😺

S3のファイル操作イベントをLambdaとWebhook使ってDiscordに通知するやつ

2022/11/16に公開

TL;DR

目的

  • S3 でファイル操作(追加・削除等)がされる度に通知してほしかった
  • Lambda と Webhook を使った通知を作った
  • 通知先は Discord

つくったもの

構成

ソースコード

https://github.com/satory074/s3_to_discord


想定

  • AWS アカウントを作成済み
  • AWS CLI をローカル環境にインストール・configure の設定済み
    (インストール方法について前回の記事で少しだけ触れています)

https://zenn.dev/satory074/articles/satory074_2021091401#1.-aws-cliのインストール(不要な手順かもしれない)


1. S3 でバケットを作成

通常通りにバケットを作成します。

既存のバケットを用いる場合は、すべて読み飛ばして「手順 2: 通知用の Webhook を作成する」に進みます。

  1. S3 のコンソール(https://s3.console.aws.amazon.com/)にアクセスします。

  2. 左側のダッシュボードから[Buckets]を選択し、[Create bucket]をクリックします。

  3. バケットの情報を入力して[Create Bucket]をクリックします。

    • Bucket name: バケット名
      • s3-notification-test-satory074(任意の一意な名前を入力)

2. 通知用の Webhook を作成する

通知先となる Discord のチャンネルの Webhook を用意します。

以前の記事の「手順 4: Slack の Webhook URL を取得する」で解説しています。
(Slack 用の手順を Discord 用の手順へ書き換えを行った記事です)

作成した Webhook URL を控えたらこの手順は終了です。

3. 通知用の Lambda を作成する

通知処理を行う Lambda を作成します。

こちらも以前の記事の「手順 5: Lambda を作る」とほぼ同じですが、本記事用に書き換えながら簡単に同じ手順を記載します。

3.1 プログラムを用意する

冒頭でも載せましたが、ソースコードは以下になります。

3.1.0 lambda_function.json

ソースコードについて少し説明させてください。読み飛ばしても大丈夫です。

lambda_function.py
colors = {
   "ObjectCreated:CompleteMultipartUpload": 0x008000,
   "ObjectCreated:Put": 0x008000,
   "ObjectCreated:Post": 0x008000,
   "ObjectCreated:Copy": 0x008B8B,
   "ObjectRemoved:Delete": 0xFF6347,
   "ObjectRemoved:DeleteMarkerCreated": 0xFF6347,
}

event["Records"]["eventName"](追加したよとか削除したよとかいうファイル操作の種類)に対応する、Discord の通知メッセージの左側の線の色を決めています。

lambda_function.py
for rec in event["Records"]:
   color = colors[rec["eventName"]] if rec["eventName"] in colors else 0x000000
   size = (
      convert_size(rec["s3"]["object"]["size"])
      if "size" in rec["s3"]["object"]
      else ""
   )

   # Webhook data
   data = {
      "username": rec["eventSource"], # イベントソース(aws:s3)
      "avatar_url": "https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/main/dist/Storage/SimpleStorageService.png", # S3のアイコン(っぽいやつ)
      "embeds": [
            {
               "title": rec["s3"]["object"]["key"], # ファイル名
               "description": size, # ファイルサイズ
               "color": color, # 通知メッセージの色
               "timestamp": rec["eventTime"], # タイムスタンプ
               "footer": {"text": rec["eventName"]}, # イベント名
               "fields": [
                  { # リージョン
                        "name": "awsRegion",
                        "value": rec["awsRegion"],
                        "inline": True,
                  },
                  { # バケット名
                        "name": "bucket",
                        "value": rec["s3"]["bucket"]["name"],
                        "inline": True,
                  },
               ],
            }
      ],
   }

   # POST
   requests.post(
      WEBHOOK_URL, json.dumps(data), headers={"Content-Type": "application/json"},
   )

Recordsごとに「データ取得」「通知の Post」を行っています。

sizeについては後述します。

dataは Webhook で POST される通知メッセージのデータです。

通知メッセージの各値の意味や編集については、以下の記事を参考にしましたので、興味がある方は色々カスタマイズしてみてください。

https://qiita.com/Eai/items/1165d08dce9f183eac74

eventが、S3 でファイル操作された情報ですが、ここでは(すべて正確に網羅する自信がないので)説明を割愛をしますので、興味がある方は別のサイトを調べたり、実際にeventの中身を出力しまくってみてください。

eventの一例だけ添えておきます。

eventの一例
{
   "Records": [{
         "eventVersion": "2.1",
         "eventSource": "aws:s3",
         "awsRegion": "ap-northeast-1",
         "eventTime": "2022-11-14T12:21:32.108Z",
         "eventName": "ObjectCreated:Put",
         "userIdentity": {
           "principalId": "XXXXXXXXXXXX"
         },
         "requestParameters": {
           "sourceIPAddress": "xxx.xxx.xxx.xxx"
         },
         "responseElements": {
           "x-amz-request-id": "XXXXXXXXXXXXXXXX",
           "x-amz-id-2": "xxxx"
         },
         "s3": {
            "s3SchemaVersion": "1.0",
            "configurationId": "s3-to-discord",
            "bucket": {
               "name": "s3-notification-test-satory074",
               "ownerIdentity": {
                  "principalId": "XXXXXXXXXXXXXXXX"
               },
               "arn": "arn:aws:s3:::s3-notification-test-satory074"
            },
            "object": {
               "key": "Figure_1.png",
               "size": 1024,
               "eTag": "xxxx",
               "versionId": "xxxx",
               "sequencer": "XXXXXXXXXXXXXXXXXXXXX"
            }
         }
      }
   ]
}

lambda_function.py
# 参考: https://pystyle.info/python-data-size-conversion/

def convert_size(size):
    """ Convert size to readable format """
    units = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB")
    i = math.floor(math.log(size, 1024)) if size > 0 else 0
    size = round(size / 1024 ** i, 2)

    return f"{size} {units[i]}"

size(ファイルサイズ)がバイトなので、適切な単位に変換を行っています。

3.1.1 lambda.json

データ構造

JSON Key 内容 書き換え 備考
name Lambda の関数名 任意の 名前でも OK です
description Lambda の関数の説明
runtime ランタイム
region Lambda を作成するリージョン
handler ハンドラ
role 関数のロール 必要 ロールの ARN
timeout タイムアウト
memory メモリ
variables 関数を実行する為に必要な引数
accountId AWS アカウントの ID 必要
TZ タイムゾーン
WebhookURL Webhook の URL 必要 手順 2 で控えた Webhook の URL

3.2 作成したコードを Lambda にアップロード

zsh
satory074@MacBook-Pro ~/src/s3_to_discord $ lambda-uploader
λ Building Package
λ Uploading Package
λ Fin

4. S3 と Lambda の連携

手順 1 で作成した S3 バケットでファイル操作のイベントが行われるたびに、手順 3 で作成した Lambda で Discord への通知を行う設定をします。

  1. S3 のコンソール(https://s3.console.aws.amazon.com/)にアクセスします。

  2. バケットの一覧から手順 1 で作成したバケットをクリックします。

  3. [Properties タブ]をクリックし、Event notifications 項目の[Create event notification]をクリックします。

  4. 各項目を入力して、[Save changes]をクリックします。

    • Event name: イベント名
      • 任意の名前(例: notification)
    • Event types: (通知を行う)対象のイベント
      • 左側の「All ...」すべてにチェックをつけていいと思います
    • Lambda function: イベントを連携する Lambda 関数
      • 手順 3 で作成した Lambda を選択

  5. 手順 3 で作成した Lambda にアクセスすると、S3 と連携してるっぽい図が見られると思います。

  6. S3 でファイルをアップロードしたり消したりしてみて、Discord に通知されていれば成功です!おつかれさまでした!!


おわりに

S3 より EC2 の通知が欲しくなった

Discussion