Lambda、便利ですよね。
私は好きですよ、Lambda。ランタイム更新に追われることを除けば――――
こんにちは。コーポレートエンジニアリング部の礒です。
AWS の操作を自動化したいときに Lambda 関数の作成を検討することがあると思いますが、それにはランタイム更新という保守作業がついて回ります。
こんなときに Lambda を書かずに保守コストを下げられる仕組み、Step Functions の SDK 統合を利用した AWS 操作をやってみます。
この記事は、以下のような状況を想定して書いてみました。
- 特定の AWS アカウントの AWS Organizations(以下「Organizations」)配下アカウント作成を自動化したい。結果の整形など複雑な操作はなし。
- 社内ワークフロー(外部サービス)に上記アカウント作成処理を組み込みたい
本記事の登場サービス
| 略称 | 正式名称 |
|---|---|
| Lambda | AWS Lambda |
| Step Functions | AWS Step Functions |
| API Gateway | Amazon API Gateway |
| CloudFormation | AWS CloudFormation |
Step Functions SDK 統合 + API Gateway という構成
Step Functions には、Lambda を介さずに AWS サービスを直接呼び出せる「SDK 統合」という機能があります。
SDK 統合は 200 以上のサービスが対象になっています。ほとんどの AWS サービスが操作できますね。
この機能と API Gateway を組み合わせると、外部サービスからの HTTP リクエストで AWS リソースを操作できます。
コードを書かなくてよいため、保守コストが下がる上に設定できる人が限られにくくなるのも利点です。
外部サービス → API Gateway → Step Functions → AWS リソース
外部サービスは API Gateway を呼び出すだけです。API の呼び出し方は2段階で、まず実行を開始し、その後ポーリングで結果を確認します。
- 起動:
POST /accounts→ ステートマシンを起動し、executionArnを返す - 確認:
GET /executions/{executionArn}→ 実行ステータスを返す(RUNNING/SUCCEEDED/FAILED)
私はこの構成で、Organizations 配下へのアカウント追加を自動化しました。
設計判断のポイント
コールバックパターンではなくポーリングにした理由
Step Functions には waitForTaskToken という、外部からの完了通知を待つコールバックパターンがあります。
ポーリングより洗練された仕組みですが、今回は使えませんでした。
organizations:createAccount API には、タスクトークンを渡すためのパラメータフィールドが存在しないからです。(API リファレンス)
リトライと冪等性
ステートマシンが失敗した場合の再実行は呼び出し側(外部サービス)の判断に任せます。
今回は外部サービスが自動でリトライすることはない前提として、二重にアカウントが作成される心配はしていません。
自動リトライする外部サービスと組み合わせる場合は、別途の冪等性担保が必要になりそうです。
実装手順
★ 操作の特性上、Organizations 管理アカウントで実施します。
ステートマシンの定義
以下のような Standard Workflow のステートマシンを作成します。

