Step Functions Express vs Standard完全ガイド — 同一処理で比較するワークフロータイプ選択術

目次

Step Functions Express vs Standard完全ガイド — 同一処理で比較するワークフロータイプ選択術

公開日: 2026-04-13
難易度: 中級
所要時間: 約90分
シリーズ: [Step Functions シリーズ 第7回]

この記事で学ぶこと
– Standard Workflows と Express Workflows の本質的な違い(実行保証・履歴・課金モデル)
– 同期Express(StartSyncExecution)と非同期Express(StartExecution)のユースケース
– 同一ASLをtype変更だけでStandard/Express両方に適用する実践テクニック
– コスト比較: 月1万回実行でExpressが$0.031 vs Standard $0.75(24倍差)
– Terraform での完全構築(コンソール版とASL完全一致)

シリーズ前回記事:
– 第1回: AWS Step Functions 入門
– 第2回: ECS × Step Functions 入門
– 第3回: Step Functions エラーハンドリング完全ガイド
– 第4回: Step Functions 入出力データフロー制御完全ガイド
– 第5回: Step Functions Callbackパターン完全ガイド
– 第6回: Step Functions Distributed Map完全ガイド


Section 1: Express vs Standard Workflows 概念編

本記事では、同一の注文処理ワークフローを StandardExpress の両方で実装し、違いを体験しながら学ぶ。まず概念編として、2つのワークフロータイプの特徴・課金モデル・選択基準を整理する。


1-1. Standard Workflows とは

Standard Workflows は、AWS Step Functions の「デフォルト」タイプである。長時間・重要なビジネスロジックの実行に最適化されている。

主な特徴:

