Lambda + IAMロールで Kiro CLI の use_aws ツールを安全に動かしてみた

Lambda + IAMロールで Kiro CLI の use_aws ツールを安全に動かしてみた

Kiro CLIのヘッドレスモードは`--trust-all-tools`で全ツールを許可して動かします。野放しは怖いですが、Lambda + IAMロールなら実行できるAWS操作を最小権限に絞れます。Docker化でコールドスタートも22秒に短縮。CloudFormation 2スタック構成でテンプレート化したので紹介します。
2026.05.21

Kiro CLI 2.0でヘッドレスモードが追加されました。KIRO_API_KEY環境変数と--no-interactive--trust-all-toolsフラグで対話なし実行できます。

https://dev.classmethod.jp/articles/kiro-cli-2-0-headless-mode-api-key-auth/

Kiro CLIはuse_awsという専用ツールを備えており、内部でawsコマンドを実行してAWS APIを操作します。Lambda上で動かせばイベント駆動でAWS操作を自動化でき、操作範囲はIAMロールで制御できるのがポイントです。

公式CI/CD例(GitHub Actions)では毎回curl | bashでインストールしますが、Lambdaでは毎回ダウンロードが走りコールドスタート約40秒。これをDocker化で短縮できるか検証しました。

Lambdaへのデプロイ方式として3パターンを検討しました。

方式 コールドスタート ウォーム ECR必要 デプロイ
/tmp都度インストール 41秒 3.5秒 zip
Docker (ECR) 22秒 3.2秒 イメージ
Lambda Layer 不可(452MB > 上限250MB) - - -

※ 測定条件: メモリ512MB / ARM64 / us-east-1 / kiro-cli 2.3.0 / プロンプト「What is 2+2?」

検証内容

Dockerfile

ベースイメージはpublic.ecr.aws/lambda/python:3.12です。ポイントは以下の通り。

  • Kiro CLIをビルド時にインストールし、/usr/local/bin へコピー。Lambda実行環境は非rootなので /root/.local/bin には直接アクセスできません
  • ENV PATHはビルド時のインストールスクリプト用。Lambda実行時はhandler.pyからフルパス指定で呼び出すため、実行時のPATH依存はありません
  • KIRO_CLI_SKIP_SETUP=1でインストールスクリプトの初期セットアップ(対話的な設定)をスキップ
  • AWS CLI v2もインストール。use_awsツールが内部でawsコマンドをspawnするため必須です
Dockerfile全文
FROM public.ecr.aws/lambda/python:3.12

ENV PATH="/root/.local/bin:${PATH}"

RUN dnf install -y tar gzip unzip && \
    curl -fsSL https://cli.kiro.dev/install | KIRO_CLI_SKIP_SETUP=1 bash && \
    cp /root/.local/bin/kiro-cli* /usr/local/bin/ && \
    curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o /tmp/awscliv2.zip && \
    unzip -q /tmp/awscliv2.zip -d /tmp && /tmp/aws/install && rm -rf /tmp/aws /tmp/awscliv2.zip && \
    dnf clean all

RUN pip install boto3

COPY handler.py ${LAMBDA_TASK_ROOT}/

CMD ["handler.handler"]

handler.py

Lambda関数のエントリポイントです。

  • SSM Parameter Store(SecureString)からKiro APIキーを取得し、ウォーム時はキャッシュを利用
  • HOME=/tmpを設定。kiro-cliは初回起動時にSQLiteDBを作成するため書き込み可能なパスが必要(/var/taskは読み取り専用)

--trust-all-toolsは全ツール実行を許可するフラグですが、Lambdaではuse_awsが実行するAWS APIコールがIAMロールの権限に縛られます。今回はec2:DescribeRegionsec2:DescribeAvailabilityZonesのみ許可しており、仮にエージェントがEC2インスタンスを起動しようとしてもPermission Deniedになります。用途別にLambdaを分ければ、ロール単位で最小権限を実現できます。