- CreateAccount:
organizations:createAccountを呼び出し、アカウント作成をリクエストする - WaitForCompletion:20 秒待機する
- CheckStatus:
describeCreateAccountStatusで作成状況を確認する - IsComplete:
SUCCEEDEDなら成功、FAILEDなら失敗。それ以外(作成中)は 2 に戻ってポーリングを繰り返す
ASL(ステートマシンの定義 JSON)は AI に生成してもらいました。
{ "Comment": "Create AWS Account via Organizations", "StartAt": "CreateAccount", "States": { "CreateAccount": { "Type": "Task", "Resource": "arn:aws:states:::aws-sdk:organizations:createAccount", "Parameters": { "AccountName.$": "$.accountName", "Email.$": "$.email" }, "ResultPath": "$.createResult", "Next": "WaitForCompletion" }, "WaitForCompletion": { "Type": "Wait", "Seconds": 20, "Next": "CheckStatus" }, "CheckStatus": { "Type": "Task", "Resource": "arn:aws:states:::aws-sdk:organizations:describeCreateAccountStatus", "Parameters": { "CreateAccountRequestId.$": "$.createResult.CreateAccountStatus.Id" }, "ResultPath": "$.statusResult", "Next": "IsComplete" }, "IsComplete": { "Type": "Choice", "Choices": [ { "Variable": "$.statusResult.CreateAccountStatus.State", "StringEquals": "SUCCEEDED", "Next": "Success" }, { "Variable": "$.statusResult.CreateAccountStatus.State", "StringEquals": "FAILED", "Next": "Fail" } ], "Default": "WaitForCompletion" }, "Success": { "Type": "Succeed" }, "Fail": { "Type": "Fail", "Error": "AccountCreationFailed", "Cause": "Organizations CreateAccount returned FAILED state" } } }
API Gateway との接続
/accounts エンドポイント(起動)
API Gateway で REST API を作成し、 AWS サービス統合で以下のように設定します。
(HTTP API では API キーによる認証方法が設定できないため、今回は REST API を選択しました。)
| 設定項目 | 値 |
|---|---|
| Integration type | AWS Service |
| AWS Service | Step Functions |
| HTTP Method | POST |
| Action | StartExecution |
| Execution Role | API Gateway 用 IAM ロール |
リクエストマッピングテンプレート:リクエストボディをそのまま input に格納し、起動するステートマシンの ARN を指定します。
{ "input": "$util.escapeJavaScript($input.body)", "stateMachineArn": "arn:aws:states:REGION:ACCOUNT_ID:stateMachine:YOUR_STATE_MACHINE" }
レスポンスマッピングテンプレート:Step Functions からのレスポンスのうち、executionArn と startDate だけを抜き出して返します。
{ "executionArn": $input.json('$.executionArn'), "startDate": $input.json('$.startDate') }
/executions/{executionArn} エンドポイント(実行ステータス確認)
| 設定項目 | 値 |
|---|---|
| Integration type | AWS Service |
| AWS Service | Step Functions |
| HTTP Method | POST |
| Action | DescribeExecution |
DescribeExecution は GET 的な操作ですが、Step Functions の API は常に HTTP POST で呼ぶ仕様なのでご注意ください。
リクエストマッピングテンプレート:パスパラメータの executionArn を、DescribeExecution が受け取れる形式に変換します。
{ "executionArn": "$util.urlDecode($input.params('executionArn'))" }
executionArn はコロンを含むため、URL エンコードされてくる前提で urlDecode を挟んでいます。
レスポンスマッピングテンプレート:実行が成功したときと失敗したときで返ってくるキーに差があるため、分岐処理を入れています。
#set($body = $input.path('$')) { "executionArn": $input.json('$.executionArn'), "status": $input.json('$.status'), "startDate": $input.json('$.startDate'), "stopDate": $input.json('$.stopDate'), #if($body.status == "FAILED") "error": $input.json('$.error'), "cause": $input.json('$.cause'), #else "output": $input.json('$.output') #end }
output には実行が成功した場合の最終出力が JSON 文字列として入ります。RUNNING 中は null です。
API キーによる認証設定
両エンドポイントとも API キーを必須にし、使用量プランに紐付けています。
IAM ロールの設定
Step Functions 用の実行ロールに以下のポリシーをアタッチします。 アカウント作成を実行する権限と、作成リクエストしたアカウントの作成状況を参照する権限を付与します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "organizations:CreateAccount", "organizations:DescribeCreateAccountStatus" ], "Resource": "*" } ] }
API Gateway 用には states:StartExecution と states:DescribeExecution を許可するロールを別途作成します。
それぞれステートマシンを実行する権限と、特定の実行のステータスを参照する権限です。
外部システムからのリクエスト
起動(1回目)
POST /accounts { "accountName": "MyNewAccount", "email": "admin@example.com" }
レスポンス:
{ "executionArn": "arn:aws:states:...:execution:...", "startDate": "2026-06-10T..." }
ステータス確認(2回目)
GET /executions/{executionArn}
ポーリングしながら RUNNING → SUCCEEDED を確認します。
レスポンス:
{ "status": "RUNNING", "output": null } { "status": "RUNNING", "output": null } { "status": "SUCCEEDED", "output": "{\"accountId\": \"...\"}" }
動作確認
テスト開始前の状態:

ステートマシン実行成功・アカウントが作成された:

エラー時の挙動も確認しました(意図的に権限不足のロールを指定)。
ステートマシンが失敗し、外部システムには失敗ステータスが返ることを確認しています。

振り返り
今回、AI に相談しながら設計を固めた後に「CloudFormation テンプレートを作って」と頼むと YAML テンプレートを生成してくれました。
初めて使う機能でも AI に協力してもらうと調べ物の時間が少なく済みますね。
簡単にまとめます。
- Lambda を書かずに AWS リソースを操作したいなら、Step Functions SDK 統合 + API Gateway という選択肢がある
- ノーコードで実装できるため Lambda と比べて保守コストが低く、作れる人を選ばない
- 自動リトライする外部サービスと組み合わせる場合は、冪等性の担保のため追加の対策が必要
礒 瑞希
コーポレートエンジニアリング部で社内業務の効率化などしています。
趣味は編み物とお絵かき。