項目仕様
実行保証Exactly-once(正確に1回)
最大実行時間1年
実行履歴コンソール・API で参照可能(90日間保存
課金モデル状態遷移数 × $0.025 / 1,000遷移
無料枠4,000遷移 / 月(永続無料枠)

向いているユースケース:
– 長時間処理(数分〜数時間)
– 実行履歴・監査ログが必要なフロー
– 人間承認(Human Approval)が含まれるフロー
– 重要なビジネスロジック(注文処理・決済フロー)

Exactly-once とは?
ワークフロー内の各ステートが重複して実行されることなく、必ず1回だけ実行される保証。冪等性(べき等性)を考慮しなくてよいため、設計がシンプルになる。


1-2. Express Workflows とは

Express Workflows は、高スループット・短時間処理に特化したタイプである。Standard と比べて大幅にコストを削減できる反面、実行保証や実行履歴の扱いが異なる。

主な特徴:

項目仕様
実行保証At-least-once(少なくとも1回)
最大実行時間5分
実行履歴CloudWatch Logs のみ(コンソールでは実行一覧は確認可能だが詳細履歴なし)
課金モデル実行数 × $1 / 100万実行 + 実行時間(GB秒課金)
無料枠100万実行 / 月 + 100 GB秒 / 月(永続無料枠)

向いているユースケース:
– 高スループット処理(月間数十万〜数百万回の実行)
– 短時間処理(秒〜分単位)
– イベント駆動アーキテクチャ(S3イベント・SNS・Kinesis連携)
– IoT データ処理・ストリーム処理

At-least-once とは?
ステートが稀に複数回実行される可能性がある。そのため、処理側(Lambda 関数など)を冪等(べき等)に設計する必要がある(同じ処理を2回実行しても結果が変わらないように実装する)。


1-3. 同期Express vs 非同期Express

Express Workflows には「同期(Sync)」と「非同期(Async)」の2つの呼び出し方がある。

項目同期Express (Sync)非同期Express (Async)
呼び出し APIStartSyncExecutionStartExecution
応答タイミング実行完了まで待機(最大5分)即座に実行ARNを返す
結果取得レスポンスボディに含まれるCloudWatch Logs を別途確認
ユースケースAPI バックエンド・同期処理非同期バッチ・イベント処理
呼び出し元の制約API Gateway / Lambda / SDK経由のみ制約なし
実行保証At-most-once(高々1回)At-least-once(少なくとも1回)

同期Express の典型的なアーキテクチャ:

クライアント → API Gateway → Step Functions(StartSyncExecution) ↓(最大5分待機)レスポンスをそのまま返す

非同期Express の典型的なアーキテクチャ:

S3 イベント → EventBridge → Step Functions(StartExecution) ↓(即座にARN返却)処理はバックグラウンドで継続

1-4. 比較表(Standard vs Express)

項目StandardExpress
実行時間上限1年5分
実行保証Exactly-onceAt-least-once
実行履歴✅ コンソール/API(90日)CloudWatch Logs のみ
課金モデル状態遷移数課金実行数 + 継続時間課金
無料枠4,000遷移/月100万実行/月 + 100 GB秒/月
最大ステート数上限なし上限なし
実行中の状態確認✅ API/コンソール△ CloudWatch Logs のみ
べき等性の要件不要必須
典型ユースケース注文処理・承認フローIoT/イベント処理・高スループット

1-5. 選択フローチャート

ワークフロータイプ選択の判断フローを以下に示す。

処理時間が5分以内?  NO  → Standard 一択(Expressは5分が上限)  YES ↓月間実行回数が多い(数万回以上)?  NO  → 状態遷移課金の Standard が安い可能性あり → 1-6のコスト試算を参照して決定  YES ↓実行履歴をコンソールで確認・監査したい?  YES → Standard  NO  ↓At-least-once で問題ない(冪等処理が実装可能)?  NO  → Standard  YES → Express(コスト削減)

判断のポイントまとめ:
5分超 → Standard 確定
監査・コンプライアンス要件あり → Standard
高スループット + 短時間 + 冪等処理 → Express


1-6. コスト比較(試算例)

実際の数値でコストを比較する。

シナリオA: 月1万回実行、平均3ステート/実行、平均実行時間2秒、平均メモリ64MB

項目StandardExpress
状態遷移コスト3万遷移 × $0.025/1,000 = $0.75
実行数コスト1万 × $1/100万 = $0.01
実行時間コスト1万回 × 2秒 × 64MB/1,024 = 1,250 GB秒 × $0.00001667 ≒ $0.021
合計/月$0.75$0.031

シナリオB: 月100万回実行(同一スペック)

項目StandardExpress
合計/月$75$3.1

結論: 高スループット × 短時間処理では、Express の料金は Standard の 約1/24
月間実行回数が増えるほど Express の優位性が拡大する。

損益分岐点の目安:
1実行あたり平均3ステートの場合、月間約3.3万遷移(約1.1万実行)以下なら Standard の方が安くなるケースもある。実際の構成に合わせて試算すること。


1-7. 本シリーズとの関連

本記事は Step Functions シリーズ第7弾として位置づけられる。過去記事との関係を整理する。

記事テーマ推奨タイプ
第3弾(エラーハンドリング)Retry/Catch/WaitStandard(長時間・再試行処理)
第5弾(Callback パターン).waitForTaskTokenStandard(人間承認・外部サービス待機)
第4弾(Distributed Map)大規模並列処理Standard / Express 両方(子実行はどちらも可)
本記事(Express vs Standard)同一処理での比較体験両方(比較目的)

Section 2 以降の予告:
– Section 2: 同一の注文処理ワークフローを Standard で実装(Terraform + コンソール操作)
– Section 3: 同じワークフローを Express(同期)で実装・比較
– Section 4: Express(非同期)パターンの実装
– Section 5: コスト最適化の実践(実計測値で比較)


まとめ
Standard: Exactly-once・最大1年・状態遷移課金 → 長時間/重要処理向け
Express: At-least-once・最大5分・実行数+時間課金 → 高スループット/短時間処理向け
– 同期Express は API バックエンドに、非同期Express はイベント駆動に最適
– コスト差は最大24倍以上になる場合があり、適切な選択がコスト最適化の鍵


Section 2: アーキテクチャ解説

概念を理解したところで、次はハンズオンのアーキテクチャを確認しましょう。同一ビジネスロジックをStandard/Express両方で構築することで、タイプの違いが動作にどう影響するかを直感的に体験できます。

2-1. ハンズオンシナリオ概要

本ハンズオンでは「注文処理ワークフロー(在庫確認 → 決済 → 発送 → 完了通知)」を題材に、同一ビジネスロジックをStandard型とExpress型の両方で構築し、動作・履歴管理・コストの違いを体感します。

同じ ASL(Amazon States Language)定義を使いながら、ステートマシンのタイプを切り替えるだけで挙動がどう変わるかを比較できるのがこのシナリオの特徴です。

アーキテクチャ比較

使用リソース一覧

リソースStandard版Express版
SF State Machineorder-standard(Standard型)order-express(Express型)
Lambdaconfirm-inventoryprocess-paymentarrange-shipping(共用)同じ関数を共用
SNSorder-notification(共用)同じトピックを共用
実行履歴コンソール / API(90日保存)CloudWatch Logs のみ

Lambda 関数と SNS トピックは両ステートマシンで共用します。これにより「タイプが違っても ASL と処理ロジックは完全に同一」という点を直感的に理解できます。


2-2. 実行フロー比較

実行フロー比較

Standard型とExpress型の最大の違いは実行履歴の保存先と参照手段にあります。

Standard ワークフロー

StartExecution  ↓ConfirmInventory  (状態遷移を履歴DB に自動記録)  ↓ProcessPayment (状態遷移を履歴DB に自動記録)  ↓ArrangeShipping(状態遷移を履歴DB に自動記録)  ↓NotifyCompletion  (状態遷移を履歴DB に自動記録)  ↓SUCCEEDED  ↓[コンソール / API で全履歴参照可能 ✓]
  • 各状態遷移がすべて履歴DBに記録されるため、マネジメントコンソールの Step Functions 画面から実行グラフを視覚的に確認できます。
  • GetExecutionHistory API を呼び出せば、入出力データを含む完全なイベントログを取得できます。
  • 最大90日間保存されるため、後からのデバッグや監査ログとして活用できます。

Express ワークフロー

StartExecution / StartSyncExecution  ↓ConfirmInventory  → CloudWatch Logs  ↓ProcessPayment → CloudWatch Logs  ↓ArrangeShipping→ CloudWatch Logs  ↓NotifyCompletion  → CloudWatch Logs  ↓SUCCEEDED  ↓[実行履歴は CloudWatch Logs のみ △]
  • 各状態遷移のログは CloudWatch Logs に書き込まれます(ただしログ出力は有効化設定が必要)。
  • マネジメントコンソールの Step Functions 画面では実行履歴を参照できません
  • StartSyncExecution API を使うと同期実行(最大5分)ができ、レスポンスに直接出力が返ってきます。

ポイント: デバッグ・監査が重要なワークフローには Standard型、高スループット・短時間のバッチ処理には Express型が適しています。


2-3. 課金モデル比較

課金モデル比較

Standard の課金体系

Standard型は状態遷移数で課金されます。

課金要素単価
状態遷移$0.025 / 1,000遷移
最初の 4,000遷移/月無料枠

本ハンズオンのワークフロー(4状態)では 1実行あたり約3遷移が発生します(開始・各 Lambda 呼び出し・終了をカウント)。

月間実行数状態遷移数月額費用
1万回3万遷移$0.75
10万回30万遷移$7.50
100万回300万遷移$75.00

Express の課金体系

Express型は実行数 + 継続時間(GB秒)の二軸で課金されます。

課金要素単価
実行数$1.00 / 100万実行
継続時間$0.00001667 / GB秒
最初の 100万実行/月 および 300GB秒/月無料枠

実行時間を平均2秒、メモリ64MBと仮定した場合(= 2 × 0.0625 GB = 0.125 GB秒/実行):

月間実行数実行数課金継続時間課金月額合計
1万回$0.010$0.021$0.031
10万回$0.10$0.21$0.31
100万回$1.00$2.10$3.10

損益分岐点の考え方

Standard の $0.025/1,000遷移 と Express の実行コストを比較すると、約33,000遷移/月(=約1.1万実行/月 × 3遷移) が損益分岐点となります。

Standard コスト = Express コスト0.025/1,000 × N遷移 = Express 月額N ≈ 33,000 遷移/月
  • 33,000遷移/月 未満: Standard の方が安い場合もあるが、そもそも無料枠内に収まる可能性大
  • 33,000遷移/月 超: Express の方が圧倒的に安価(最大24倍のコスト差)

実務判断の目安: 高頻度(秒間数百件以上)かつ短時間(5分以内)のワークフローは Express型を選択。複雑な長期ワークフローや監査ログが必要な場合は Standard型を選択。


Section 3: AWSコンソールでのハンズオン

アーキテクチャを把握したところで、実際にAWSコンソールを使って構築してみましょう。StandardとExpressを段階的に作成し、その違いを手を動かしながら体験します。

このセクションでは、注文処理ワークフロー(在庫確認→決済→発送→完了通知)を題材に、Standard版→Express版の順に構築し、動作・履歴・コストの違いを実際に体験します。同一のASLを使うことで、typeの違いだけで挙動が変わることを体感できます。


3-1. 前提条件

AWSアカウントと権限

以下のIAMアクションが実行できるユーザー/ロールが必要です。

  • Lambda: lambda:CreateFunction, lambda:InvokeFunction, lambda:UpdateFunctionCode
  • Step Functions: states:CreateStateMachine, states:StartExecution, states:StartSyncExecution, states:DescribeExecution
  • SNS: sns:CreateTopic, sns:Subscribe, sns:Publish
  • IAM: iam:CreateRole, iam:AttachRolePolicy, iam:PassRole
  • CloudWatch Logs: logs:CreateLogGroup, logs:FilterLogEvents

AWS CLI

Step D(同期Express実行)で start-sync-execution コマンドを使用します。

# バージョン確認(v2.x 推奨)aws --version# デフォルトリージョンを ap-northeast-1 に設定aws configure set region ap-northeast-1

3-2. Lambda 3関数のデプロイ(モック実装)

注文処理の各ステップをシミュレートするモックLambda関数を作成します。

confirm_inventory 関数

AWSコンソール → Lambda → 「関数の作成」

  • 関数名: confirm_inventory
  • ランタイム: Python 3.12
  • アーキテクチャ: x86_64

コードエディタに以下を入力し、「Deploy」:

import jsondef lambda_handler(event, context): order_id = event.get("order_id", "unknown") return {"available": True, "stock": 100, "order_id": order_id}

確認: テストイベント {"order_id": "ORD-001"} で実行し、{"available": true, "stock": 100, "order_id": "ORD-001"} が返ることを確認。

process_payment 関数

  • 関数名: process_payment
  • ランタイム: Python 3.12
import jsonimport uuiddef lambda_handler(event, context): return {"transaction_id": str(uuid.uuid4())[:8], "status": "approved"}

arrange_shipping 関数

  • 関数名: arrange_shipping
  • ランタイム: Python 3.12
import jsonimport uuiddef lambda_handler(event, context): return {"tracking_id": f"TRACK-{str(uuid.uuid4())[:8].upper()}"}

関数ARNの記録

3関数を作成したら、各関数のARNをメモしておきます。後のASL設定で使用します。

confirm_inventory  : arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:confirm_inventoryprocess_payment : arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:process_paymentarrange_shipping: arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:arrange_shipping

3-3. SNSトピック作成

AWSコンソール → Simple Notification Service (SNS) → 「トピックの作成」

  • タイプ: スタンダード
  • 名前: order-notification

作成後、「サブスクリプションの作成」でメールアドレスを登録します(任意)。

  • プロトコル: Eメール
  • エンドポイント: 受信したいメールアドレス

確認メールが届いたら「Confirm subscription」をクリックして有効化します。

トピックARNの記録: arn:aws:sns:ap-northeast-1:<ACCOUNT_ID>:order-notification


3-4. IAMロール作成

Step Functionsが各サービスを呼び出すためのロールを作成します。

AWSコンソール → IAM → 「ロールの作成」

  1. 信頼されたエンティティ: AWSのサービス → Step Functions
  2. ロール名: sf-order-execution-role

ロール作成後、以下のインラインポリシーを追加します。

{  "Version": "2012-10-17",  "Statement": [ {"Effect": "Allow","Action": "lambda:InvokeFunction","Resource": [  "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:confirm_inventory",  "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:process_payment",  "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:arrange_shipping"] }, {"Effect": "Allow","Action": "sns:Publish","Resource": "arn:aws:sns:ap-northeast-1:<ACCOUNT_ID>:order-notification" }, {"Effect": "Allow","Action": [  "logs:CreateLogGroup",  "logs:CreateLogDelivery",  "logs:GetLogDelivery",  "logs:UpdateLogDelivery",  "logs:DeleteLogDelivery",  "logs:ListLogDeliveries",  "logs:PutResourcePolicy",  "logs:DescribeResourcePolicies",  "logs:DescribeLogGroups",  "logs:CreateLogStream",  "logs:PutLogEvents"],"Resource": "*" }  ]}

ロールARNの記録: arn:aws:iam::<ACCOUNT_ID>:role/sf-order-execution-role


3-5. 段階的構築

4つのステップで機能を段階的に追加しながら、StandardとExpressの違いを体感します。

Step A: Standard版を構築 — 実行履歴を体験する

ステートマシンの作成

AWSコンソール → Step Functions → 「ステートマシンの作成」

  • 作成方法: コードでワークフローを記述
  • タイプ: 標準(Standard)
  • 名前: order-standard-step-a
  • ロール: sf-order-execution-role

ASLエディタに以下を一字一句そのまま貼り付け、<LAMBDA_INVENTORY_ARN><LAMBDA_PAYMENT_ARN><LAMBDA_SHIPPING_ARN><SNS_TOPIC_ARN> を実際のARNに置き換えます。

{  "Comment": "注文処理ワークフロー — Step A(基本フロー)",  "StartAt": "ConfirmInventory",  "States": { "ConfirmInventory": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "<LAMBDA_INVENTORY_ARN>",  "Payload.$": "$"},"ResultSelector": {  "available.$": "$.Payload.available",  "stock.$": "$.Payload.stock"},"ResultPath": "$.inventory","Next": "ProcessPayment" }, "ProcessPayment": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "<LAMBDA_PAYMENT_ARN>",  "Payload.$": "$"},"ResultSelector": {  "transaction_id.$": "$.Payload.transaction_id",  "status.$": "$.Payload.status"},"ResultPath": "$.payment","Next": "ArrangeShipping" }, "ArrangeShipping": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "<LAMBDA_SHIPPING_ARN>",  "Payload.$": "$"},"ResultSelector": {  "tracking_id.$": "$.Payload.tracking_id"},"ResultPath": "$.shipping","Next": "NotifyCompletion" }, "NotifyCompletion": {"Type": "Task","Resource": "arn:aws:states:::sns:publish","Parameters": {  "TopicArn": "<SNS_TOPIC_ARN>",  "Message.$": "States.Format('注文 {} が完了しました', $.order_id)",  "Subject": "注文完了通知"},"ResultPath": null,"End": true }  }}

