AWS CDK で S3 の PUT をトリガーに Step Functions 起動する構成を作成してみた
CX事業本部の佐藤です。
S3 の PUT をトリガーに Step Functions を呼び出す構成を作成していたところ、ちょっとハマってしまったので知見として残します。今回はAWS CDKを使ってリソースを構成していきます。
環境
- AWS CDK 1.42.1
- TypeScript 3.9.2
- Node.js 12.16.0
AWS アーキテクチャ
以下のようなアーキテクチャを作成します。S3 に何らかのファイルが PUT されたのをトリガーに EventBridge にイベントを流し、ターゲットとして Step Functions のステートマシンを起動します。
やってみた
まずは、AWS CDK のプロジェクトを作成します。プロジェクト名は適宜変えます。
mkdir hoge cdk init app --language=typescript cd hoge
必要なライブラリをインストールします。
npm install --dev @aws-cdk/aws-stepfunctions @aws-cdk/aws-stepfunctions-tasks @aws-cdk/aws-lambda @aws-cdk/aws-s3 @aws-cdk/aws-events @aws-cdk/aws-events-targets
AWS CDKで実装
lib/*-stack.ts
を以下のように実装します。Step Functions のステートマシンは受けたイベントをパススルーするだけの簡易的なものです。EventBridge ルールは S3 に PUT されたイベントをステートマシンに流す設定を行います。
import * as cdk from '@aws-cdk/core'; import * as s3 from '@aws-cdk/aws-s3'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as events from '@aws-cdk/aws-events'; import * as targets from '@aws-cdk/aws-events-targets'; export class HogeStepfunctionsStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // The code that defines your stack goes here // ファイルアップロード用バケット const fileUploadBucket = new s3.Bucket(this, 'fileUploadBucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, }); // StepFunctionsの定義 const definition = new sfn.Pass(this, 'pass'); const stateMachine = new sfn.StateMachine(this, 'StateMachine', { definition, }); // EventBridge ルールのターゲットにStepFunctionsを設定する const sfnTarget = new targets.SfnStateMachine(stateMachine); // ファイルが PUT されたら StepFuntions を起動する EventBridge Rule を作成する const rule = new events.Rule(this, 'put event rule', { description: '特定のバケットにファイルがPUTされたら動作するルールです', eventPattern: { source: ['aws.s3'], detailType: ['AWS API Call via CloudTrail'], detail: { eventSource: ['s3.amazonaws.com'], eventName: [ 'PutObject', 'CopyObject', 'CompleteMultipartUpload', ], requestParameters: { bucketName: [fileUploadBucket.bucketName], }, }, }, }); rule.addTarget(sfnTarget); } }
デプロイ
以下でデプロイを行います。
cdk deploy
以下のように単純にパススルーするだけのステートマシンがデプロイされています。
また、EventBridge のルールも以下のようにデプロイされています。
イベントパターンとは、イベントをフィルタする役割で、以下のように定義すると バケット名が hoge
で putObject
, CopyObject
, CompleteMultipartUpload
が行われたら、そのイベントをターゲットに流すという処理になります。今回はターゲットが Step Functions なので、該当のステートマシンが起動します。
{ "detail-type": [ "AWS API Call via CloudTrail" ], "source": [ "aws.s3" ], "detail": { "eventSource": [ "s3.amazonaws.com" ], "requestParameters": { "bucketName": [ "hoge" ] }, "eventName": [ "PutObject", "CopyObject", "CompleteMultipartUpload" ] } }
動作確認
デプロイできたので、動作確認をしてみます。デプロイされたS3バケットに適当なファイルをPUTしてStep Functionsのステートマシンが起動するかを確認します。test.txt
のような適当なファイルを作成し、AWSコンソールからS3バケットに移動し ドラッグ&ドロップでアップロードしてみます。
アップロードができたら、StepFunctionsのステートマシンが起動しているかを確認します。デプロイされたステートマシンに移動します。
なぜか実行されていなさそうです。
原因を探る
Step Functionsドキュメントを確認したところ、以下の情報にたどり着きました。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-cloudwatch-events-s3.html
これによると、
ステップ 2: AWS CloudTrail で証跡を作成する
とありました。また、
Amazon S3 の API イベントが CloudWatch イベント のルールを満たすには、これらのイベントを受信するように CloudTrail の証跡を設定する必要があります。
とあります。S3バケットのAPI 呼び出しの証跡がないと、PutObject などのイベントは EventBridge には流れないようです。なので、CloudTrailのS3証跡を作成する必要がありそうです。CloudTrailの設定を以下のハイライトの箇所に追加しました。
import * as cdk from '@aws-cdk/core'; import * as s3 from '@aws-cdk/aws-s3'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as events from '@aws-cdk/aws-events'; import * as targets from '@aws-cdk/aws-events-targets'; import * as cloudtrail from '@aws-cdk/aws-cloudtrail'; export class HogeStepfunctionsStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // The code that defines your stack goes here // ファイルアップロード用バケット const fileUploadBucket = new s3.Bucket(this, 'fileUploadBucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, }); // StepFunctionsの定義 const definition = new sfn.Pass(this, 'pass'); const stateMachine = new sfn.StateMachine(this, 'StateMachine', { definition, }); // PutObject ➾ EventBridge ➾ StepFunctionsを実現するためにはCloudTrailの設定が必要 const trail = new cloudtrail.Trail(this, 'trail'); trail.addS3EventSelector([{ bucket: fileUploadBucket, }], { readWriteType: cloudtrail.ReadWriteType.ALL, }); // EventBridge ルールのターゲットにStepFunctionsを設定する const sfnTarget = new targets.SfnStateMachine(stateMachine); // ファイルが PUT されたら StepFuntions を起動する EventBridge Rule を作成する const rule = new events.Rule(this, 'CSV put event rule', { description: '特定のバケットにファイルがPUTされたら動作するルールです', eventPattern: { source: ['aws.s3'], detailType: ['AWS API Call via CloudTrail'], detail: { eventSource: ['s3.amazonaws.com'], eventName: [ 'PutObject', 'CopyObject', 'CompleteMultipartUpload', ], requestParameters: { bucketName: [fileUploadBucket.bucketName], }, }, }, }); rule.addTarget(sfnTarget); } }
再度デプロイします。
cdk deploy
以下のように、CloudTrailにアップロード対象S3の証跡が設定されていることを確認します。
再度動作確認
再度、S3バケットに対して適当なファイルをアップロードします。アップロード後、StepFunctionsの実行履歴を確認します。
今度は実行されていそうです。うまくいきました。
まとめ
最初に実装してみたときは、なんでステートマシンが起動しないんだ!?という感じで、結構ハマりました。まさかCloudTrailの設定が必要だとは思いもしませんでした。誰かの参考になれば幸いです。
参考
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-cloudwatch-events-s3.html
https://docs.aws.amazon.com/cdk/api/latest/docs/aws-construct-library.html