APIキーの発行手順は前回の記事を参照してください。

handler.py全文
import subprocess
import json
import os
import time

import boto3

ssm = boto3.client("ssm", region_name=os.environ.get("AWS_REGION", "us-east-1"))
_api_key_cache = None

def get_api_key():
    global _api_key_cache
    if not _api_key_cache:
        _api_key_cache = os.environ.get("KIRO_API_KEY")
        if not _api_key_cache:
            resp = ssm.get_parameter(Name="/kiro/headless/api-key", WithDecryption=True)
            _api_key_cache = resp["Parameter"]["Value"]
    return _api_key_cache

def handler(event, context):
    timings = {}
    t0 = time.time()

    prompt = event.get("prompt", "")
    if not prompt or len(prompt.strip()) < 10:
        return {"statusCode": 400, "error": "Prompt too short (min 10 chars)"}

    api_key = get_api_key()
    env = {**os.environ, "KIRO_API_KEY": api_key, "HOME": "/tmp"}
    kiro_bin = "/usr/local/bin/kiro-cli"

    cmd = [kiro_bin, "chat", "--no-interactive", "--trust-all-tools"]
    # model = event.get("model")  # e.g. "claude-sonnet-4.6"
    # if model:
    #     cmd += ["--model", model]
    cmd.append(prompt)

    start = time.time()
    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        timeout=280,
        env=env,
    )
    timings["kiro_execution"] = round(time.time() - start, 2)
    timings["total"] = round(time.time() - t0, 2)

    if result.returncode != 0:
        return {
            "statusCode": 500,
            "error": result.stderr.strip() or f"kiro-cli exited with code {result.returncode}",
            "timings": timings,
        }

    return {
        "statusCode": 200,
        "prompt": prompt,
        "result": result.stdout.strip(),
        "timings": timings,
    }

/tmp方式(参考)

Docker方式との比較対象として、/tmp に都度インストールする方式も検証しました。Python標準ライブラリのみ(urllib + zipfile)でKiro CLIとAWS CLIをダウンロード・展開します。ECR不要でPoC向きですが、コールドスタートが41秒かかり、ネットワーク依存もあるため本番運用にはDocker方式を推奨します。

handler_tmp.py全文
import subprocess
import json
import os
import time
import zipfile
import urllib.request

import boto3

ssm = boto3.client("ssm", region_name=os.environ.get("AWS_REGION", "us-east-1"))
_api_key_cache = None

KIRO_BIN = "/tmp/.local/bin/kiro-cli"
AWS_CLI_BIN = "/tmp/aws-bin/aws"

def get_api_key():
    global _api_key_cache
    if not _api_key_cache:
        _api_key_cache = os.environ.get("KIRO_API_KEY")
        if not _api_key_cache:
            resp = ssm.get_parameter(Name="/kiro/headless/api-key", WithDecryption=True)
            _api_key_cache = resp["Parameter"]["Value"]
    return _api_key_cache

def download_and_extract(url, dest_dir):
    """Download zip and extract to dest_dir."""
    zip_path = f"/tmp/{os.path.basename(dest_dir)}.zip"
    urllib.request.urlretrieve(url, zip_path)
    with zipfile.ZipFile(zip_path, "r") as zf:
        zf.extractall(dest_dir)
    os.remove(zip_path)

def install_kiro():
    if os.path.exists(KIRO_BIN):
        return 0
    start = time.time()
    arch = "aarch64" if os.uname().machine == "aarch64" else "x86_64"
    url = f"https://desktop-release.q.us-east-1.amazonaws.com/latest/kirocli-{arch}-linux.zip"
    download_and_extract(url, "/tmp/kirocli-pkg")
    os.makedirs("/tmp/.local/bin", exist_ok=True)
    for name in ["kiro-cli", "kiro-cli-chat", "kiro-cli-term"]:
        src = f"/tmp/kirocli-pkg/kirocli/bin/{name}"
        dst = f"/tmp/.local/bin/{name}"
        if os.path.exists(src):
            subprocess.run(["cp", src, dst], check=True)
            os.chmod(dst, 0o755)
    subprocess.run(["rm", "-rf", "/tmp/kirocli-pkg"], capture_output=True)
    return round(time.time() - start, 2)

