CDKを使って既存S3バケットのPUTイベントをトリガーにLambda関数を実行しようとしたらハマった話

既存バケットのイベントドリブンでLambda関数を実行したい場合は s3.Bucket.AddNotification を使おう。
2022.06.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、AWS事業本部@福岡オフィスのべこみん(@beco_minn)です。

最近はCDK楽しいマンになってます。

さて、皆さんは「CDKを使って、既存S3バケットのイベントドリブンでLambda関数を実行したいな〜」と思ったことはないですか?

今回は僕がそう思った時にハマったことを共有します。

ちなみに本記事で使用している言語はTypeScriptです。

いきなりまとめ

  • CDKを使って既存S3バケットのイベントをトリガーにLambda関数をキックしたい時は、S3側のイベント通知機能を使おう
    • s3.Bucket.AddNotification を使う
    • lambda.Function.addEventSource はS3バケットを新規作成する場合にしか対応してない

はじめに

今回やりたいことはこんなイメージ

S3のイベントドリブンでLambda関数を実行するパターンは以下の2パターンが考えられるかと思います。

  • S3のイベント通知を使う
  • Lambdaにイベントソースを追加する

僕は後者の『Lambdaにイベントソースを追加する』でいけるだろ〜と思ってやってみました。

Lambdaにイベントソースを追加するにはこの S3EventSource クラスを使用します。

lambda.Function.addEventSource でハマった

早速サンプルコードを真似して書いてみるとこんなエラーが。

型 'IBucket' の引数を型 'Bucket' のパラメーターに割り当てることはできません。
  型 'IBucket' には 型 'Bucket' からの次のプロパティがありません: autoCreatePolicy, lifecycleRules, metrics, cors、32 など。

どうやらS3EventSourceで指定するバケットの型は Bucket じゃないとダメらしい。

既存バケットを取得する方法としては、s3.Bucketの fromBucketArn()fromBucketAttributes()fromBucketName() があるのですが、これらの戻り値の型はどれも IBucket なんです。。。

Google先生と睨めっこしてたらこんなissueがありました。

S3EventSource should take IBucket instead of Bucket #4323 - https://github.com/aws/aws-cdk/issues/4323

どうやら既存バケットを使う場合は s3.Bucket.AddNotification を使うのが正解っぽい。

【解決方法】 s3.Bucket.AddNotification

ということで、既存バケットのイベントドリブンでLambda関数を実行したい場合は s3.Bucket.AddNotification を使いましょう。

sumple.ts

    // Lambda Function
    const target_function = new Function(this, "sumpleFunc", {
      code: new AssetCode("resources"),
      handler: "index.handler",
      runtime: Runtime.NODEJS_16_X,
      functionName: "sumple_function",
      timeout: Duration.seconds(60)
    });
  
    // 既存のS3バケットを取得
    const existing_bucket = Bucket.fromBucketArn(
      this,
      'sumple-bucket-20220630-becominn',
      'arn:aws:s3:::sumple-bucket-20220630-becominn'
    )

    // S3Notificationを設定
    existing_bucket.addEventNotification(
      EventType.OBJECT_CREATED_PUT,
      new LambdaDestination(target_function)
    )

サンプルのソースコードも置いておきます。

最後に

今回の解決方法はどちらかといえば妥協案のようなものですが、やりたいことは達成出来ました。

私は最近CDKを触り始めた初心者なので、今回のように型でハマることが多いですね。(ただ型があるのは嬉しい)

本記事がどなたかのお役に立てれば幸いです。

以上、べこみんでした。