実行と確認

「実行の開始」をクリックし、以下の入力を貼り付けます。

{"order_id": "ORD-001", "customer_id": "CUST-001", "items": [{"product": "laptop", "qty": 1}]}

実行完了後、実行詳細画面を開きます。

  • グラフビュー: 各ステートが緑色に変わり、通過した経路を視覚確認できます
  • イベント履歴: 各ステートの開始・終了・所要時間がタイムスタンプ付きで記録されています
  • ステートごとの入出力: 各ステートをクリックすると、入力JSONと出力JSONを確認できます

これがStandardの強みです。 数日〜数ヶ月後でも実行履歴を振り返り、どのステートで何が起きたかを詳細に調査できます。


Step B: Express版を構築 — CloudWatch Logsのみ

ステートマシンの作成

同じASL(Step A)を使って今度はExpressステートマシンを作成します。

AWSコンソール → Step Functions → 「ステートマシンの作成」

  • タイプ: 高速(Express)← ここだけが違います
  • 名前: order-express-step-b
  • ロール: sf-order-execution-role
  • ログレベル: ALL(CloudWatch Logsへの出力を有効化)
  • ロググループ: /aws/states/order-express-step-b(自動作成または手動指定)

ASLはStep Aと全く同一のJSONを貼り付け、ARNを同様に置き換えます。

実行と確認

同じ入力で実行します。

{"order_id": "ORD-002", "customer_id": "CUST-001", "items": [{"product": "laptop", "qty": 1}]}

実行一覧には表示されますが、「実行の詳細」ボタンが表示されない(または詳細が取得できない)ことを確認します。

Expressの実行ログはCloudWatch Logsで確認します。

aws logs filter-log-events \  --log-group-name /aws/states/order-express-step-b \  --start-time $(date -d "10 min ago" +%s000)

macOSの場合: date -d の代わりに date -v-10M +%s000 を使用してください。

ログ出力例:

{  "id": "1",  "type": "ExecutionStarted",  "details": { "input": "{\"order_id\": \"ORD-002\", ...}", "roleArn": "arn:aws:iam::<ACCOUNT_ID>:role/sf-order-execution-role"  }}


Step C: 比較検証 — 速度・履歴・課金の違いを体感

同一の注文処理を両タイプで実行し、違いを比較します。

比較項目StandardExpress
実行完了後の履歴確認コンソールで即座に確認可CWLogs検索が必要
同一実行の重複排除される(Exactly-once)重複の可能性あり(At-least-once)
実行速度(体感)ほぼ同じほぼ同じ
エラー時の再実行実行履歴から再試行可能CWLogsで調査後に再実行
実行履歴の保存期間90日間CloudWatch Logs保存期間に依存
料金体系状態遷移回数 + 期間課金実行回数 + 実行時間課金