def install_aws_cli():
    if os.path.exists(AWS_CLI_BIN):
        return 0
    start = time.time()
    arch = "aarch64" if os.uname().machine == "aarch64" else "x86_64"
    url = f"https://awscli.amazonaws.com/awscli-exe-linux-{arch}.zip"
    download_and_extract(url, "/tmp/aws-install")
    installer = "/tmp/aws-install/aws/install"
    os.chmod(installer, 0o755)
    subprocess.run(
        [installer, "--install-dir", "/tmp/aws-cli", "--bin-dir", "/tmp/aws-bin"],
        check=True, capture_output=True, text=True,
    )
    subprocess.run(["rm", "-rf", "/tmp/aws-install"], capture_output=True)
    return round(time.time() - start, 2)

def handler(event, context):
    timings = {}

    t0 = time.time()
    try:
        timings["kiro_install"] = install_kiro()
    except Exception as e:
        return {"statusCode": 500, "error": f"kiro install failed: {e}", "timings": timings}
    try:
        timings["aws_cli_install"] = install_aws_cli()
    except Exception as e:
        return {"statusCode": 500, "error": f"aws cli install failed: {e}", "timings": timings}
    timings["total_install"] = round(time.time() - t0, 2)

    prompt = event.get("prompt", "")
    if not prompt or len(prompt.strip()) < 10:
        return {"statusCode": 400, "error": "Prompt too short (min 10 chars)", "timings": timings}

    api_key = get_api_key()
    env = {
        **os.environ,
        "KIRO_API_KEY": api_key,
        "HOME": "/tmp",
        "PATH": f"/tmp/.local/bin:/tmp/aws-bin:{os.environ.get('PATH', '')}",
    }

    start = time.time()
    result = subprocess.run(
        [KIRO_BIN, "chat", "--no-interactive", "--trust-all-tools", prompt],
        capture_output=True, text=True, timeout=280, env=env,
    )
    timings["kiro_execution"] = round(time.time() - start, 2)
    timings["total"] = round(time.time() - t0, 2)

    if result.returncode != 0:
        return {
            "statusCode": 500,
            "error": result.stderr.strip() or f"exit code {result.returncode}",
            "timings": timings,
        }

    return {
        "statusCode": 200,
        "prompt": prompt,
        "result": result.stdout.strip(),
        "timings": timings,
    }

CloudFormation + CodeBuild

S3やGitHub連携なしで、テンプレート2枚だけで環境構築を完結させる設計です。

スタック 内容
Stack1 ECRリポジトリ + CodeBuildプロジェクト
Stack2 Lambda関数(Stack1でイメージpush後にデプロイ)

CodeBuildはNO_SOURCEタイプで、buildspec内にDockerfileとhandler.pyをbase64埋め込みしています。ARM64ビルド環境(aws/codebuild/amazonlinux-aarch64-standard:3.0)を使用します。

stack1-ecr-codebuild.yaml全文
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Kiro Headless Lambda - Stack 1: ECR + CodeBuild'

