こんにちは、CX事業本部 IoT事業部の若槻です。
最近の業務で、実行された時間によって処理を分岐させるAWS Step FunctionsのState Machineを作りたいことがありました。
そのState MachineはEventBridge RuleのCronスケジュールを起動ソースとしていたため、分岐させたい時間毎にRuleを分けて、起動ソースのRuleに応じてState Machine内で分岐がさせられないか?と考えました。
そこで今回は、AWS Step Functionsで起動ソースのEventBridge Ruleに応じて処理を分岐させる実装をAWS CDK v2で作ってみました。
やってみた
実装
AWS CDK v2(TypeScript)で次のようなCDKスタックを作成します。
lib/aws-app-stack.ts
import { Construct } from 'constructs';
import {
aws_events,
aws_events_targets,
aws_stepfunctions,
Stack,
StackProps,
} from 'aws-cdk-lib';
export class AwsAppStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
const RULE_1_NAME = 'rule1';
// StateMachineの起動ソースに応じて分岐するChoice
const choice = new aws_stepfunctions.Choice(this, 'choice');
choice.when(
aws_stepfunctions.Condition.stringEquals(
aws_stepfunctions.JsonPath.stringAt('$.resources[0]'),
`arn:aws:events:${this.region}:${this.account}:rule/${RULE_1_NAME}`,
),
new aws_stepfunctions.Pass(this, 'rule1Pass'),
);
choice.otherwise(new aws_stepfunctions.Pass(this, 'otherPass'));
// StateMachine
const stateMachine = new aws_stepfunctions.StateMachine(
this,
'stateMachine',
{
stateMachineName: 'stateMachine',
definition: choice,
},
);
// EventBridge Rule1(毎時0〜20分に起動)
new aws_events.Rule(this, 'rule1', {
ruleName: RULE_1_NAME,
schedule: aws_events.Schedule.cron({
minute: '0-20',
}),
targets: [new aws_events_targets.SfnStateMachine(stateMachine)],
});
// EventBridge Rule2(毎時21〜40分に起動)
new aws_events.Rule(this, 'rule2', {
ruleName: 'rule2',
schedule: aws_events.Schedule.cron({
minute: '21-40',
}),
targets: [new aws_events_targets.SfnStateMachine(stateMachine)],
});
}
}
ここで、EventBridge Ruleにより起動された際のExecutionのInputは次のようになります。このうちresources
に起動元のRuleの情報が格納されているので、この値をStateMachine内の分岐処理で参照すれば良さそうです。
Input
{
"version": "0",
"id": "f5bb14f2-6316-d9ad-cae3-e03ea6c3a797",
"detail-type": "Scheduled Event",
"source": "aws.events",
"account": "XXXXXXXXXXXX",
"time": "2022-07-13T12:19:00Z",
"region": "ap-northeast-1",
"resources": [
"arn:aws:events:ap-northeast-1:XXXXXXXXXXXX:rule/rule1"
],
"detail": {}
}
そこでChoice Stateで、JsonPath.stringAt('$.resources[0]')
により取得したRuleが指定値(rule1
)と一致しているかどうかを判定しています。
上記をCDK Deployしてスタックをデプロイします。すると次のようなDefinitionのState Machineが作成されます。
Definition
{
"StartAt": "choice",
"States": {
"choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.resources[0]",
"StringEquals": "arn:aws:events:ap-northeast-1:XXXXXXXXXXXX:rule/rule1",
"Next": "rule1Pass"
}
],
"Default": "otherPass"
},
"otherPass": {
"Type": "Pass",
"End": true
},
"rule1Pass": {
"Type": "Pass",
"End": true
}
}
}
動作確認
State Machineの実行一覧を見ると、EventBridge Ruleにより1分毎にState Machineが起動されています。
起動ソースがrule1
となる時間帯の実行を見ると、分岐がrule1Pass
に進んでいます。
起動ソースがrule2
となる時間帯の実行を見ると、分岐がotherPass
に進んでいます。
想定通りの動きとなっていますね!
(別解)起動ソースの情報を使わずに現在時間による分岐をしたい場合
ここまでは起動ソースのEventBridge Ruleが時間帯によって異なることを利用して分岐をさせましたが、起動ソースの情報を使わない(使えない)場合はどうすれば良いでしょう。
その場合は、EvaluateExpression Taskを次のように使用すれば、時間による判定処理を比較的簡単に実装できます。
const isAfter24HourJst = new aws_stepfunctions_tasks.EvaluateExpression(
this,
'isAfter24HourJst',
{
expression: 'new Date().getHours() === 15',
resultPath: '$.isAfter24HourJstOutPut',
},
);
EvaluateExpressionはLambda関数が隠蔽されたTaskなので柔軟な判定処理が可能ですが、コストや実行速度の面で前述の起動ソース(およびJsonPath)を使用した方法には劣ります。メリット/デメリットを理解して適した方法を採用したいですね。
以上