ポイント: この段階では実行速度はほぼ同じです。Expressの真価は高頻度・短時間実行のシナリオで発揮されます(1秒間に何千回も実行するようなユースケース)。


Step D: 同期Express体験 — StartSyncExecution API

完成形ステートマシンの作成

Step D ASL(エラーハンドリング完成形)で、StandardとExpressの両方を作成します。

Standard版: order-standard-final(タイプ: 標準)
Express版: order-express-final(タイプ: 高速)

両方に以下のASLを一字一句そのまま貼り付け、ARNを置き換えます。

{  "Comment": "注文処理ワークフロー — Step D(エラーハンドリング完成形)",  "StartAt": "ConfirmInventory",  "States": { "ConfirmInventory": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "<LAMBDA_INVENTORY_ARN>",  "Payload.$": "$"},"Retry": [  { "ErrorEquals": ["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException","Lambda.TooManyRequestsException" ], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2  }],"Catch": [  { "ErrorEquals": ["States.ALL"], "Next": "OrderFailed", "ResultPath": "$.error"  }],"ResultSelector": {  "available.$": "$.Payload.available",  "stock.$": "$.Payload.stock"},"ResultPath": "$.inventory","Next": "ProcessPayment" }, "ProcessPayment": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "<LAMBDA_PAYMENT_ARN>",  "Payload.$": "$"},"Retry": [  { "ErrorEquals": ["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException","Lambda.TooManyRequestsException" ], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2  }],"Catch": [  { "ErrorEquals": ["States.ALL"], "Next": "OrderFailed", "ResultPath": "$.error"  }],"ResultSelector": {  "transaction_id.$": "$.Payload.transaction_id",  "status.$": "$.Payload.status"},"ResultPath": "$.payment","Next": "ArrangeShipping" }, "ArrangeShipping": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "<LAMBDA_SHIPPING_ARN>",  "Payload.$": "$"},"Retry": [  { "ErrorEquals": ["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException","Lambda.TooManyRequestsException" ], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2  }],"Catch": [  { "ErrorEquals": ["States.ALL"], "Next": "OrderFailed", "ResultPath": "$.error"  }],"ResultSelector": {  "tracking_id.$": "$.Payload.tracking_id"},"ResultPath": "$.shipping","Next": "NotifyCompletion" }, "NotifyCompletion": {"Type": "Task","Resource": "arn:aws:states:::sns:publish","Parameters": {  "TopicArn": "<SNS_TOPIC_ARN>",  "Message.$": "States.Format('注文 {} が完了しました(追跡番号: {})', $.order_id, $.shipping.tracking_id)",  "Subject": "注文完了通知"},"ResultPath": null,"Next": "OrderCompleted" }, "OrderCompleted": {"Type": "Succeed" }, "OrderFailed": {"Type": "Fail","Error": "OrderProcessingFailed","Cause": "注文処理中にエラーが発生しました" }  }}

同期実行(StartSyncExecution)

ExpressステートマシンはHTTP APIから同期的に呼び出すことができます。CLI で体験しましょう。

# 同期Express実行(レスポンスに結果が直接含まれる)aws stepfunctions start-sync-execution \  --state-machine-arn <EXPRESS_SM_ARN> \  --input '{"order_id": "ORD-SYNC-001", "customer_id": "CUST-001", "items": []}' \  --region ap-northeast-1

実行完了まで待機し、コマンドが返るまでブロックされることを確認します。

レスポンス例:

{  "executionArn": "arn:aws:states:ap-northeast-1:<ACCOUNT_ID>:express:order-express-final:...",  "stateMachineArn": "arn:aws:states:ap-northeast-1:<ACCOUNT_ID>:stateMachine:order-express-final",  "name": "...",  "startDate": "2026-04-13T07:40:00.000Z",  "stopDate": "2026-04-13T07:40:00.850Z",  "status": "SUCCEEDED",  "output": "{\"order_id\":\"ORD-SYNC-001\",\"customer_id\":\"CUST-001\",\"items\":[],\"inventory\":{\"available\":true,\"stock\":100},\"payment\":{\"transaction_id\":\"a1b2c3d4\",\"status\":\"approved\"},\"shipping\":{\"tracking_id\":\"TRACK-E5F6G7H8\"}}"}

output フィールドに結果JSONが直接含まれる点に注目してください。

StandardのStartExecution(非同期)との比較

Standard版で同様の実行を行うと、レスポンスにはexecutionArnのみが返り、結果は別途DescribeExecution APIで取得する必要があります。

# Standard版は非同期(executionArnのみが返る)aws stepfunctions start-execution \  --state-machine-arn <STANDARD_SM_ARN> \  --input '{"order_id": "ORD-ASYNC-001", "customer_id": "CUST-001", "items": []}' \  --region ap-northeast-1
観点StartExecution (Standard)StartSyncExecution (Express)
戻り値executionArnのみ実行結果(output)まで含む
呼び出し方非同期(fire & forget)同期(結果を待つ)
タイムアウト上限1年5分
用途長時間ワークフロー、人間の承認フローAPI統合、リアルタイム処理

同期Expressは、API GatewayやLambdaからStep Functionsを呼び出し、結果をそのままHTTPレスポンスとして返すアーキテクチャで特に有効です。マイクロサービス間の高速オーケストレーションに最適です。


まとめ(Section 3)

このセクションで体験したこと:

ステップ構築内容学んだこと
Step AStandard基本フロー実行履歴・イベント詳細がコンソールで確認できる
Step BExpress基本フロー(同一ASL)履歴非表示・CWLogsで調査する
Step C両タイプの比較可視性・冗長性・課金の違い
Step Dエラーハンドリング完成形 + 同期実行StandardとExpressで同一ASLが動く、同期実行APIの使い方

重要ポイント: StandardとExpressで使用したASLは完全に同一です。typeの選択だけで、実行セマンティクス・可視性・料金体系が変わります。次のTerraformセクションでも、このASLをそのままコード化します。


Section 4: TerraformでのStandard/Express構築

コンソールで動作を確認したら、次はTerraformでInfrastructure as Codeとして管理しましょう。同一のASLテンプレートファイルからStandard/Expressを生成するアプローチが、IaCの強みを活かします。

このセクションでは、Section 3(コンソール版)で構築した注文処理ワークフローを Terraform でコード化します。Standard ワークフローと Express ワークフローを 同一の ASL テンプレートファイル から生成することで、「typeだけ異なり、ステートマシン定義は完全に同じ」という Express vs Standard の核心を体験できます。


4-1. 前提条件

作業を始める前に以下の環境が整っていることを確認してください。

  • Terraform 1.0 以上 がインストール済み(terraform version で確認)
  • AWS CLI が設定済み(aws configure でアクセスキー・リージョン等を設定)
  • 以下の AWS リソースを作成・管理できる IAM 権限:
  • AWS Lambda(作成・更新・削除)
  • AWS Step Functions(ステートマシン作成・更新・削除)
  • Amazon SNS(トピック作成・サブスクリプション管理)
  • AWS IAM(ロール・ポリシーの作成・管理)
  • Amazon CloudWatch Logs(ロググループ作成・管理)