Resources:
  EcrRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: kiro-headless-lambda
      ImageScanningConfiguration:
        ScanOnPush: true
      LifecyclePolicy:
        LifecyclePolicyText: |
          {"rules":[{"rulePriority":1,"selection":{"tagStatus":"untagged","countType":"imageCountMoreThan","countNumber":3},"action":{"type":"expire"}}]}

  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodeBuildPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: '*'
              - Effect: Allow
                Action: ecr:GetAuthorizationToken
                Resource: '*'
              - Effect: Allow
                Action:
                  - ecr:BatchCheckLayerAvailability
                  - ecr:GetDownloadUrlForLayer
                  - ecr:BatchGetImage
                  - ecr:PutImage
                  - ecr:InitiateLayerUpload
                  - ecr:UploadLayerPart
                  - ecr:CompleteLayerUpload
                Resource: !GetAtt EcrRepository.Arn

  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: kiro-headless-lambda-build
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Artifacts:
        Type: NO_ARTIFACTS
      Environment:
        Type: ARM_CONTAINER
        Image: aws/codebuild/amazonlinux-aarch64-standard:3.0
        ComputeType: BUILD_GENERAL1_SMALL
        PrivilegedMode: true
        EnvironmentVariables:
          - Name: ECR_REPO_URI
            Value: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepository}'
      Source:
        Type: NO_SOURCE
        BuildSpec: !Sub |
          version: 0.2
          phases:
            pre_build:
              commands:
                - aws ecr get-login-password | docker login --username AWS --password-stdin "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com"
            build:
              commands:
                - echo "RlJPTSBwdWJsaWMuZWNyLmF3cy9sYW1iZGEvcHl0aG9uOjMuMTIKCkVOViBQQVRIPSIvcm9vdC8ubG9jYWwvYmluOiR7UEFUSH0iCgpSVU4gZG5mIGluc3RhbGwgLXkgdGFyIGd6aXAgdW56aXAgJiYgXAogICAgY3VybCAtZnNTTCBodHRwczovL2NsaS5raXJvLmRldi9pbnN0YWxsIHwgS0lST19DTElfU0tJUF9TRVRVUD0xIGJhc2ggJiYgXAogICAgY3AgL3Jvb3QvLmxvY2FsL2Jpbi9raXJvLWNsaSogL3Vzci9sb2NhbC9iaW4vICYmIFwKICAgIGN1cmwgImh0dHBzOi8vYXdzY2xpLmFtYXpvbmF3cy5jb20vYXdzY2xpLWV4ZS1saW51eC1hYXJjaDY0LnppcCIgLW8gL3RtcC9hd3NjbGl2Mi56aXAgJiYgXAogICAgdW56aXAgLXEgL3RtcC9hd3NjbGl2Mi56aXAgLWQgL3RtcCAmJiAvdG1wL2F3cy9pbnN0YWxsICYmIHJtIC1yZiAvdG1wL2F3cyAvdG1wL2F3c2NsaXYyLnppcCAmJiBcCiAgICBkbmYgY2xlYW4gYWxsCgpSVU4gcGlwIGluc3RhbGwgYm90bzMKCkNPUFkgaGFuZGxlci5weSAke0xBTUJEQV9UQVNLX1JPT1R9LwoKQ01EIFsiaGFuZGxlci5oYW5kbGVyIl0K" | base64 -d > Dockerfile
                - echo "aW1wb3J0IHN1YnByb2Nlc3MKaW1wb3J0IGpzb24KaW1wb3J0IG9zCmltcG9ydCB0aW1lCgppbXBvcnQgYm90bzMKCnNzbSA9IGJvdG8zLmNsaWVudCgic3NtIiwgcmVnaW9uX25hbWU9b3MuZW52aXJvbi5nZXQoIkFXU19SRUdJT04iLCAidXMtZWFzdC0xIikpCl9hcGlfa2V5X2NhY2hlID0gTm9uZQoKCmRlZiBnZXRfYXBpX2tleSgpOgogICAgZ2xvYmFsIF9hcGlfa2V5X2NhY2hlCiAgICBpZiBub3QgX2FwaV9rZXlfY2FjaGU6CiAgICAgICAgX2FwaV9rZXlfY2FjaGUgPSBvcy5lbnZpcm9uLmdldCgiS0lST19BUElfS0VZIikKICAgICAgICBpZiBub3QgX2FwaV9rZXlfY2FjaGU6CiAgICAgICAgICAgIHJlc3AgPSBzc20uZ2V0X3BhcmFtZXRlcihOYW1lPSIva2lyby9oZWFkbGVzcy9hcGkta2V5IiwgV2l0aERlY3J5cHRpb249VHJ1ZSkKICAgICAgICAgICAgX2FwaV9rZXlfY2FjaGUgPSByZXNwWyJQYXJhbWV0ZXIiXVsiVmFsdWUiXQogICAgcmV0dXJuIF9hcGlfa2V5X2NhY2hlCgoKZGVmIGhhbmRsZXIoZXZlbnQsIGNvbnRleHQpOgogICAgdGltaW5ncyA9IHt9CiAgICB0MCA9IHRpbWUudGltZSgpCgogICAgcHJvbXB0ID0gZXZlbnQuZ2V0KCJwcm9tcHQiLCAiIikKICAgIGlmIG5vdCBwcm9tcHQgb3IgbGVuKHByb21wdC5zdHJpcCgpKSA8IDEwOgogICAgICAgIHJldHVybiB7InN0YXR1c0NvZGUiOiA0MDAsICJlcnJvciI6ICJQcm9tcHQgdG9vIHNob3J0IChtaW4gMTAgY2hhcnMpIn0KCiAgICBhcGlfa2V5ID0gZ2V0X2FwaV9rZXkoKQogICAgZW52ID0geyoqb3MuZW52aXJvbiwgIktJUk9fQVBJX0tFWSI6IGFwaV9rZXksICJIT01FIjogIi90bXAifQogICAga2lyb19iaW4gPSAiL3Vzci9sb2NhbC9iaW4va2lyby1jbGkiCgogICAgc3RhcnQgPSB0aW1lLnRpbWUoKQogICAgcmVzdWx0ID0gc3VicHJvY2Vzcy5ydW4oCiAgICAgICAgW2tpcm9fYmluLCAiY2hhdCIsICItLW5vLWludGVyYWN0aXZlIiwgIi0tdHJ1c3QtYWxsLXRvb2xzIiwgcHJvbXB0XSwKICAgICAgICBjYXB0dXJlX291dHB1dD1UcnVlLAogICAgICAgIHRleHQ9VHJ1ZSwKICAgICAgICB0aW1lb3V0PTI4MCwKICAgICAgICBlbnY9ZW52LAogICAgKQogICAgdGltaW5nc1sia2lyb19leGVjdXRpb24iXSA9IHJvdW5kKHRpbWUudGltZSgpIC0gc3RhcnQsIDIpCiAgICB0aW1pbmdzWyJ0b3RhbCJdID0gcm91bmQodGltZS50aW1lKCkgLSB0MCwgMikKCiAgICBpZiByZXN1bHQucmV0dXJuY29kZSAhPSAwOgogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJzdGF0dXNDb2RlIjogNTAwLAogICAgICAgICAgICAiZXJyb3IiOiByZXN1bHQuc3RkZXJyLnN0cmlwKCkgb3IgZiJraXJvLWNsaSBleGl0ZWQgd2l0aCBjb2RlIHtyZXN1bHQucmV0dXJuY29kZX0iLAogICAgICAgICAgICAidGltaW5ncyI6IHRpbWluZ3MsCiAgICAgICAgfQoKICAgIHJldHVybiB7CiAgICAgICAgInN0YXR1c0NvZGUiOiAyMDAsCiAgICAgICAgInByb21wdCI6IHByb21wdCwKICAgICAgICAicmVzdWx0IjogcmVzdWx0LnN0ZG91dC5zdHJpcCgpLAogICAgICAgICJ0aW1pbmdzIjogdGltaW5ncywKICAgIH0K" | base64 -d > handler.py
                - docker build --platform linux/arm64 -t "$ECR_REPO_URI:latest" .
            post_build:
              commands:
                - docker push "$ECR_REPO_URI:latest"

