Lambda MicroVMs に対応した Step Functions の SDK 統合を試してみた
はじめに
2026 年 9 月 15 日、AWS は、AWS Step Functions が新しい AWS サービスや機能向けの AWS SDK 統合をリリース後数週間以内に自動追加するようになったと発表しました。
今回の告知では、新しい AWS サービスとして AWS Lambda MicroVMs、AWS Lambda Core などが紹介されています。
Step Functions の SDK 統合から、6月にリリースされた Lambda MicroVMs の操作を試してみました。
この記事で作るもの
トークン認証が必要なウェブサイトを起動し、3 分間だけアクセスを受け付けた後、自動停止する仕組みを作りました。
全体の流れ
事前に用意するのは、MicroVM Image 1 つ、IAM ロール 2 つ(MicroVM の実行ロールとステートマシンの実行ロール)、イメージの素材を置く S3 バケット 1 つです。これらに加えて、ステートマシンを 1 本作ります。
ステートマシンを実行するたびに、MicroVM の起動、認証トークンの発行、3 分の待機、MicroVM の停止が行われます。この 4 つの処理を、SDK 統合の Task と Wait ステートで構成します。Step Functions が制御するのは MicroVM の寿命、アクセス用 URL の取得、トークンの発行であり、MicroVM の処理結果は受け取りません。
アプリと MicroVM Image を用意する
検証環境
- リージョン:
ap-northeast-1 - AWS CLI:
aws-cli/2.36.46 - MicroVM Image のベースイメージ:
al2023-1(version1)
サイトには、リクエストごとに現在時刻を返し、アクセスログを標準出力へ出力する HTTP サーバーを使います。現在時刻が変化することで、応答が実際に MicroVM 上で生成されていることを確認できます。
app.py
import datetime
import os
import socket
from http.server import BaseHTTPRequestHandler, HTTPServer
LISTEN_PORT = 8080
BOOT_AT = datetime.datetime.now(datetime.timezone.utc)
def log(line):
now = datetime.datetime.now(datetime.timezone.utc).isoformat()
print(f"[{now}] {line}", flush=True)
class TimeHandler(BaseHTTPRequestHandler):
server_version = "microvm-clock/1.0"
def do_GET(self):
now = datetime.datetime.now(datetime.timezone.utc)
body = (
"MicroVM clock\n"
f"now_utc: {now.isoformat()}\n"
f"epoch: {now.timestamp():.3f}\n"
f"booted_utc: {BOOT_AT.isoformat()}\n"
f"uptime_seconds: {(now - BOOT_AT).total_seconds():.1f}\n"
f"hostname: {socket.gethostname()}\n"
).encode()
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
log(
f"ACCESS port={self.server.server_address[1]} "
f"client={self.client_address[0]} "
f'"{self.requestline}" 200 {len(body)} '
f'ua="{self.headers.get("User-Agent", "-")}" '
f'host="{self.headers.get("Host", "-")}" '
f'xff="{self.headers.get("X-Forwarded-For", "-")}"'
)
def log_message(self, fmt, *args):
pass # アクセスログは do_GET 側で構造化して出力する
def serve(port):
try:
httpd = HTTPServer(("0.0.0.0", port), TimeHandler)
except Exception as e:
log(f"LISTEN_FAILED port={port} error={e}")
return
log(f"LISTENING port={port}")
httpd.serve_forever()
if __name__ == "__main__":
log(f"BOOT env_port={os.environ.get('PORT', '-')}")
serve(LISTEN_PORT)
MicroVM の HTTPS エンドポイントは既定で MicroVM 内のポート 8080 へルーティングされるため、アプリも 8080 で待ち受けます。
Dockerfile
FROM public.ecr.aws/amazonlinux/amazonlinux:2023
RUN dnf install -y python3 && dnf clean all
COPY app.py /app/app.py
WORKDIR /app
EXPOSE 8080
CMD ["python3", "-u", "/app/app.py"]
Dockerfile と app.py を同じディレクトリに置き、zip にまとめて S3 へアップロードします。
zip -j image.zip Dockerfile app.py && aws s3 cp image.zip s3://<BUCKET>/
その zip を指定してイメージを作成します。
aws lambda-microvms create-microvm-image \
--name microvm-3min-site \
--base-image-arn arn:aws:lambda:ap-northeast-1:aws:microvm-image:al2023-1 \
--base-image-version 1 \
--build-role-arn arn:aws:iam::<ACCOUNT_ID>:role/microvm-3min-site-execution \
--code-artifact uri=s3://<BUCKET>/image.zip \
--egress-network-connectors arn:aws:lambda:ap-northeast-1:aws:network-connector:aws-network-connector:INTERNET_EGRESS \
--logging '{"cloudWatch":{"logGroup":"/aws/lambda-microvms/microvm-3min-site","logStream":"build"}}' \
--resources '[{"minimumMemoryInMiB":2048}]'
IAM ロール
MicroVM の実行ロールと、Step Functions の実行ロールを 1 本ずつ用意します。信頼ポリシーと権限は以下のとおりです。
IAM ロールとポリシーの全文
ロール 1: microvm-3min-site-execution(MicroVM の実行ロール)
Trust Policy
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
Inline Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:ap-northeast-1:<ACCOUNT_ID>:log-group:/aws/lambda-microvms/*"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::<BUCKET>/*"
}
]
}
ロール 2: microvm-3min-site-sfn(ステートマシンの実行ロール)
Trust Policy
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "states.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
Inline Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:RunMicrovm",
"lambda:TerminateMicrovm",
"lambda:CreateMicrovmAuthToken"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::<ACCOUNT_ID>:role/microvm-3min-site-execution"
}
]
}
ステートマシン
ステートマシンのクエリ言語には JSONata を採用します。SDK 統合で RunMicrovm、CreateMicrovmAuthToken、TerminateMicrovm を呼び出し、JSONata で各応答から MicroVM ID やエンドポイントなどの値を取り出します。定義は sfn-3min-site.asl.json として保存します。
JSONata と SDK 統合のポイント
SDK 統合のリソース ARN は arn:aws:states:::aws-sdk:<サービス名小文字>:<apiAction> の形です。JSONata モードでは Arguments / Assign / Output を使います。
RunMicrovm の応答から MicroVM ID とエンドポイントを取り出し、後続のステートで再利用します。
"Assign": {
"microvmId": "{% $states.result.MicrovmId %}",
"endpoint": "{% $states.result.Endpoint %}"
}
RunMicrovm の応答時に JSONata で 3 分後の stopAt を計算し、Wait と認証トークンの有効期限に使います。
"Assign": {
"stopAt": "{% $fromMillis($toMillis($now()) + 180000) %}"
}
"Timestamp": "{% $stopAt %}"
認証トークンのキー X-aws-proxy-auth にはハイフンが含まれるため、JSONata ではバックティックで囲んで参照します。
"Assign": {
"authToken": "{% $states.result.AuthToken.`X-aws-proxy-auth` %}"
}
ログストリーム名に実行名を含めることで、MicroVM のログを実行単位で分けます。
"LogStream": "{% 'sfn-' & $states.context.Execution.Name %}"
sfn-3min-site.asl.json(全文)
{
"Comment": "MicroVM を起動して 3 分だけ公開し、3 分後に停止する (JSONata / SDK 統合のみ)",
"QueryLanguage": "JSONata",
"StartAt": "RunMicrovm",
"States": {
"RunMicrovm": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:lambdamicrovms:runMicrovm",
"Arguments": {
"ImageIdentifier": "{% $states.input.imageArn %}",
"ImageVersion": "{% $states.input.imageVersion %}",
"ExecutionRoleArn": "{% $states.input.executionRoleArn %}",
"MaximumDurationInSeconds": 900,
"Logging": {
"CloudWatch": {
"LogGroup": "/aws/lambda-microvms/microvm-3min-site",
"LogStream": "{% 'sfn-' & $states.context.Execution.Name %}"
}
}
},
"Assign": {
"microvmId": "{% $states.result.MicrovmId %}",
"endpoint": "{% $states.result.Endpoint %}",
"stopAt": "{% $fromMillis($toMillis($now()) + 180000) %}"
},
"Next": "CreateAuthToken"
},
"CreateAuthToken": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:lambdamicrovms:createMicrovmAuthToken",
"Arguments": {
"MicrovmIdentifier": "{% $microvmId %}",
"ExpirationInMinutes": 3,
"AllowedPorts": [
{
"Port": 8080
}
]
},
"Assign": {
"siteUrl": "{% 'https://' & $endpoint %}",
"authToken": "{% $states.result.AuthToken.`X-aws-proxy-auth` %}"
},
"Next": "ServeFor3Minutes"
},
"ServeFor3Minutes": {
"Type": "Wait",
"Timestamp": "{% $stopAt %}",
"Next": "TerminateMicrovm"
},
"TerminateMicrovm": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:lambdamicrovms:terminateMicrovm",
"Arguments": {
"MicrovmIdentifier": "{% $microvmId %}"
},
"Next": "Done"
},
"Done": {
"Type": "Pass",
"Output": {
"microvmId": "{% $microvmId %}",
"url": "{% $siteUrl %}",
"servedUntil": "{% $stopAt %}"
},
"End": true
}
}
}
aws stepfunctions create-state-machine --name microvm-3min-site --definition file://sfn-3min-site.asl.json --role-arn <SFN_ROLE_ARN> --type STANDARD
--type STANDARD を指定しています。後述する get-execution-history で実行履歴から認証トークンを確認するためです。
実行して 3 分間アクセスする
イメージの ARN と版、MicroVM の実行ロールを入力にして実行します。
aws stepfunctions start-execution \
--state-machine-arn arn:aws:states:ap-northeast-1:<ACCOUNT_ID>:stateMachine:microvm-3min-site \
--input '{"imageArn":"arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:microvm-image:microvm-3min-site","imageVersion":"1.0","executionRoleArn":"arn:aws:iam::<ACCOUNT_ID>:role/microvm-3min-site-execution"}'
認証トークンは、CreateMicrovmAuthToken を呼び出す CreateAuthToken ステートの成功イベント(TaskSucceeded)の出力に含まれます。実行履歴からこのイベントを取得します。
aws stepfunctions get-execution-history --execution-arn <EXECUTION_ARN> \
--query 'events[?type==`TaskSucceeded`].taskSucceededEventDetails.output'
実行が終わったあとの結果です。主要なフィールドを抜粋しています。
aws stepfunctions describe-execution --execution-arn <EXECUTION_ARN>
{
"status": "SUCCEEDED",
"startDate": "2026-09-17T20:22:33.344000+09:00",
"stopDate": "2026-09-17T20:25:34.323000+09:00",
"output": "{\"microvmId\":\"microvm-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\",\"url\":\"https://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.lambda-microvm.ap-northeast-1.on.aws\",\"servedUntil\":\"2026-09-17T11:25:33.728Z\"}"
}
RunMicrovm の応答から 180 秒後に停止するよう指示し、実行は停止の完了まで含めて 181 秒で終了しました。
MicroVM 側の最終状態も見ておきます。こちらも主要なフィールドだけです。
aws lambda-microvms get-microvm --microvm-identifier <MICROVM_ID>
{
"state": "TERMINATED",
"startedAt": "2026-09-17T20:22:33.655000+09:00",
"terminatedAt": "2026-09-17T20:25:34.751000+09:00",
"stateReason": "Success.",
"maximumDurationInSeconds": 900,
"ingressNetworkConnectors": [
"arn:aws:lambda:ap-northeast-1:aws:network-connector:aws-network-connector:HTTP_INGRESS"
]
}
RunMicrovm の応答時の State は PENDING でした。実行開始から最初の curl が 200 を返すまでには約 4 秒かかりました。
手元から投げた curl の結果です。
| # | 時刻(UTC) | 条件 | HTTP | 応答から |
|---|---|---|---|---|
| 1 | 11:22:37 | トークンなし | 403 | Request missing authentication |
| 2 | 11:22:37 | トークンあり | 200 | now_utc: 2026-09-17T11:22:37.482160+00:00 |
| 3 | 11:22:47 | トークンあり | 200 | now_utc: 2026-09-17T11:22:47.651081+00:00 |
| 4 | 11:22:57 | トークンあり | 200 | now_utc: 2026-09-17T11:22:57.708069+00:00 |
| 5 | 11:25:35(応答は 11:25:38) | トークンあり | 502 | 本文なし |
撤去
失敗した実行で残った MicroVM を先に終了し、その後に作成したリソースを削除します。
撤去コマンド
aws lambda-microvms list-microvms
aws lambda-microvms terminate-microvm --microvm-identifier <MICROVM_ID>
aws stepfunctions delete-state-machine --state-machine-arn <STATE_MACHINE_ARN>
aws lambda-microvms delete-microvm-image --image-identifier <IMAGE_ARN>
aws iam delete-role-policy --role-name <ROLE> --policy-name <NAME>
aws iam delete-role --role-name <ROLE>
aws s3 rm s3://<BUCKET>/image.zip
aws s3api delete-bucket --bucket <BUCKET>
aws logs delete-log-group --log-group-name /aws/lambda-microvms/microvm-3min-site
まとめ
Step Functions の SDK 統合と JSONata だけで、MicroVM の起動、アクセス用 URL の取得とトークンの発行、一定時間後の停止までを、Lambda 関数なしに書けました。
Standard Workflow の実行時間は最大 1 年、Lambda MicroVMs は最大 8 時間稼働できます。今回のように認証トークンを一度だけ発行する構成では、アクセスできる時間は最長 60 分ですが、短時間のデモサイトのように、利用時間を明確に区切る用途に応用できそうです。
Lambda MicroVMs の SDK 統合には約 3 か月かかりましたが、今後は新サービスがリリース後数週間で統合される見込みです。新しいサービスを扱う際にも、Step Functions の SDK 統合を積極的に活用していきたいと思います。