4-2. ディレクトリ構成

プロジェクトディレクトリ sf-order/ を作成し、以下の構成にします。

sf-order/├── main.tf# メインリソース定義├── variables.tf # 変数定義├── outputs.tf# 出力定義├── lambda/│├── confirm_inventory.py # 在庫確認 Lambda│├── process_payment.py# 決済処理 Lambda│└── arrange_shipping.py  # 発送手配 Lambda└── statemachine/ └── definition.json.tpl  # ASL テンプレート(Standard/Express 共通)
mkdir -p sf-order/lambda sf-order/statemachinecd sf-order

4-3. Lambda ソースコード(コンソール版と同一ロジック)

Section 3 のコンソール版で作成した Lambda と同じロジックをファイルに保存します。

lambda/confirm_inventory.py

在庫確認関数。注文 ID を受け取り、在庫あり・在庫数 100 を返します。

import jsondef lambda_handler(event, context): order_id = event.get("order_id", "unknown") return {"available": True, "stock": 100, "order_id": order_id}

lambda/process_payment.py

決済処理関数。ランダムなトランザクション ID を生成して返します。

import json, uuiddef lambda_handler(event, context): return {"transaction_id": str(uuid.uuid4())[:8], "status": "approved"}

lambda/arrange_shipping.py

発送手配関数。TRACK-XXXXXXXX 形式の追跡番号を生成して返します。

import json, uuiddef lambda_handler(event, context): return {"tracking_id": f"TRACK-{str(uuid.uuid4())[:8].upper()}"}

4-4. statemachine/definition.json.tpl

ステートマシン定義は テンプレートファイル として管理します。${...} の部分は Terraform の templatefile() 関数が補間します。

ポイント: このテンプレートは Standard 版と Express 版で共通 です。aws_sfn_state_machine リソースの type 属性("STANDARD" / "EXPRESS")だけが異なり、ASL 定義は一字一句同じです。これが Section 1 で解説した「Express と Standard は ASL 互換」という特性そのものです。

コンソール版(Section 3)の Step D と同じ ASL 構造です。 Retry・Catch・ResultSelector・ResultPath・SNS メッセージフォーマットがすべて一致しています。

{  "Comment": "注文処理ワークフロー — Step D(エラーハンドリング完成形)",  "StartAt": "ConfirmInventory",  "States": { "ConfirmInventory": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "${lambda_inventory_arn}",  "Payload.$": "$"},"Retry": [  { "ErrorEquals": ["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException","Lambda.TooManyRequestsException" ], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2  }],"Catch": [  { "ErrorEquals": ["States.ALL"], "Next": "OrderFailed", "ResultPath": "$.error"  }],"ResultSelector": {  "available.$": "$.Payload.available",  "stock.$": "$.Payload.stock"},"ResultPath": "$.inventory","Next": "ProcessPayment" }, "ProcessPayment": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "${lambda_payment_arn}",  "Payload.$": "$"},"Retry": [  { "ErrorEquals": ["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException","Lambda.TooManyRequestsException" ], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2  }],"Catch": [  { "ErrorEquals": ["States.ALL"], "Next": "OrderFailed", "ResultPath": "$.error"  }],"ResultSelector": {  "transaction_id.$": "$.Payload.transaction_id",  "status.$": "$.Payload.status"},"ResultPath": "$.payment","Next": "ArrangeShipping" }, "ArrangeShipping": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "${lambda_shipping_arn}",  "Payload.$": "$"},"Retry": [  { "ErrorEquals": ["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException","Lambda.TooManyRequestsException" ], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2  }],"Catch": [  { "ErrorEquals": ["States.ALL"], "Next": "OrderFailed", "ResultPath": "$.error"  }],"ResultSelector": {  "tracking_id.$": "$.Payload.tracking_id"},"ResultPath": "$.shipping","Next": "NotifyCompletion" }, "NotifyCompletion": {"Type": "Task","Resource": "arn:aws:states:::sns:publish","Parameters": {  "TopicArn": "${sns_topic_arn}",  "Message.$": "States.Format('注文 {} が完了しました(追跡番号: {})', $.order_id, $.shipping.tracking_id)",  "Subject": "注文完了通知"},"ResultPath": null,"Next": "OrderCompleted" }, "OrderCompleted": {"Type": "Succeed" }, "OrderFailed": {"Type": "Fail","Error": "OrderProcessingFailed","Cause": "注文処理中にエラーが発生しました" }  }}

templatefile() で補間される変数一覧:

変数名内容
${lambda_inventory_arn}在庫確認 Lambda の ARN
${lambda_payment_arn}決済処理 Lambda の ARN
${lambda_shipping_arn}発送手配 Lambda の ARN
${sns_topic_arn}注文完了通知 SNS トピックの ARN

Terraform がリソースを作成した後、実際の ARN 値をこれらの変数に埋め込んで JSON を生成します。


4-5. variables.tf

variable "aws_region" {  default = "ap-northeast-1"}variable "project_name" {  default = "sf-order"}variable "notification_email" {  description = "SNS完了通知の送信先メールアドレス"  type  = string}

4-6. main.tf(完全なコード)

すべての AWS リソースを定義します。terraform plan が通る完全なコードです。