Outputs:
  EcrRepositoryUri:
    Value: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepository}'
    Export:
      Name: KiroHeadlessEcrUri
  CodeBuildProjectName:
    Value: !Ref CodeBuildProject
stack2-lambda.yaml全文
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Kiro Headless Lambda - Stack 2: Lambda Function'

Parameters:
  ImageUri:
    Type: String
    Description: ECR Image URI (from Stack 1 CodeBuild output)
  SsmParameterName:
    Type: String
    Default: /kiro/headless/api-key
    Description: handler.py内のパラメータ名と一致させること

Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: KiroHeadlessPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Sid: KiroApiKey
                Effect: Allow
                Action: ssm:GetParameter
                Resource: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter${SsmParameterName}'
              - Sid: KmsDecrypt
                Effect: Allow
                Action: kms:Decrypt
                Resource: '*'
              - Sid: SafeReadOnly
                Effect: Allow
                Action:
                  - ec2:DescribeRegions
                  - ec2:DescribeAvailabilityZones
                Resource: '*'

  KiroLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: kiro-headless
      PackageType: Image
      Code:
        ImageUri: !Ref ImageUri
      Architectures:
        - arm64
      Role: !GetAtt LambdaRole.Arn
      Timeout: 360
      MemorySize: 512

Outputs:
  LambdaFunction:
    Value: !Ref KiroLambda
  LambdaArn:
    Value: !GetAtt KiroLambda.Arn

