読者です 読者をやめる 読者になる 読者になる

AWS LambdaとSESを使ったS3ファイルダウンロード通知の仕組み

背景

とある日

お客さん「業務システムからS3にファイルPUTされてるんやけど、権限ないからダウンロードしてファイルちょうだい」
ぼく「はい、このパスに配置しておきました。」

翌日

お客さん「また業務システムからS3にファイルPUTされてるから、ダウンロードしてファイルちょうだい」
ぼく「はい、このパスに配置しておきました。」

翌々日

お客さん「また業務システムからS3にファイルPUTされてるから、ダウンロードしてファイルちょうだい」
ぼく「はい、このパスに配置しておきました。」

~~以下無限ループ~~

めんどくさいんじゃ!!!!!!!!!!!!

ということで作りました。 いやほんとはたいした手間じゃないんですけどね。

構成

導入前

f:id:iryond:20160609193950j:plain

今2016年だよな、、、??と思うほどトラディショナルな仕組みでやってました。

導入前

f:id:iryond:20160609193932j:plain

いいですね、2016年はこうあるべきだと思います。
ただちょっと難点だったのは、SESが東京リージョンにリリースされていないこと。

たいした重要度でもないからまあいいや的な感じでエイッって作りましたが、
もっといいやり方があるんじゃないかと思ったり思わなかったり。

やったこと

SESの準備

email verification
  1. SES開く
  2. Identity ManagementのEmailAddressesクリック
  3. Verify a new email addressボタンをクリック
  4. 検証対象のメアド入力
  5. 指定したメアドにメールが届くので、メール内のリンクをクリック
  6. ブラウザ上のステータスがverifiedになっていることを確認

大前提として、fromに指定するemail addressを所有していることを確認するために、
上記の作業を実施する必要があります。

また、SESは、特に設定を施していない場合、sandboxという検証環境にあります。
その場合、toに指定するアドレスについても、verificationする必要があります。
本番サービスで稼働する場合は、sandboxの外に出す申請をすると思うのですが、
今回は内部的な処理なので、from,toどちらのアドレスともverificationを行っています。

lambdaの準備

Lambdaでやりたい処理は下記のとおりです。

  • S3からのイベントを受け付けてBucketとKeyに分割
  • boto3を使ってPresigned-URLを生成
  • 生成したPresigned-URLを本文にメール送信

実装するとこうなります。

github.com

#!/usr/local/bin/python

from __future__ import print_function
import boto3
import os
import sys

ADMIN_EMAIL = 'xxxxxx@gmail.com'
s3_client = boto3.client('s3')
ses_client = boto3.client('ses', region_name='us-west-2')

def send_email(to, reply, subject, body):
    response = ses_client.send_email(
        Source=ADMIN_EMAIL,
        Destination={
            'ToAddresses': [
                to,
            ]
        },
        Message={
            'Subject': {
                'Data': subject,
            },
            'Body': {
                'Text': {
                    'Data': 'Please download the file from this url.\n'+body,
                },
            }
        },
        ReplyToAddresses=[
            reply,
        ],
        ReturnPath=ADMIN_EMAIL
    )

def send_mail_from_admin(to, message):
    send_email(to, ADMIN_EMAIL, "S3 notification", message)

def lambda_handler(event, context) :
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']
        print(bucket)
        print(key)
        generated_url=(s3_client.generate_presigned_url(
            ClientMethod = 'get_object',
            Params = {'Bucket' : bucket, 'Key' : key},
            ExpiresIn = 3600,
            HttpMethod = 'GET'))
        send_mail_from_admin('xxxxxx@gmail.com', generated_url)

boto3があればなんでも簡単に書けちゃいますね。
コメントとかないのはご容赦ください。
なお、lambda実行時にlambda_functionが呼び出されるよう、設定しています。

lambdaの権限設定として、ロールに下記のポリシーをアタッチ

  • S3触る
  • SES触る

S3のイベント通知設定

配置対象のbucketのプロパティで、
特定のフォルダに特定の拡張子のファイルが配置された際に、
lambdaを呼び出すよう設定しています。

f:id:iryond:20160609193931p:plain

まとめ

lambdaを使って簡単な仕組みを構築してみました。
こういったお遊びシステムだとあまり気になりませんが、
本番導入時には、下記のような観点が気になるかなと思いました。

  • 特にデプロイ周りの運用
  • 稼働対象ノードによるパフォーマンスのばらつき
  • (Javaの場合)起動オーバヘッド
  • APIGatewayはVPC内に立たないのか?
  • その他いろいろ

上司からは無停止デプロイや、エラー時の問題切り分けが難しそうだよねというご指摘をいただきました。
たしかに。

エンタープライズレベルのシステムで適用できるように、いろいろ考えたいと思います。

それでは。