terraform {  required_providers { aws = {source  = "hashicorp/aws"version = "~> 5.0" }  }}provider "aws" {  region = var.aws_region}# -----------------------------------------------------------# Lambda: ソースコードの ZIP 化# -----------------------------------------------------------data "archive_file" "confirm_inventory" {  type  = "zip"  source_file = "${path.module}/lambda/confirm_inventory.py"  output_path = "${path.module}/lambda/confirm_inventory.zip"}data "archive_file" "process_payment" {  type  = "zip"  source_file = "${path.module}/lambda/process_payment.py"  output_path = "${path.module}/lambda/process_payment.zip"}data "archive_file" "arrange_shipping" {  type  = "zip"  source_file = "${path.module}/lambda/arrange_shipping.py"  output_path = "${path.module}/lambda/arrange_shipping.zip"}# -----------------------------------------------------------# IAM: Lambda 実行ロール# -----------------------------------------------------------resource "aws_iam_role" "lambda_role" {  name = "${var.project_name}-lambda-role"  assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{Action = "sts:AssumeRole"Effect = "Allow"Principal = { Service = "lambda.amazonaws.com" } }]  })}resource "aws_iam_role_policy_attachment" "lambda_basic" {  role = aws_iam_role.lambda_role.name  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"}# -----------------------------------------------------------# CloudWatch Logs: Lambda 用ロググループ# -----------------------------------------------------------resource "aws_cloudwatch_log_group" "lambda_confirm_inventory" {  name  = "/aws/lambda/${var.project_name}-confirm-inventory"  retention_in_days = 7}resource "aws_cloudwatch_log_group" "lambda_process_payment" {  name  = "/aws/lambda/${var.project_name}-process-payment"  retention_in_days = 7}resource "aws_cloudwatch_log_group" "lambda_arrange_shipping" {  name  = "/aws/lambda/${var.project_name}-arrange-shipping"  retention_in_days = 7}# -----------------------------------------------------------# Lambda 関数# -----------------------------------------------------------resource "aws_lambda_function" "confirm_inventory" {  function_name = "${var.project_name}-confirm-inventory"  role = aws_iam_role.lambda_role.arn  handler = "confirm_inventory.lambda_handler"  runtime = "python3.12"  filename= data.archive_file.confirm_inventory.output_path  source_code_hash = data.archive_file.confirm_inventory.output_base64sha256  depends_on = [aws_cloudwatch_log_group.lambda_confirm_inventory]}resource "aws_lambda_function" "process_payment" {  function_name = "${var.project_name}-process-payment"  role = aws_iam_role.lambda_role.arn  handler = "process_payment.lambda_handler"  runtime = "python3.12"  filename= data.archive_file.process_payment.output_path  source_code_hash = data.archive_file.process_payment.output_base64sha256  depends_on = [aws_cloudwatch_log_group.lambda_process_payment]}resource "aws_lambda_function" "arrange_shipping" {  function_name = "${var.project_name}-arrange-shipping"  role = aws_iam_role.lambda_role.arn  handler = "arrange_shipping.lambda_handler"  runtime = "python3.12"  filename= data.archive_file.arrange_shipping.output_path  source_code_hash = data.archive_file.arrange_shipping.output_base64sha256  depends_on = [aws_cloudwatch_log_group.lambda_arrange_shipping]}# -----------------------------------------------------------# SNS: 注文完了通知# -----------------------------------------------------------resource "aws_sns_topic" "order_notification" {  name = "${var.project_name}-order-notification"}resource "aws_sns_topic_subscription" "email" {  topic_arn = aws_sns_topic.order_notification.arn  protocol  = "email"  endpoint  = var.notification_email}# -----------------------------------------------------------# CloudWatch Logs: Step Functions 用ロググループ# -----------------------------------------------------------resource "aws_cloudwatch_log_group" "sf_standard" {  name  = "/aws/states/${var.project_name}-standard"  retention_in_days = 7}resource "aws_cloudwatch_log_group" "sf_express" {  name  = "/aws/states/${var.project_name}-express"  retention_in_days = 7}# -----------------------------------------------------------# IAM: Step Functions 実行ロール# -----------------------------------------------------------resource "aws_iam_role" "sf_execution_role" {  name = "${var.project_name}-sf-execution-role"  assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{Action = "sts:AssumeRole"Effect = "Allow"Principal = { Service = "states.amazonaws.com" } }]  })}resource "aws_iam_role_policy" "sf_policy" {  name = "${var.project_name}-sf-policy"  role = aws_iam_role.sf_execution_role.id  policy = jsonencode({ Version = "2012-10-17" Statement = [{  Effect = "Allow"  Action = ["lambda:InvokeFunction"]  Resource = [ aws_lambda_function.confirm_inventory.arn, aws_lambda_function.process_payment.arn, aws_lambda_function.arrange_shipping.arn,  ]},{  Effect= "Allow"  Action= ["sns:Publish"]  Resource = [aws_sns_topic.order_notification.arn]},{  Effect = "Allow"  Action = [ "logs:CreateLogDelivery", "logs:CreateLogGroup", "logs:PutLogEvents", "logs:GetLogDelivery", "logs:UpdateLogDelivery", "logs:DeleteLogDelivery", "logs:ListLogDeliveries", "logs:PutResourcePolicy", "logs:DescribeResourcePolicies", "logs:DescribeLogGroups",  ]  Resource = ["*"]}, ]  })}# -----------------------------------------------------------# Step Functions: Standard ワークフロー# -----------------------------------------------------------# コンソール版(Section 3)の Step D と ASL 構造が完全一致しています。# Standard 版と Express 版は同一の templatefile を使用しており、# type 属性だけが "STANDARD" / "EXPRESS" で異なります。resource "aws_sfn_state_machine" "order_standard" {  name  = "${var.project_name}-standard"  role_arn = aws_iam_role.sf_execution_role.arn  type  = "STANDARD"  definition = templatefile("${path.module}/statemachine/definition.json.tpl", { lambda_inventory_arn = aws_lambda_function.confirm_inventory.arn lambda_payment_arn= aws_lambda_function.process_payment.arn lambda_shipping_arn  = aws_lambda_function.arrange_shipping.arn sns_topic_arn  = aws_sns_topic.order_notification.arn  })  logging_configuration { log_destination  = "${aws_cloudwatch_log_group.sf_standard.arn}:*" include_execution_data = false level= "ERROR"  }  depends_on = [aws_iam_role_policy.sf_policy]}# -----------------------------------------------------------# Step Functions: Express ワークフロー# -----------------------------------------------------------# Standard 版と完全に同じ templatefile を使用(ASL は一字一句同じ)。# Express ではログが唯一の実行履歴であるため、level = "ALL" を推奨。resource "aws_sfn_state_machine" "order_express" {  name  = "${var.project_name}-express"  role_arn = aws_iam_role.sf_execution_role.arn  type  = "EXPRESS"  definition = templatefile("${path.module}/statemachine/definition.json.tpl", { lambda_inventory_arn = aws_lambda_function.confirm_inventory.arn lambda_payment_arn= aws_lambda_function.process_payment.arn lambda_shipping_arn  = aws_lambda_function.arrange_shipping.arn sns_topic_arn  = aws_sns_topic.order_notification.arn  })  logging_configuration { log_destination  = "${aws_cloudwatch_log_group.sf_express.arn}:*" include_execution_data = true level= "ALL"  }  depends_on = [aws_iam_role_policy.sf_policy]}

コードのポイント:

項目StandardExpress
type"STANDARD""EXPRESS"
definitiontemplatefile(...) — 共通templatefile(...) — 共通(完全同一)
logging_configuration.level"ERROR""ALL"(実行履歴がないため詳細ログ必須)
include_execution_datafalsetrue

4-7. outputs.tf

output "standard_state_machine_arn" {  description = "Standard ワークフローの ARN"  value = aws_sfn_state_machine.order_standard.arn}output "express_state_machine_arn" {  description = "Express ワークフローの ARN"  value = aws_sfn_state_machine.order_express.arn}output "lambda_inventory_arn" {  description = "在庫確認 Lambda の ARN"  value = aws_lambda_function.confirm_inventory.arn}output "lambda_payment_arn" {  description = "決済処理 Lambda の ARN"  value = aws_lambda_function.process_payment.arn}output "lambda_shipping_arn" {  description = "発送手配 Lambda の ARN"  value = aws_lambda_function.arrange_shipping.arn}output "sns_topic_arn" {  description = "注文完了通知 SNS トピックの ARN"  value = aws_sns_topic.order_notification.arn}

4-8. デプロイ手順

# 1. Terraform 初期化(プロバイダーのダウンロード)terraform init# 2. 実行計画の確認terraform plan -var="notification_email=your@email.com"# 3. リソースの作成terraform apply -var="notification_email=your@email.com"

terraform apply 完了後、SNS サブスクリプションの 確認メールnotification_email 宛に届きます。メール内の「Confirm subscription」リンクをクリックしてサブスクリプションを有効化してください。


4-9. 動作確認(Standard版)

Standard ワークフローは 非同期実行 です。start-execution コマンドで開始し、コンソールまたは CLI で実行履歴を確認できます。