デプロイ手順

APIキー未取得の方は前回の記事でKiro APIキーを発行してください。

# 1. SSMにAPIキー保存
aws ssm put-parameter \
  --name "/kiro/headless/api-key" \
  --type SecureString \
  --value "YOUR_KIRO_API_KEY" \
  --overwrite \
  --region us-east-1

# 2. スタック1デプロイ (ECR + CodeBuild)
aws cloudformation deploy \
  --template-file stack1-ecr-codebuild.yaml \
  --stack-name KiroHeadlessStack1 \
  --capabilities CAPABILITY_IAM \
  --region us-east-1

# 3. CodeBuildでイメージビルド(約2分)
aws codebuild start-build \
  --project-name kiro-headless-lambda-build \
  --region us-east-1

# ビルド完了を待機(SUCCEEDEDになるまでループ)
BUILD_ID=$(aws codebuild list-builds-for-project \
  --project-name kiro-headless-lambda-build \
  --region us-east-1 \
  --query 'ids[0]' --output text)

while true; do
  STATUS=$(aws codebuild batch-get-builds \
    --ids "$BUILD_ID" \
    --region us-east-1 \
    --query 'builds[0].buildStatus' --output text)
  echo "Build status: $STATUS"
  [ "$STATUS" = "SUCCEEDED" ] && break
  [ "$STATUS" = "FAILED" ] && echo "Build failed!" && exit 1
  sleep 15
done

# 4. スタック2デプロイ (Lambda)
ECR_URI=$(aws cloudformation describe-stacks \
  --stack-name KiroHeadlessStack1 \
  --region us-east-1 \
  --query 'Stacks[0].Outputs[?OutputKey==`EcrRepositoryUri`].OutputValue' --output text)

aws cloudformation deploy \
  --template-file stack2-lambda.yaml \
  --stack-name KiroHeadlessStack2 \
  --capabilities CAPABILITY_IAM \
  --parameter-overrides "ImageUri=${ECR_URI}:latest" \
  --region us-east-1

動作確認

許可された操作: リージョン一覧取得

aws lambda invoke \
  --function-name kiro-headless \
  --region us-east-1 \
  --payload '{"prompt": "アジアパシフィックのAWSリージョンを一覧して"}' \
  --cli-read-timeout 360 \
  result.json && cat result.json | sed 's/\x1b\[[0-9;]*m//g'

