はじめに
「複数のLambda関数をオーケストレーションするワークフローを、コードで管理したい」という要件に、AWS SAM(Serverless Application Model) は最適な選択肢のひとつです。
この記事では、AWS SAM を使って、受注処理ワークフロー(注文検証 → 在庫確認・支払い並列処理 → 注文確定)をゼロから構築するハンズオンを紹介します。
入力: {"order_id": "ORD-001", "item_id": "item-001", "quantity": 2}
↓
[ValidateOrder] 注文検証(Retry + Catch)
↓ 成功
[ProcessInParallel] 並列処理(Catch)
├─ [CheckInventory] 在庫確認
└─ [ProcessPayment] 支払い処理
↓ 成功(結果リスト: [inventory_result, payment_result])
[ConfirmOrder] 注文確定
↓
終了(成功)
[HandleError] エラー処理(Catch 先)
↓
終了(エラー通知済み)このハンズオンで体験できること:
AWS::Serverless::StateMachineを使った Step Functions の SAM 定義DefinitionSubstitutionsで ASL 内の Lambda ARN を 自動差し込みする仕組みLambdaInvokePolicyによる1行での権限付与(コンソール版ではIAMロールを自動生成)- CLI でのテスト実行 + コンソールの実行グラフで視覚的に確認
このハンズオンの特徴:
sam build+sam deployの 2コマンドで全リソースをデプロイ- Step Functions(ステートマシン)+ Lambda × 5 + IAM ロール × 6 を
template.yaml1ファイルで管理 sam deleteで全リソースを一括削除(コンソール版では5つのLambda・ステートマシン・IAMロールを個別に削除)
SAM vs コンソール:どれだけ違うか
| 比較項目 | SAM(コード) | コンソール(手動) |
|---|---|---|
| Lambda ARN の参照 | ${ValidateFunctionArn} で自動差し込み | 5関数のARNを手動でコピー・貼り付け |
| IAM ロール | LambdaInvokePolicy × 5 で自動生成 | 「新しいロールを作成」で自動生成(1回限り) |
| ASL 定義の管理 | template.yaml で Git 管理 | コンソールのエディタのみ |
| 削除 | sam delete 1コマンド | Lambda × 5 / ステートマシン / IAMロール / ロググループを個別に削除 |
| 再現性 | チームで同じ環境を即座に再現できる | 手順書が必要 |
キーワード解説
| 用語 | 意味 |
|---|---|
| ステートマシン | ワークフローの全体定義。状態(State)の集合と遷移ルールをもつ |
| Task ステート | Lambda などの外部リソースを呼び出す状態 |
| Parallel ステート | 複数のブランチを同時に実行する状態。両ブランチの結果がリストで次のステートに渡る |
| Retry | エラー発生時に同じステートを自動で再実行するルール |
| Catch | リトライ後もエラーが続く場合に別のステートへ遷移するルール |
| ResultPath | Catch 時にエラー情報をどのフィールドに格納するかの指定("$.error" → 元の入力の error キーにマージ) |
| ASL | Amazon States Language。ステートマシン定義の JSON 形式 |
| DefinitionSubstitutions | ASL 内の ${変数名} を実際の値(ARN など)に置換するSAMの仕組み |
前提条件
| ツール | 確認コマンド | 最低バージョン目安 |
|---|---|---|
| AWS CLI v2 | aws --version | 2.x |
| AWS SAM CLI | sam --version | 1.x |
| Python 3.12 | python --version | 3.12 |
| Git | git --version | - |
| VSCode | - | 最新版推奨 |
aws sts get-caller-identityアカウントIDが表示されれば認証設定済みです。
使用するAWSサービス
| サービス | 役割 | 料金 |
|---|---|---|
| Step Functions | ワークフロー(ステートマシン)の管理・実行 | 月4,000回の状態遷移まで無料(標準タイプ) |
| Lambda | 各ステートで実行されるビジネスロジック(5関数) | 月100万リクエスト・400,000 GB-秒まで無料 |
| IAM | Lambda・Step Functionsの実行権限管理 | 無料 |
| S3 | SAM デプロイパッケージ置き場 | 少量のため実質無料 |
| CloudWatch Logs | Lambdaの実行ログ | 月5GBまで無料 |
Step 1: プロジェクトフォルダを開く
cd C:\my-aws\aws-learning-projects\step-functions-lambdaフォルダ構造
step-functions-lambda/
├── template.yaml # SAMテンプレート(Step Functions + Lambda 定義)
├── samconfig.toml # デプロイ設定(gitignore 対象・毎回手動作成が必要)
├── docs/
│ ├── 1_console.md # AWSコンソール版手順
│ └── 2_sam.md # SAM版手順
└── src/
├── validate.py # 注文検証 Lambda
├── inventory.py # 在庫確認 Lambda
├── payment.py # 支払い処理 Lambda
├── confirm.py # 注文確定 Lambda
└── error_handler.py # エラー処理 LambdaStep 2: SAMテンプレートの確認(template.yaml)
template.yaml は全 AWSリソースの設計図です。ポイントを確認しておきます。
Lambda 関数の定義
Globals:
Function:
Runtime: python3.12
Timeout: 10
MemorySize: 128
Resources:
ValidateFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/ # src/ 配下の全 .py を1つのパッケージとしてデプロイ
Handler: validate.lambda_handler
InventoryFunction: # 在庫確認(Parallel ブランチ 1)
PaymentFunction: # 支払い処理(Parallel ブランチ 2)
ConfirmFunction: # 注文確定
ErrorHandlerFunction: # エラー処理(Catch 先)ポイント: 5つの Lambda 関数が全て CodeUri: src/ を共有しています。SAM は src/ フォルダを1つの ZIP パッケージとして S3 にアップロードし、各関数は Handler でどのファイル・関数を使うかを指定します。
ステートマシンの定義
OrderWorkflow:
Type: AWS::Serverless::StateMachine
Properties:
Type: STANDARD
Definition:
StartAt: ValidateOrder
States:
ValidateOrder:
Type: Task
Resource: "${ValidateFunctionArn}" # DefinitionSubstitutions で実際の ARN に置換
Next: ProcessInParallel
Retry:
- ErrorEquals: [States.ALL]
IntervalSeconds: 2
MaxAttempts: 1
BackoffRate: 2.0
Catch:
- ErrorEquals: [States.ALL]
Next: HandleError
ResultPath: "$.error"
ProcessInParallel:
Type: Parallel
Branches:
- StartAt: CheckInventory
States:
CheckInventory:
Type: Task
Resource: "${InventoryFunctionArn}"
End: true
- StartAt: ProcessPayment
States:
ProcessPayment:
Type: Task
Resource: "${PaymentFunctionArn}"
End: true
Next: ConfirmOrder
Catch:
- ErrorEquals: [States.ALL]
Next: HandleError
ResultPath: "$.error"
ConfirmOrder:
Type: Task
Resource: "${ConfirmFunctionArn}"
End: true
HandleError:
Type: Task
Resource: "${ErrorHandlerFunctionArn}"
End: true
DefinitionSubstitutions: # ${変数名} を実際の ARN に自動置換
ValidateFunctionArn: !GetAtt ValidateFunction.Arn
InventoryFunctionArn: !GetAtt InventoryFunction.Arn
PaymentFunctionArn: !GetAtt PaymentFunction.Arn
ConfirmFunctionArn: !GetAtt ConfirmFunction.Arn
ErrorHandlerFunctionArn: !GetAtt ErrorHandlerFunction.Arn
Policies: # ステートマシンが Lambda を呼び出す IAM 権限
- LambdaInvokePolicy:
FunctionName: !Ref ValidateFunction
- LambdaInvokePolicy:
FunctionName: !Ref InventoryFunction
- LambdaInvokePolicy:
FunctionName: !Ref PaymentFunction
- LambdaInvokePolicy:
FunctionName: !Ref ConfirmFunction
- LambdaInvokePolicy:
FunctionName: !Ref ErrorHandlerFunctiontemplate.yaml のポイント解説
| ポイント | 説明 |
|---|---|
DefinitionSubstitutions | ASL 内の ${ValidateFunctionArn} などを !GetAtt ValidateFunction.Arn で取得した実際の ARN に置換する。コンソール版で5回ARNをコピーする手間がなくなる |
LambdaInvokePolicy × 5 | SAM 組み込みポリシー。lambda:InvokeFunction 権限を1行で付与。コンソール版ではIAMロールの自動生成で対応 |
Type: STANDARD | 監査ログ・正確に1回実行・長時間実行対応。コンソール版では「標準」を選択する部分に対応 |
ResultPath: "$.error" | エラー情報を元の入力の $.error フィールドにマージして HandleError へ渡す |
CodeUri: src/(共有) | 5関数で1つの src/ パッケージを共有。コードを変更すると全関数に反映される |
Step 3: Lambda コードの確認(src/)
各ファイルの役割を確認しておきます。
| ファイル | ステート | 特記事項 |
|---|---|---|
validate.py | ValidateOrder | order_id / item_id / quantity を検証。欠けていると ValueError |
inventory.py | CheckInventory | item-999 で ValueError(在庫切れテスト用) |
payment.py | ProcessPayment | fail_payment=True で RuntimeError(支払い失敗テスト用) |
confirm.py | ConfirmOrder | event[0](在庫確認結果)と event[1](支払い結果)を受け取る。Parallel 後はリスト形式になる |
error_handler.py | HandleError | event["error"] にエラー情報、それ以外に元の入力が格納されている |
各 Python コードの全文はコンソール版記事を参照してください。
SAM版・コンソール版で使用する Lambda コードは共通です。コンソール版では各関数ごとにコードを貼り付ける手順とあわせて全文を掲載しています。→ AWSコンソールだけでStep Functions + Lambdaワークフローを構築する手順【SAM版との比較付き / 新UI対応】
confirm.pyのevent[0]/event[1]について:
Parallel ステートが成功すると、各ブランチの出力が**配列(リスト)**にまとめられて次のステートに渡されます。このため ConfirmFunction はevent[0]で在庫確認結果、event[1]で支払い結果にアクセスしています。この仕様を知らないとlist indices must be integersエラーで詰まります。
Step 4: samconfig.toml を作成する
samconfig.toml は .gitignore で管理外のため、毎回手動で作成する必要があります。
step-functions-lambda/samconfig.toml を新規作成して以下を貼り付けます。
version = 0.1
[default.deploy.parameters]
stack_name = "step-functions-lambda-stack"
resolve_s3 = true
s3_prefix = "step-functions-lambda-stack"
region = "ap-northeast-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
disable_rollback = true
image_repositories = []
[default.global.parameters]
region = "ap-northeast-1"
samconfig.tomlの置き場所:step-functions-lambda/フォルダの直下に置く必要があります。
Step 5: sam build(ビルド)
cd C:\my-aws\aws-learning-projects\step-functions-lambda
sam build成功すると以下のように表示されます。
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
sam buildが行っていること:
src/フォルダを ZIP パッケージ化して.aws-sam/build/に配置template.yamlを.aws-sam/build/template.yamlにコピー- Lambda デプロイの準備を整える
Step 6: sam deploy(デプロイ)
sam deploy変更内容が表示されて確認を求められます。
Deploy this changeset? [y/N]: yy を入力して進めます。数分でデプロイが完了します。
デプロイ完了の確認
ターミナルに Outputs が表示されます。
Outputs
----------------------------------------------------------------------
Key OrderWorkflowArn
Value arn:aws:states:ap-northeast-1:123456789012:stateMachine:step-functions-lambda-stack-order-workflow
Key OrderWorkflowName
Value step-functions-lambda-stack-order-workflow
----------------------------------------------------------------------OrderWorkflowArn を控えておきます。(テスト実行コマンドで使用)
デプロイされるリソース一覧
Step Functions ステートマシン × 1 : step-functions-lambda-stack-order-workflow
Lambda 関数 × 5 : step-functions-lambda-stack-ValidateFunction-XXXX 他
IAM ロール × 6 : ステートマシン用 × 1 / Lambda 用 × 5
S3 : SAM デプロイパッケージ
CloudWatch Logs : Lambda 自動生成ログ × 5Step 7: 動作テスト
事前準備: ARN を変数に設定する
set SM_ARN=arn:aws:states:ap-northeast-1:123456789012:stateMachine:step-functions-lambda-stack-order-workflow
123456789012を自分のアカウントIDに置き換えてください(Outputs の値をそのままコピーすればよいです)。
テスト1: 正常処理
aws stepfunctions start-execution ^
--state-machine-arn %SM_ARN% ^
--name "test-normal-1" ^
--input "{\"order_id\": \"ORD-001\", \"item_id\": \"item-001\", \"quantity\": 2}" ^
--region ap-northeast-1レスポンス例:
{
"executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:...:test-normal-1",
"startDate": "2026-03-01T10:00:00.000000+09:00"
}実行結果を確認(数秒後):
aws stepfunctions describe-execution ^
--execution-arn "上記の executionArn" ^
--region ap-northeast-1"status": "SUCCEEDED" と以下のような出力が表示されます。
{
"order_id": "ORD-001",
"item_id": "item-001",
"quantity": 2,
"amount": 2000,
"confirmed_at": "2026-03-01T01:00:00.000000+00:00",
"status": "confirmed"
}AWSコンソールで実行グラフを確認(任意):
Step Functions → ステートマシン → step-functions-lambda-stack-order-workflow → 実行一覧 → test-normal-1 をクリックすると、全ステートが緑色になり ProcessInParallel の2ブランチが同時に実行されたことを視覚的に確認できます。
テスト2: 在庫切れエラー(ProcessInParallel Catch の確認)
aws stepfunctions start-execution ^
--state-machine-arn %SM_ARN% ^
--name "test-inventory-error" ^
--input "{\"order_id\": \"ORD-002\", \"item_id\": \"item-999\", \"quantity\": 1}" ^
--region ap-northeast-1期待する動作:
ValidateOrder→ 成功ProcessInParallel→CheckInventoryがValueError(在庫切れ)を発生させるProcessInParallelの Catch が動作 →HandleErrorへ遷移- 実行ステータス:
SUCCEEDED(HandleError が正常完了)
出力例:
{
"status": "error",
"error_type": "ValueError",
"error_cause": "在庫不足: item_id=item-999",
"original_input": {
"order_id": "ORD-002",
"item_id": "item-999",
"quantity": 1,
"validated": true
}
}実行ステータスが
SUCCEEDEDになる理由:HandleError関数が正常終了(return result)しているため、ステートマシン全体としては「成功」扱いになります。エラーが「処理されずに残った」場合はFAILEDになります。
テスト3: 支払い失敗エラー(ProcessInParallel Catch の確認)
aws stepfunctions start-execution ^
--state-machine-arn %SM_ARN% ^
--name "test-payment-error" ^
--input "{\"order_id\": \"ORD-003\", \"item_id\": \"item-001\", \"quantity\": 2, \"fail_payment\": true}" ^
--region ap-northeast-1ProcessPayment が RuntimeError を発生させ、同様に HandleError へ遷移します。
テスト4: 検証エラー(ValidateOrder Retry + Catch の確認)
aws stepfunctions start-execution ^
--state-machine-arn %SM_ARN% ^
--name "test-validate-error" ^
--input "{\"item_id\": \"item-001\", \"quantity\": 2}" ^
--region ap-northeast-1order_id を省略しているため ValidateOrder が ValueError を発生させます。
Retry の確認(AWSコンソール):
Step Functions コンソールの実行グラフで ValidateOrder を選択 → 「イベント」タブ で以下の順序を確認します:
TaskStateEntered → TaskScheduled → TaskStarted → TaskFailed
→ TaskScheduled(リトライ1回目)→ TaskStarted → TaskFailed
→ TaskStateExited(Catch 発動)→ HandleError へValidateFunction が合計2回呼ばれてから HandleError へ遷移することが分かります。
Step 8: AWSコンソールで確認(任意)
SAMでデプロイしたリソースはコンソールで確認できます。
- Step Functions: ステートマシン一覧 →
step-functions-lambda-stack-order-workflow→ 各実行のグラフを確認 - Lambda: Lambda → 関数 →
step-functions-lambda-stack-ValidateFunction-XXXX→ 「モニタリング」→ 呼び出し回数を確認 - CloudWatch Logs: Lambda → 対象関数 → 「モニタリング」タブ → 「CloudWatch Logs を表示」
Step 9: リソースの削除
課金を止めるために、ハンズオン完了後は必ずリソースを削除してください。
sam delete --stack-name step-functions-lambda-stack --region ap-northeast-1対話式で確認が入ります。両方 y で進めます。
Are you sure you want to delete the stack step-functions-lambda-stack? [y/N]: y
Are you sure you want to delete the folder step-functions-lambda-stack in S3? [y/N]: y削除完了の確認
aws cloudformation describe-stacks --stack-name step-functions-lambda-stack --region ap-northeast-1An error occurred (ValidationError): Stack with id step-functions-lambda-stack does not existこのメッセージが表示されれば削除完了です。
sam delete で削除されるリソース一覧
| リソース | 数 |
|---|---|
| Step Functions ステートマシン | × 1 |
| Lambda 関数 | × 5 |
| IAM ロール(ステートマシン用・Lambda 用) | × 6 |
| Lambda デプロイパッケージ(S3) | × 1 |
| CloudWatch Logs ロググループ | × 5 |
コンソール版との大きな違い(SAMのメリット):
コンソール版では Lambda × 5 / ステートマシン / IAM ロール × 6 / ロググループを個別に削除する必要があります。
SAMではsam delete1コマンドで全リソースを一括削除できます。
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
sam build が失敗する | src/ フォルダ内のファイルが存在しない | src/ 配下に5つの .py ファイルがあるか確認 |
sam deploy が Missing --stack-name エラー | samconfig.toml がない、または場所が違う | step-functions-lambda/ 直下に samconfig.toml を作成する |
実行が即座に FAILED になる | Lambda ARN が間違っている | sam deploy のエラーログを確認 |
テストで executionArn が同じ名前で失敗する | --name が重複している | --name のサフィックスを変えて再実行する(例: test-normal-2) |
describe-execution の status が RUNNING のまま | Lambda がまだ実行中 | 数秒〜数十秒待ってから再実行する |
ConfirmFunction でエラー: list index out of range | Parallel ブランチが正常完了していない | ProcessInParallel の後にのみ呼ばれる関数かを確認する |
まとめ
今回のハンズオンで実現したこと:
| 確認項目 | 内容 |
|---|---|
| SAM でのステートマシン定義 | AWS::Serverless::StateMachine + DefinitionSubstitutions で ARN を自動差し込み |
| Lambda × 5 の一括デプロイ | CodeUri: src/ 共有パッケージ + Handler で各関数を定義 |
| Retry + Catch | ValidateOrder でリトライ → 失敗 → HandleError へ遷移 |
| Parallel + Catch | 在庫確認と支払い処理を同時実行 → 一方が失敗したら HandleError へ |
| 一括削除 | sam delete で全リソースをクリーンアップ |
SAMのメリットを実感できたポイント
DefinitionSubstitutionsにより Lambda ARN のコピー貼り付けが不要LambdaInvokePolicy× 5 で IAM 権限設定が1行ずつで完結sam deleteでステートマシン・Lambda 5つ・IAM ロール 6つを一括削除
コンソール版と比較してみる
SAMが裏で何をやっているか、同じ構成をAWSコンソールのみで構築する手順をまとめました。Workflow Studioの新UI(3択モーダル・「{ } コード」タブ)での操作方法や、コンソール版ならではのつまずきポイントを解説しています。
コメント