# Standard 版: 非同期実行aws stepfunctions start-execution \  --state-machine-arn $(terraform output -raw standard_state_machine_arn) \  --input '{"order_id": "TF-001", "customer_id": "CUST-001", "items": [{"product": "laptop", "qty": 1}]}' \  --region ap-northeast-1
# 実行一覧確認(コンソールで詳細履歴を確認可能)aws stepfunctions list-executions \  --state-machine-arn $(terraform output -raw standard_state_machine_arn)

AWSコンソールでの確認手順:

  1. AWS コンソール > Step Functions > ステートマシン > sf-order-standard を選択
  2. 「実行」タブを開き、実行 ID をクリック
  3. ビジュアルワークフロー・イベント履歴・入出力データを確認

Standard ワークフローの実行履歴は 90日間 保持されます。


4-10. 動作確認(Express版)

Express ワークフローは 同期実行(start-sync-execution が利用可能です。コマンドのレスポンスに実行結果が直接含まれます。

# Express 版: 同期実行(結果がレスポンスに含まれる)aws stepfunctions start-sync-execution \  --state-machine-arn $(terraform output -raw express_state_machine_arn) \  --input '{"order_id": "TF-SYNC-001", "customer_id": "CUST-001", "items": [{"product": "laptop", "qty": 1}]}' \  --region ap-northeast-1

レスポンス例:

{ "executionArn": "arn:aws:states:ap-northeast-1:...", "stateMachineArn": "arn:aws:states:ap-northeast-1:...:stateMachine:sf-order-express", "status": "SUCCEEDED", "output": "{...最終出力データ...}"}

Standard と異なり、Express の実行履歴はコンソールに表示されません。ログは CloudWatch Logs で確認します。


4-11. Standard vs Express の比較確認

両方のワークフローを実行し、以下の点を比較してみましょう。

実行履歴の確認方法の違い

Standard 版: AWSコンソール

  1. Step Functions コンソール > sf-order-standard > 「実行」タブ
  2. 各ステートの実行時間・入出力・イベント履歴を詳細に閲覧可能
  3. 90日間の履歴が自動保持される

Express 版: CloudWatch Logs

  1. CloudWatch コンソール > ロググループ > /aws/states/sf-order-express
  2. ログストリームを開き、各ステートのログエントリを確認
  3. level = "ALL" 設定により、すべてのステート遷移が記録されている
# Express 版のログを CLI で確認aws logs filter-log-events \  --log-group-name /aws/states/sf-order-express \  --region ap-northeast-1 \  --limit 20

ASL の同一性確認

重要: 2つのステートマシンは type 以外まったく同じです。

# Standard 版の定義を取得aws stepfunctions describe-state-machine \  --state-machine-arn $(terraform output -raw standard_state_machine_arn) \  --query 'definition' --output text | python3 -m json.tool > /tmp/standard_def.json# Express 版の定義を取得aws stepfunctions describe-state-machine \  --state-machine-arn $(terraform output -raw express_state_machine_arn) \  --query 'definition' --output text | python3 -m json.tool > /tmp/express_def.json# 差分確認(ASL は完全一致のはず)diff /tmp/standard_def.json /tmp/express_def.json

diff の出力が空(差分なし)であれば、Standard と Express で まったく同一の ASL が使われていることが確認できます。これが「Terraform の templatefile() で同一定義を共有する」アプローチの実証です。


クリーンアップ

動作確認が完了したらリソースを削除してコストを節約しましょう。

terraform destroy -var="notification_email=your@email.com"

まとめ(Section 4)

このセクションで構築したポイントを振り返ります。

項目内容
ASL の一元管理definition.json.tpl 1ファイルで Standard・Express 両方を生成
コンソール版との一致Section 3 Step D の ASL と完全一致(Retry/Catch/ResultSelector/ResultPath すべて同一)
ログ設定の違いStandard は ERROR のみ、Express は ALL(実行履歴がないため)
実行方式の違いStandard は非同期(start-execution)、Express は同期も可能(start-sync-execution

Terraform で IaC 化することで、Standard → Express への切り替えが type の1行変更だけ で済むことが実感できたはずです。次の Section 5 では、ユースケース別の選択基準とベストプラクティスをまとめます。


Section 5: 実践Tips(Express vs Standard設計ガイド)

このSectionでは、Express/StandardをプロダクションレベルのワークフローWに活用するためのTipsを解説します。

5-1. Express移行の注意点: べき等性設計

At-least-once保証とは:
Expressは同一実行が複数回トリガーされる可能性があります。
べき等な処理(同じ処理を何度実行しても結果が同じ)でないと、重複処理による不整合が生じます。

べき等性を実現するパターン:

  1. DynamoDB条件付き書き込み:
{  "Type": "Task",  "Resource": "arn:aws:states:::dynamodb:putItem",  "Parameters": { "TableName": "Orders", "Item": {"order_id": {"S.$": "$.order_id"},"status": {"S": "processing"} }, "ConditionExpression": "attribute_not_exists(order_id)"  }}

→ 同じ order_id の重複処理を防止。すでに存在するキーへの書き込みは ConditionalCheckFailedException で弾かれます。

  1. 実行名(ExecutionName)に冪等キーを埋め込む:
aws stepfunctions start-execution \  --state-machine-arn <ARN> \  --name "order-$(echo -n ${ORDER_ID} | md5sum | cut -c1-8)" \  --input "{\"order_id\": \"${ORDER_ID}\"}"

→ 同じ ORDER_ID からは同名の実行が生成されます。Express Workflowsでは同名の実行を重複起動しようとするとエラーが返るため、重複処理を防止できます。


5-2. ログ設計: Express版でのデバッグ戦略

Express WorkflowsはStandardと異なり、コンソールから実行履歴を直接参照できません。CloudWatch Logsへの出力設定が必須です。

CloudWatch Logsの設定推奨値:

# Express State Machine(Terraform例)logging_configuration {  level= "ALL" # 開発: ALL / 本番: ERROR  include_execution_data = true  # 開発: true / 本番: false(コスト削減)  log_destination  = "${aws_cloudwatch_log_group.sf_express.arn}:*"}

ログコスト試算(月1万実行、平均3ステート、include_execution_data=true):

設定ログ量目安月額CloudWatch Logs
ALL + execution_data=true~100MB~$0.08
ERROR のみ~1MB< $0.01
ALLのみ(data=false)~10MB~$0.008

本番環境では include_execution_data = false + level = "ERROR" が推奨です。デバッグ時だけ ALL に変更しましょう。

実行ログの検索(AWS CLI):

# 特定実行のログを取得aws logs filter-log-events \  --log-group-name /aws/states/order-express \  --filter-pattern '{ $.execution_arn = "*ORD-001*" }' \  --start-time $(date -u -d "1 hour ago" +%s000) \  --region ap-northeast-1

5-3. ハイブリッド構成: StandardからExpressを呼び出す

ユースケース: メイン処理はStandard(実行履歴・監査が必要)、高スループットのサブタスクはExpress(コスト削減・スケール)。

{  "Comment": "ハイブリッド: Standard親ワークフロー → Express子ワークフロー",  "StartAt": "ValidateOrder",  "States": { "ValidateOrder": {"Type": "Task","Resource": "arn:aws:states:::states:startExecution.sync:2","Parameters": {  "StateMachineArn": "<EXPRESS_STATE_MACHINE_ARN>",  "Input.$": "$"},"ResultPath": "$.validation_result","Next": "ProcessOrder" }, "ProcessOrder": {"Type": "Task","Resource": "arn:aws:states:::lambda:invoke","Parameters": {  "FunctionName": "<LAMBDA_ARN>",  "Payload.$": "$"},"End": true }  }}

states:startExecution.sync:2 を使うことで、Express子実行の完了を親Standardが同期的に待機できます。子の実行結果は $.validation_result に格納されます。

ポイント:
– 親Standardは子Expressの実行をひとつのTaskとして扱うため、エラーハンドリング(Retry/Catch)も通常通り設定可能
– Express子実行のコストは「リクエスト数 × 実行時間」で課金されるため、短時間・高頻度処理に最適


5-4. Standard → Express 移行チェックリスト

移行前に以下を必ず確認してください:

確認項目判定基準対応方法
処理時間が5分以内かYES必須超える処理はStandard継続
べき等性が保証されているかYES必須DynamoDB条件付き書き込み等で担保
実行履歴の参照が不要か監査要件確認監査要件がある場合はStandard
エラー調査フローがあるかCloudWatch Logs設定確認Logs Insightsのクエリを事前設計
コスト試算済みか月間実行数で試算月間実行数×時間×メモリで計算

料金参考(2026年現在):
Standard: $0.025 / 1,000 状態遷移(月4,000遷移まで無料)
Express: $1.00 / 100万リクエスト + $0.00001667 / GB秒
– 月1万回実行でExpressが約$0.031 vs Standard約$0.75(約24倍の差)


Section 6: ハンズオン後の削除手順

6-1. コスト注意事項

⚠️ ハンズオン用リソースを放置すると、SNSサブスクリプション・CloudWatch Logsの保存料金が継続的に発生します。作業終了後は速やかに削除してください。

リソース月額目安備考
Lambda(3関数)無料枠内100万リクエスト/月まで無料
Step Functions無料枠内4,000遷移/月まで無料(Standard)
SNS無料枠内通知100万件/月まで無料
CloudWatch Logs~$0.76/GBExpress ALLレベルでログが溜まる

6-2. Terraformで構築した場合

すべてのリソースを一括削除:

terraform destroy -var="notification_email=your@email.com"

terraform destroy では削除されない場合があるリソース(手動削除が必要):

# CloudWatch Logsロググループaws logs delete-log-group \  --log-group-name /aws/states/sf-order-standard \  --region ap-northeast-1aws logs delete-log-group \  --log-group-name /aws/states/sf-order-express \  --region ap-northeast-1aws logs delete-log-group \  --log-group-name /aws/lambda/sf-order-confirm-inventory \  --region ap-northeast-1aws logs delete-log-group \  --log-group-name /aws/lambda/sf-order-process-payment \  --region ap-northeast-1aws logs delete-log-group \  --log-group-name /aws/lambda/sf-order-arrange-shipping \  --region ap-northeast-1

削除後の確認:

# ステートマシン一覧(空になればOK)aws stepfunctions list-state-machines --region ap-northeast-1# Lambda一覧(sf-order-* がなければOK)aws lambda list-functions --region ap-northeast-1 \  --query 'Functions[?starts_with(FunctionName, `sf-order`)].FunctionName'

6-3. コンソールで構築した場合の削除チェックリスト

以下の順番で削除することを推奨します(依存関係の逆順):

  • [ ] Step Functions ステートマシン を削除
  • order-standard-step-a
  • order-express-step-b
  • order-standard-final
  • order-express-final
  • [ ] Lambda 関数 3関数を削除
  • sf-order-confirm-inventory
  • sf-order-process-payment
  • sf-order-arrange-shipping
  • [ ] SNS トピックorder-notification)とすべてのサブスクリプションを削除
  • [ ] IAMロールsf-order-execution-role)と付随するポリシーを削除
  • [ ] CloudWatch Logs ロググループ を削除
  • /aws/states/order-standard-step-a
  • /aws/states/order-express-step-b
  • /aws/states/order-standard-final
  • /aws/states/order-express-final
  • /aws/lambda/sf-order-confirm-inventory
  • /aws/lambda/sf-order-process-payment
  • /aws/lambda/sf-order-arrange-shipping

