S3での操作内容をSlackへ良い感じに通知する

こんにちは、クラウドソリューション事業部の本田です。AWSのS3って便利ですよね、エンジニア以外の方でもGUIでファイルのアップロードや削除したりできるので、コンテンツを作成される方がS3で直接操作されているなんてケースもよくあるのかなと思います。そうなるといつ作業したのかログや通知が欲しくなったりしますよね。ということで、今回はS3での操作内容をSlackへ通知するシステムを構築したいと思います。

S3イベントをSNSからAWS Chatbotを通してslackへ通知という構成でも良いのですが、イベントの内容をそのまま通知すると、中身が見にくいのでLambdaで整形して良い感じに見やすく送るような構成にしています。SQSを使ったのは、データをロストしないように。S3イベントだけでもロストはしなさそうですが、念には念をということで。

1.Slack側の用意

今回SlackのWebhookを使用したワークフローを作成して、利用します。有料プランでないと利用できないようなのでご注意ください。参考:Webhook を使用した高度なワークフローを作成する

Slackの詳細な設定方法は割愛しますが、通知を飛ばしたいチャンネルのインテグレーションからワークフローを作成します。カスタム変数を登録し、以下のようなJSONを発行されるURLへHTTP POSTすることで利用することができます。

{"Modified_time": "編集時間",   
 "Worker": "作業者名",
 "S3_Bucket_Name": "S3バケット名",
 "Work_Description": "作業内容",
 "Objects": "対象オブジェクト"  }

完成のイメージとしては、S3で操作をした際に以下のような通知がSlackに来ます。

2.SQSの用意

作成する順番に特に指定はありませんが、SQSから作成します。キューのタイプを標準にするのと、アクセスポリシーを指定し、S3イベントの通知先に設定できるようにします。以下のようなアクセスポリシーを設定しています。

{
  "Version": "2012-10-17",
  "Id": "Policy",
  "Statement": [
    {
      "Sid": "Stmt",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "sqs:SendMessage",
      "Resource": "作成するSQSのARN"
    }
  ]
}

3.Lambdaの用意

次にSQSから受け取ったデータを加工し、Slackへ通知するLambda関数を用意します。Node.jsで作成していますが、お好きな言語で用意してもらえればと思います。LambdaからSQSへアクセスできるように、以下のようなポリシーをロールにアタッチする必要があります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "sqs:DeleteMessage",
                "sqs:ReceiveMessage",
                "sqs:GetQueueAttributes"
            ],
            "Resource": "作成したSQSのARN"
        }
    ]
}

Lambda関数に使用するソースコードは以下です。環境変数で、put_post_urlを設定し、1で取得したwebhookのURLを指定しています。

const https = require('https');

exports.handler = function (event, context) {
    // TODO implement

    const target = event;
    const body = JSON.parse(target.Records[0]['body']);
    console.log(body);
    
    const s3bucket = body.Records[0].s3.bucket.name;
    const path = body.Records[0].s3.object.key;
    const user_name = body.Records[0].userIdentity.principalId.split(':');
    const eventName = body.Records[0].eventName.split(':');
    var modified_time = new Date(body.Records[0].eventTime);
    modified_time.setTime(modified_time.getTime() + 9 * 60 * 60 * 1000);
    var modified_time = modified_time.getFullYear()
        + "/" + (modified_time.getMonth() + 1)
        + "/" + modified_time.getDate().toString().padStart(2, '0')
        + " " + modified_time.getHours().toString().padStart(2, '0')
        + ":" + modified_time.getMinutes().toString().padStart(2, '0')
        + ":" + modified_time.getSeconds().toString().padStart(2, '0');


    const response = {
        Modified_time: modified_time,
        Worker: user_name[user_name.length - 1],
        S3_Bucket_Name: s3bucket,
        Work_Description: eventName[eventName.length - 1],
        Objects: path
    };

    console.log(response);
    //slackへの送信
    const data = JSON.stringify(response);
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
    };

    //URLは環境変数に設定
    const post_put_url = process.env['put_post_url'];
    const request = https.request(post_put_url, options);
    request.write(data);
    request.end();

    return {};
};

4.S3の設定

通知の対象とするS3バケットのS3イベント通知を有効化します。通知させたいイベントタイプにチェックを入れ、送信先に作成したSQSを選択します。アクセスポリシーがうまくできていないと、APIエラーが出て保存ができませんので、エラーが出た場合は設定を確認してください。

5.動作確認

最後に動作を確認してみましょう。対象のバケットにファイルアップロードと削除を行ってみます。

5-1.アップロード

test.jpegというファイルを上げてみます。

そうすると、以下のような通知がSlackに来ます。

対象にバケットに、コンテンツがPut(アップロード)されたことがわかります。

5-2.削除

では次にS3にアップロードされているオブジェクトを削除してみましょう。

AWSあるあるですが、S3でオブジェクトを削除する時は、「完全に削除」と入力します。サービスによって異なります。削除をすると、Slackに以下のような通知が来ます。

オブジェクトがDelete(削除)されたことがわかります。

これにてS3で何かしらの操作をした際に、Slackへ良い感じに通知できるようになりました。

最後に

エンジニアだけの組織や、確認するのが自分だけであれば生データで送られてきても問題なかったりするのですが、エンジニアでない方からすると呪文に見えて理解できなかったりしますので、見やすいメッセージにすることは多くの人や組織が関わる場合には、必要なことかと思います。何かのご参考になればと思います。