出力にはANSIカラーコードが含まれるため、sedで除去しています。

実行結果の例です。use_awsツールがec2:DescribeRegionsを呼び出し、日本語で応答しています。

{
  "statusCode": 200,
  "prompt": "アジアパシフィックのAWSリージョンを一覧して",
  "result": "アジアパシフィックのAWSリージョン一覧:\n\n| リージョン名 | リージョンコード |\n|---|---|\n| アジアパシフィック (東京) | ap-northeast-1 |\n| アジアパシフィック (大阪) | ap-northeast-3 |\n| アジアパシフィック (ソウル) | ap-northeast-2 |\n| アジアパシフィック (ムンバイ) | ap-south-1 |\n| アジアパシフィック (ハイデラバード) | ap-south-2 |\n| アジアパシフィック (シンガポール) | ap-southeast-1 |\n| アジアパシフィック (シドニー) | ap-southeast-2 |\n| アジアパシフィック (ジャカルタ) | ap-southeast-3 |\n| アジアパシフィック (メルボルン) | ap-southeast-4 |\n| アジアパシフィック (香港) | ap-east-1 |\n| アジアパシフィック (マレーシア) | ap-southeast-5 |\n| アジアパシフィック (タイ) | ap-southeast-7 |",
  "timings": {
    "kiro_execution": 8.94,
    "total": 9.05
  }
}

コールドスタート時はLambda側のINIT Duration(約22秒)が加算されます。CloudWatch Logsでは以下のように記録されます。

REPORT Duration: 18920.00 ms  Billed Duration: 19521 ms  Memory Size: 512 MB  Max Memory Used: 358 MB  Init Duration: 22045.12 ms

ウォーム状態ではtimings.totalの値がそのままレスポンス時間です。

拒否される操作: EC2インスタンス一覧取得

IAMロールの効果を確認するため、許可していない操作を試みます。

aws lambda invoke \
  --function-name kiro-headless \
  --region us-east-1 \
  --payload '{"prompt": "us-east-1のEC2インスタンス一覧を取得して表示して"}' \
  --cli-read-timeout 360 \
  result.json && cat result.json
{
  "statusCode": 200,
  "prompt": "us-east-1のEC2インスタンス一覧を取得して表示して",
  "result": "現在の実行環境(IAMロール KiroHeadlessStack2-LambdaRole)には ec2:DescribeInstances の権限が付与されていないため、EC2インスタンス一覧を取得できませんでした。\n\n対処方法:\n- このIAMロールに ec2:DescribeInstances 権限を追加する\n- または、適切な権限を持つAWSプロファイルを指定して再実行する",
  "timings": {
    "kiro_execution": 28.77,
    "total": 28.77
  }
}

ec2:DescribeInstancesは許可していないため、use_awsツールが実行してもIAMロールがブロックします。エージェントは権限不足を検知して対処方法まで案内してくれます。--trust-all-toolsで全ツールの実行自体は許可しつつ、実際のAWS操作はIAMポリシーで最小権限に制御できています。

なお、EC2インスタンス起動のような高リスク操作はIAM以前にエージェント自身のセーフティガードレールが確認を求めます。IAMロール + エージェントのガードレールの多重防御が効いている形です。

まとめ

--trust-all-toolsを使いつつ、IAMロールでec2:Describe*のみに絞ることで、use_awsツールを安全にLambdaで実行できました。Docker化によりコールドスタートも/tmp方式の41秒から22秒に短縮。CloudFormation+CodeBuildでテンプレート化したので、AWSアカウントとKiro APIキーがあれば数分で再現可能です。

AWS MCPサーバー経由のAWS操作も選択肢ですが、Kiroのuse_awsツールはAWS CLIの全操作をカバーしており、Lambda+IAMロールとの組み合わせで権限制御もシンプルです。ぜひお試しください。

参考リンク

この記事をシェアする

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

関連記事