[AWS] Serverless FrameworkでS3にファイルが追加されたらLambdaを起動する

AWS LambdaはRESTful APIの開発時だけではなく、特定のイベントをトリガーとして処理を行うことができます。今回はServerlessFrameworkを利用し、S3に新しくオブジェクトが追加された際に関数を起動するところまでをまとめます。

準備

Node.jsServerlessFrameworkがインストールされた状態で設定を行っていきます。

初期ファイルの作成

slsコマンドを実行しServerlessFrameworkのテンプレを取得、npm initでpackage.jsonを作成します。

$ sls
? What do you want to make? AWS - Node.js - Starter
? What do you want to call this project? test1

$ cd test1
$ npm init

必要なモジュールのインストール

S3上のオブジェクト(ファイル)を操作する場合はAWS SDKを入れます。

$ npm install aws-sdk

サンプル

オブジェクトが追加されるとconsole.logにバケットとオブジェクト名を出力するサンプルを作成してみます。

serverless.yml

以下のYAMLは次のような意味になります。指定されたバケットが存在しない場合は自動的に作成される点に注意が必要です。

  1. 東京リージョン」の
  2. バケットs3bucket.example.com」内に、
  3. images/*.jpg」というネーミングルールのオブジェクトが追加された場合
  4. 「handler.js内のcreate関数」を実行する
service: s3eventtest
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs14.x
  region: ap-northeast-1  # 東京リージョン
  environment:
    S3_BUCKET: s3bucket.example.com  # バケットを指定
  iam:
    role:
      statements:
        # S3の指定バケット上のオブジェクトの入出力を許可
        - Effect: Allow
          Action:
            - s3:GetObject
            - s3:PutObject
          Resource:
            - arn:aws:s3:::${self:provider.environment.S3_BUCKET}/*
        # CloudWatchにログ出力を許可
        - Effect: Allow
          Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
          Resource:
            - "*"

functions:
  eventCreat:
    handler: handler.create
    events:
      - s3:
          bucket: ${self:provider.environment.S3_BUCKET}
          event: "s3:ObjectCreated:*"
          rules:
            - prefix: images/
            - suffix: .jpg
          # すでに存在するバケットを対象とする場合は以下のコメントを外す
          # existing: true 

イベントの種類

ここではeventにオブジェクトが作成された場合を示すs3:ObjectCreated:*を指定していますが、他にも様々なイベントが用意されています。

例えば以下のイベントが利用できます。

イベント 内容
s3:ObjectCreated:* オブジェクト作成
s3:ObjectRemoved:* オブジェクト削除
s3:ObjectRestore:* Glacierから復元(開始、完了)
s3:LifecycleExpiration:* ライフサイクル設定でオブジェクトを削除
s3:ObjectTagging:* オブジェクトにタグ追加・削除
s3:ObjectACL:PUT オブジェクトのACL設定が変更

特定のオブジェクトだけ対象にする

rulesで特定のファイル名のときだけLambdaを起動できるフィルター機能があります。

prefix
ファイル名の一部
suffix
拡張子

もちろんLambda内で判定しても良いのですが、起動回数に応じて料金かかっちゃいますからね。

注意点

個人的にハマったのですが、resourcesでS3のバケットを作成しなくても上記だけで自動的にバケットが作成されます。逆にresourcesを書くとdeploy中に「すでにバケットが存在している」という旨のエラーが表示され途中で停止します。

Lambdaのコード例

関数の第一引数(event)に新しく作成されたオブジェクトの情報が渡されますので、これをもとに自分のやりたい処理を定義します。

'use strict';

/**
 * S3に作成されたファイル名を記録する
 *
 */

//----------------------------------------------------
// モジュール
//----------------------------------------------------
const AWS = require('aws-sdk');
const s3 = new AWS.S3({ apiVersion: '2006-03-01'});

// リージョン情報を更新
AWS.config.update({region: 'ap-northeast-1'});

/**
 * S3 ObjectCreated event
 *
 * @param {object} event
 */
module.exports.create = async (event) => {
  const bucket = event.Records[0].s3.bucket.name;
  const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));

  // CloudWatch上のログを確認する
  cosole.log(`${bucket}/${key}が作成されました`);

  // ここにやりたいことを書いていく
};

デプロイ

特別な設定や手順はありません。楽ちんですねー。

$ sls deploy

最後に実際にS3にファイルをアップロードして挙動の確認を行えば完了です。

参考ページ