確認方法: AWS Cost Explorer で「前日比コスト増加なし」を確認すれば削除完了の目安になります。


Section 7: まとめと次のステップ

7-1. この記事で学んだこと

本記事では、Standard WorkflowsとExpress Workflowsの違いを実際のコードと共に体験しました。

  • Standard vs Expressの本質的な違い: 実行保証(Exactly-once vs At-least-once)・履歴保存期間(90日 vs ~5分)・課金モデル(状態遷移数 vs リクエスト×時間)
  • 同期Express(StartSyncExecution)と非同期Express(StartExecution)のユースケース: APIレスポンスが必要な場合は同期、大量バッチ処理は非同期
  • 同一ASLをtype変更だけでStandard/Express両方に適用できること: コードの再利用性が高く、段階的な移行が可能
  • コスト比較: 月1万回実行でExpressが約$0.031 vs Standard約$0.75(約24倍差)
  • べき等性設計の重要性: At-least-once保証への対応(DynamoDB条件付き書き込み・実行名を冪等キーに)
  • Express版でのログ設計: CloudWatch Logs ALL設定の重要性(コンソールから実行履歴を参照できないため必須)
  • ハイブリッド構成: StandardからExpressを startExecution.sync:2 で呼び出すパターン

7-2. 次のステップ

第8弾予告: SDK Direct Integration(Lambda不要でAWSサービスを直接呼出し)

現在のアーキテクチャでは、Step FunctionsからAWSサービスを操作するためにLambda関数を仲介しています。次回は SDK Direct Integration(最適化済みの統合) を使って Lambda なしで DynamoDB・SNS・SQS などを直接呼び出す方法を解説します。

  • Lambdaレスアーキテクチャへの進化でコスト・複雑さをさらに削減
  • 対応サービス: DynamoDB、S3、SQS、SNS、ECS、Bedrock、その他200以上

7-3. シリーズリンク


本記事は「AWS ハンズオン TechBlog」Step Functions シリーズの第7回です。

シリーズ一覧:
– 第1回: AWS Step Functions 入門 — コンソールとTerraformで学ぶハンズオン
– 第2回: ECS × Step Functions 入門 — CSVバッチをFargateタスクでジョブ化するハンズオン
– 第3回: Step Functions エラーハンドリング完全ガイド — 注文処理パイプラインで学ぶRetry/Catch/Timeout
– 第4回: Step Functions 入出力データフロー制御完全ガイド — 5つのフィルタでペイロードを最適化するハンズオン
– 第5回: Step Functions Callbackパターン完全ガイド — .waitForTaskTokenで実現する経費承認ワークフロー
– 第6回: Step Functions Distributed Map完全ガイド — S3大規模並列処理をハンズオンで習得
– 第7回: 本記事(Express vs Standard完全ガイド)


7-4. 参考リンク

  • AWS Step Functions Developer Guide — Standard vs. Express Workflows
  • Express Workflows の制限と料金
  • StartSyncExecution API Reference
  • CloudWatch Logs を使った Express Workflows のモニタリング
最新情報をチェックしよう!