Lambda MicroVMs に対応した Step Functions の SDK 統合を試してみた

Lambda MicroVMs に対応した Step Functions の SDK 統合を試してみた

トークン認証が必要な Lambda MicroVMs サイトを起動し、指定した期間後に停止する仕組みを、StepFunctions の SDK 統合と JSONata だけで作ってみました。Lambda 関数は使いません
2026.09.17

はじめに

2026 年 9 月 15 日、AWS は、AWS Step Functions が新しい AWS サービスや機能向けの AWS SDK 統合をリリース後数週間以内に自動追加するようになったと発表しました。

https://aws.amazon.com/jp/about-aws/whats-new/2026/06/aws-step-functions-integrations/

今回の告知では、新しい AWS サービスとして AWS Lambda MicroVMs、AWS Lambda Core などが紹介されています。

Step Functions の SDK 統合から、6月にリリースされた Lambda MicroVMs の操作を試してみました。

https://dev.classmethod.jp/articles/aws-lambda-microvms-flask-suspend-resume/

この記事で作るもの

トークン認証が必要なウェブサイトを起動し、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(version 1

サイトには、リクエストごとに現在時刻を返し、アクセスログを標準出力へ出力する 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"]

Dockerfileapp.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 統合で RunMicrovmCreateMicrovmAuthTokenTerminateMicrovm を呼び出し、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 の応答時の StatePENDING でした。実行開始から最初の curl200 を返すまでには約 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 統合を積極的に活用していきたいと思います。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事