AWS Step Functionsで起動ソースのEventBridge Ruleに応じて処理を分岐させる(AWS CDK v2)

2022.07.13

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

こんにちは、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)を使用した方法には劣ります。メリット/デメリットを理解して適した方法を採用したいですね。

以上