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