Serverless Operations, inc

>_cd /blog/id_dup1-l9i3

title

Amazon Bedrock AgentCore Policyを試してみた 〜AIエージェントのツール利用をCedarで制御する 〜

今日は先日GAを迎えた Amazon Bedrock AgentCore Policy を試していきます。

AgentCore Policy とは?

AIエージェントは、顧客対応から複数のシステムにまたがるワークフロー自動化まで、複雑な問題を柔軟に解決できる強力な存在とななりました。しかしその柔軟性ゆえに、エージェントがビジネスルールを誤って解釈したり、意図した権限を超えて行動してしまうリスクが増大しています。

Amazon Bedrock AgentCore Policy は、こうしたリスクに対処するための認可制御機能です。AIエージェントが AgentCore Gateway を経由してツールを実行する際に「誰が」「どのツールを」「どのような条件で」使えるかを、ポリシーベースで細かく制御できます。

ポリシー言語: Cedar

ポリシーの記述には Cedar(シーダー)というAWSが開発したオープンソースのポリシー言語を使用します。Cedarを使うと、たとえば「返金処理ツールは1000ドル未満の場合のみ許可する」「特定のユーザーロールを持つ場合のみアクセスを許可する」といったルールを明確かつ厳密に定義できます。定義したポリシーはポリシーエンジンに格納され、Gatewayに関連付けることで、エージェントからの全リクエストがツールに到達する前に自動的に評価・適用されます。

この仕組みの特徴は、セキュリティ制御をエージェントのコードの外側に切り出せる点にあります。エージェントの実装方法に依存せず、常に一貫した認可チェックが行われるため、エージェントを操作してポリシーを回避するといった攻撃にも強い構造になっています。また、ポリシーの評価結果はCloudWatchに記録されるため、監査やトラブルシューティングにも活用できます。

なお、Cedarによる記述が難しい場合は、自然言語(英語)でルールを説明するだけでCedarポリシーを自動生成する機能も備えています。生成されたポリシーは過剰な許可や過剰な制限がないかを自動で検証した上で適用されるため、ポリシー設計の品質担保にも役立ちます。

各モジュールの関係性

作業に入る前に各モジュールの関係性を整理しておきましょう。

Amazon Bedrock AgentCore の Policy 機能を理解するうえで、これら4つのコンポーネントがどのように連携しているかを把握することが重要です。

Gateway はエージェントとツール群をつなぐ中央の窓口です。エージェントはGatewayのエンドポイントに対してリクエストを送り、GatewayがそれをTarget(実際のツール)に転送します。GatewayはOAuth認証によりリクエスト元を検証し、すべてのトラフィックの入口として機能します。

Target はGatewayに登録された実際のツールの接続先です。今回のチュートリアルではAWS Lambda関数をTargetとして登録します。Targetにはツールのスキーマ(どのような入力を受け付けるか)も定義されており、エージェントはこのスキーマを参照してツールを呼び出します。ひとつのGatewayに複数のTargetを登録することができ、それぞれのTargetが個別のツールに対応します。

Policy Engine はCedarポリシーのコレクションを管理する評価エンジンです。Gatewayに関連付けることで、エージェントからのリクエストがTargetに到達する前に必ず通過する「ポリシーの審判所」として機能します。Policy Engineは複数のPolicyを束ねて管理し、リクエストが来るたびに全ポリシーを評価して許可・拒否を決定します。

Policy はPolicy Engine内に定義される個々のルールです。Cedar言語で「誰が・何を・どのリソースに対して・どんな条件で」許可または拒否するかを記述します。Cedarはデフォルト拒否(deny-by-default)の設計になっているため、明示的に許可するポリシーがない限り、すべてのリクエストは拒否されます。

リクエストが届くと、GatewayはまずPolicy Engineに問い合わせます。Policy Engineが全Policyを評価し、許可と判断された場合のみGatewayがTargetにリクエストを転送します。拒否の場合はTargetまで到達することなくエラーレスポンスが返されます。

この構造の重要なポイントは、ポリシーの適用がエージェントのコードや実装とは完全に切り離されている点です。エージェント側にセキュリティロジックを埋め込む必要がなく、Policy Engineのルールを変更するだけで組織全体のアクセス制御を一元管理できます。

さっそくやってみる

aws configure でクレデンシャルのセット

まずは実行環境柄読み取れる IAM クレデンシャルを aws configure でセットします。

以下の権限が必要です。Power User ではIAM関連の権限が不足するため、検証環境であれば一時的に AdministratorAccess を使ってしまうのが早いです。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "STSAccess",
      "Effect": "Allow",
      "Action": ["sts:GetCallerIdentity"],
      "Resource": "*"
    },
    {
      "Sid": "IAMAccess",
      "Effect": "Allow",
      "Action": [
        "iam:CreateRole",
        "iam:GetRole",
        "iam:PutRolePolicy",
        "iam:AttachRolePolicy",
        "iam:UpdateAssumeRolePolicy",
        "iam:GetRolePolicy",
        "iam:ListRolePolicies",
        "iam:ListAttachedRolePolicies",
        "iam:DeleteRole",
        "iam:DeleteRolePolicy",
        "iam:PutUserPolicy",
        "iam:PassRole"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CognitoAccess",
      "Effect": "Allow",
      "Action": ["cognito-idp:*"],
      "Resource": "*"
    },
    {
      "Sid": "LambdaAccess",
      "Effect": "Allow",
      "Action": [
        "lambda:CreateFunction",
        "lambda:GetFunction",
        "lambda:DeleteFunction",
        "lambda:AddPermission",
        "lambda:RemovePermission",
        "lambda:InvokeFunction",
        "lambda:ListFunctions"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AgentCoreAccess",
      "Effect": "Allow",
      "Action": ["bedrock-agentcore:*"],
      "Resource": "*"
    },
    {
      "Sid": "CloudWatchAccess",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogDelivery",
        "logs:DeleteLogDelivery",
        "logs:PutLogEvents",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams",
        "logs:PutRetentionPolicy"
      ],
      "Resource": "*"
    }
  ]
}

セットアップ

以下のスクリプトを実行することで一気に環境が構築されます。

"""
Setup script - Hatena記事の手順に基づいた修正版
Gateway作成後にサービスロールへPolicy Engine権限を追加してからPolicy作成
Run: python setup_policy.py
"""

from bedrock_agentcore_starter_toolkit.operations.gateway.client import GatewayClient
from bedrock_agentcore_starter_toolkit.operations.policy.client import PolicyClient
from bedrock_agentcore_starter_toolkit.utils.lambda_utils import create_lambda_function
import boto3
import json
import logging
import time


def add_policy_engine_permissions_to_role(role_arn, region, account_id):
    """Gateway サービスロールに Policy Engine 用権限を追加する"""
    iam = boto3.client("iam")
    role_name = role_arn.split("/")[-1]

    policy_doc = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "BedrockAgentCoreFullAccess",
                "Effect": "Allow",
                "Action": ["bedrock-agentcore:*"],
                "Resource": "*"
            },
            {
                "Sid": "PolicyEngineConfiguration",
                "Effect": "Allow",
                "Action": ["bedrock-agentcore:GetPolicyEngine"],
                "Resource": [
                    f"arn:aws:bedrock-agentcore:{region}:{account_id}:policy-engine/*"
                ]
            },
            {
                "Sid": "PolicyEngineAuthorization",
                "Effect": "Allow",
                "Action": [
                    "bedrock-agentcore:AuthorizeAction",
                    "bedrock-agentcore:PartiallyAuthorizeActions"
                ],
                "Resource": [
                    f"arn:aws:bedrock-agentcore:{region}:{account_id}:policy-engine/*",
                    f"arn:aws:bedrock-agentcore:{region}:{account_id}:gateway/*"
                ]
            },
            {
                "Sid": "LambdaInvokeAccess",
                "Effect": "Allow",
                "Action": ["lambda:InvokeFunction"],
                "Resource": "arn:aws:lambda:*:*:function:*"
            },
            {
                "Sid": "GetSecretValue",
                "Effect": "Allow",
                "Action": ["secretsmanager:GetSecretValue"],
                "Resource": "*"
            },
            {
                "Sid": "CloudWatchLogs",
                "Effect": "Allow",
                "Action": [
                    "logs:CreateLogGroup",
                    "logs:CreateLogDelivery",
                    "logs:PutLogEvents",
                    "logs:DescribeLogGroups",
                    "logs:DescribeLogStreams"
                ],
                "Resource": "*"
            }
        ]
    }

    iam.put_role_policy(
        RoleName=role_name,
        PolicyName="PolicyEnginePermissions",
        PolicyDocument=json.dumps(policy_doc)
    )
    print(f"  ✅ {role_name} に PolicyEnginePermissions を追加")


def setup_policy():
    region = "us-west-2"
    refund_limit = 1000
    ts = int(time.time())

    print("🚀 Setting up AgentCore Gateway with Policy Engine...")
    print(f"Region: {region}\n")

    sts = boto3.client("sts")
    account_id = sts.get_caller_identity()["Account"]
    print(f"Account ID: {account_id}\n")

    gateway_client = GatewayClient(region_name=region)
    gateway_client.logger.setLevel(logging.INFO)

    policy_client = PolicyClient(region_name=region)
    policy_client.logger.setLevel(logging.INFO)

    # Step 1: OAuth authorizer 作成
    print("Step 1: Creating OAuth authorization server...")
    cognito_response = gateway_client.create_oauth_authorizer_with_cognito("PolicyGateway")
    print("✓ Authorization server created\n")

    # Step 2: Gateway 作成(toolkit使用・サービスロールを自動作成)
    print("Step 2: Creating Gateway...")
    gateway = gateway_client.create_mcp_gateway(
        name=None,
        role_arn=None,
        authorizer_config=cognito_response["authorizer_config"],
        enable_semantic_search=False,
    )
    print(f"✓ Gateway created: {gateway['gatewayUrl']}")
    print(f"  Gateway ID:  {gateway['gatewayId']}")
    print(f"  Role ARN:    {gateway['roleArn']}\n")

    # Step 3: fix_iam_permissions(Trust Policy修正)
    print("Step 3: Fixing IAM trust policy...")
    gateway_client.fix_iam_permissions(gateway)
    print("⏳ Waiting 30s for IAM propagation...")
    time.sleep(30)
    print("✓ Trust policy fixed\n")

    # Step 4: Gateway サービスロールに Policy Engine 権限を追加(重要!)
    print("Step 4: Adding Policy Engine permissions to Gateway service role...")
    add_policy_engine_permissions_to_role(gateway["roleArn"], region, account_id)
    print("⏳ Waiting 30s for IAM propagation...")
    time.sleep(30)
    print("✓ Policy Engine permissions added\n")

    # Step 5: Lambda 作成
    print("Step 5: Creating Lambda function with refund tool...")
    refund_lambda_code = """
def lambda_handler(event, context):
    amount = event.get('amount', 0)
    return {
        "status": "success",
        "message": f"Refund of ${amount} processed successfully",
        "amount": amount
    }
"""
    session = boto3.Session(region_name=region)
    lambda_arn = create_lambda_function(
        session=session,
        logger=gateway_client.logger,
        function_name=f"RefundTool-{ts}",
        lambda_code=refund_lambda_code,
        runtime="python3.13",
        handler="lambda_function.lambda_handler",
        gateway_role_arn=gateway["roleArn"],
        description="Refund tool for policy demo",
    )
    print("✓ Lambda function created\n")

    print("⏳ Waiting 10s for Lambda propagation")
    time.sleep(10)

    # Step 6: Lambda Target 追加
    print("Step 6: Adding Lambda target...")
    gateway_client.create_mcp_gateway_target(
        gateway=gateway,
        name="RefundTarget",
        target_type="lambda",
        target_payload={
            "lambdaArn": lambda_arn,
            "toolSchema": {
                "inlinePayload": [{
                    "name": "process_refund",
                    "description": "Process a customer refund",
                    "inputSchema": {
                        "type": "object",
                        "properties": {
                            "amount": {
                                "type": "integer",
                                "description": "Refund amount in dollars"
                            }
                        },
                        "required": ["amount"],
                    },
                }]
            },
        },
        credentials=None,
    )
    print("✓ Lambda target added\n")

    # Step 7: Policy Engine 作成
    engine_name = f"RefundPolicyEngine{ts}"
    print(f"Step 7: Creating Policy Engine ({engine_name})...")
    engine = policy_client.create_or_get_policy_engine(
        name=engine_name,
        description="Policy engine for refund governance"
    )
    print(f"✓ Policy Engine: {engine['policyEngineId']}\n")

    # Step 8: Cedar Policy 作成
    policy_name = f"refund_limit_policy{ts}"
    cedar_statement = (
        f"permit(principal, "
        f'action == AgentCore::Action::"RefundTarget___process_refund", '
        f'resource == AgentCore::Gateway::"{gateway["gatewayArn"]}") '
        f"when {{ context.input.amount < {refund_limit} }};"
    )
    print(f"Step 8: Creating Cedar policy ({policy_name})...")
    print(f"  Cedar: {cedar_statement}\n")

    policy = policy_client.create_or_get_policy(
        policy_engine_id=engine["policyEngineId"],
        name=policy_name,
        description=f"Allow refunds under ${refund_limit}",
        definition={"cedar": {"statement": cedar_statement}},
    )
    print(f"✓ Policy: {policy['policyId']}\n")

    # Step 9: Policy Engine を Gateway に紐付け
    print("Step 9: Attaching Policy Engine to Gateway (ENFORCE mode)...")
    gateway_client.update_gateway_policy_engine(
        gateway_identifier=gateway["gatewayId"],
        policy_engine_arn=engine["policyEngineArn"],
        mode="ENFORCE"
    )
    print("✓ Policy Engine attached to Gateway\n")

    # Step 10: config.json 保存
    config = {
        "gateway_url": gateway["gatewayUrl"],
        "gateway_id": gateway["gatewayId"],
        "gateway_arn": gateway["gatewayArn"],
        "policy_engine_id": engine["policyEngineId"],
        "policy_engine_arn": engine["policyEngineArn"],
        "policy_id": policy["policyId"],
        "region": region,
        "client_info": cognito_response["client_info"],
        "refund_limit": refund_limit
    }
    with open("config.json", "w") as f:
        json.dump(config, f, indent=2)

    print("=" * 60)
    print("✅ Setup complete!")
    print(f"Gateway URL:      {gateway['gatewayUrl']}")
    print(f"Policy Engine ID: {engine['policyEngineId']}")
    print(f"Policy ID:        {policy['policyId']}")
    print(f"Refund limit:     ${refund_limit}")
    print("\nConfiguration saved to: config.json")
    print("\nNext step: Run 'python test_policy.py'")
    print("=" * 60)

    return config


if __name__ == "__main__":
    setup_policy()

python setup_policy.pyを実行して数分待ちます。エラーとなる場合 us-east-1 を選択するか、aws configure でセットしているIAM権限を強くしてみてください。

python setup_policy.py
🚀 Setting up AgentCore Gateway with Policy Engine...
Region: us-east-1

Account ID: 917561075114

Step 1: Creating OAuth authorization server...
2026-06-06 19:07:37,471 - bedrock_agentcore.gateway - INFO - Starting EZ Auth setup: Creating Cognito resources...
2026-06-06 19:07:39,446 - bedrock_agentcore.gateway - INFO -   ✓ Created User Pool: us-east-1_nkOV8aCkj
2026-06-06 19:07:40,367 - bedrock_agentcore.gateway - INFO -   ✓ Created domain: agentcore-bffc410d
2026-06-06 19:07:40,367 - bedrock_agentcore.gateway - INFO -   ⏳ Waiting for domain to be available...
2026-06-06 19:07:40,589 - bedrock_agentcore.gateway - INFO -   ✓ Domain is active
2026-06-06 19:07:41,078 - bedrock_agentcore.gateway - INFO -   ✓ Created resource server: PolicyGateway
2026-06-06 19:07:41,540 - bedrock_agentcore.gateway - INFO -   ✓ Created client: 1vg6iqjh1l04jih9eogf1gdsml
2026-06-06 19:07:41,540 - bedrock_agentcore.gateway - INFO -   ⏳ Waiting for DNS propagation of domain: agentcore-bffc410d.auth.us-east-1.amazoncognito.com
2026-06-06 19:08:41,540 - bedrock_agentcore.gateway - INFO - ✓ EZ Auth setup complete!
✓ Authorization server created

Step 2: Creating Gateway...
2026-06-06 19:08:41,544 - bedrock_agentcore.gateway - INFO - Role not provided, creating an execution role to use
2026-06-06 19:08:45,659 - bedrock_agentcore.gateway - INFO - ✓ Successfully created execution role for Gateway
2026-06-06 19:08:45,660 - bedrock_agentcore.gateway - INFO - Creating Gateway
2026-06-06 19:08:46,765 - bedrock_agentcore.gateway - INFO - ✓ Created Gateway: arn:aws:bedrock-agentcore:us-east-1:917561075114:gateway/testgateway2c01624b-gdutqlvkte
2026-06-06 19:08:46,765 - bedrock_agentcore.gateway - INFO -   Gateway URL: https://testgateway2c01624b-gdutqlvkte.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
2026-06-06 19:08:46,765 - bedrock_agentcore.gateway - INFO -   Waiting for Gateway to be ready...
2026-06-06 19:08:49,238 - bedrock_agentcore.gateway - INFO -
✅Gateway is ready
ObservabilityDeliveryManager initialized for region: us-east-1, account: 917561075114
Created log group: /aws/vendedlogs/bedrock-agentcore/gateway/APPLICATION_LOGS/testgateway2c01624b-gdutqlvkte
✅ Logs delivery enabled for gateway/testgateway2c01624b-gdutqlvkte
✅ Traces delivery enabled for gateway/testgateway2c01624b-gdutqlvkte
Observability enabled for gateway/testgateway2c01624b-gdutqlvkte - logs: True, traces: True
2026-06-06 19:08:53,366 - bedrock_agentcore.gateway - INFO - ✅ Observability enabled for gateway testgateway2c01624b-gdutqlvkte
2026-06-06 19:08:53,366 - bedrock_agentcore.gateway - INFO -    Log group: /aws/vendedlogs/bedrock-agentcore/gateway/APPLICATION_LOGS/testgateway2c01624b-gdutqlvkte
✓ Gateway created: https://testgateway2c01624b-gdutqlvkte.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
  Gateway ID:  testgateway2c01624b-gdutqlvkte
  Role ARN:    arn:aws:iam::917561075114:role/AgentCoreGatewayExecutionRole

Step 3: Fixing IAM trust policy...
2026-06-06 19:08:55,465 - bedrock_agentcore.gateway - INFO - ✓ Fixed IAM permissions for Gateway
⏳ Waiting 30s for IAM propagation...
✓ Trust policy fixed

Step 4: Adding Policy Engine permissions to Gateway service role...
  ✅ AgentCoreGatewayExecutionRole に PolicyEnginePermissions を追加
⏳ Waiting 30s for IAM propagation...
✓ Policy Engine permissions added

Step 5: Creating Lambda function with refund tool...
2026-06-06 19:09:58,449 - bedrock_agentcore.gateway - INFO - ✓ Created Lambda execution role: arn:aws:iam::917561075114:role/RefundTool-1780740456Role
⏳ IAM role not ready to be asssumed (attempt 1/4), retrying in 5s... Role: arn:aws:iam::917561075114:role/RefundTool-1780740456Role
⏳ IAM role not ready to be asssumed (attempt 2/4), retrying in 10s... Role: arn:aws:iam::917561075114:role/RefundTool-1780740456Role
2026-06-06 19:10:15,635 - bedrock_agentcore.gateway - INFO - ✓ Created Lambda function: arn:aws:lambda:us-east-1:917561075114:function:RefundTool-1780740456
2026-06-06 19:10:15,635 - bedrock_agentcore.gateway - INFO - ✓ Attaching access policy to: arn:aws:lambda:us-east-1:917561075114:function:RefundTool-1780740456 for arn:aws:iam::917561075114:role/AgentCoreGatewayExecutionRole
2026-06-06 19:10:15,913 - bedrock_agentcore.gateway - INFO - ✓ Attached permissions for role invocation: arn:aws:lambda:us-east-1:917561075114:function:RefundTool-1780740456
✓ Lambda function created

⏳ Waiting 10s for Lambda propagation
Step 6: Adding Lambda target...
2026-06-06 19:10:25,915 - bedrock_agentcore.gateway - INFO - Creating Target
2026-06-06 19:10:25,916 - bedrock_agentcore.gateway - INFO - {'gatewayIdentifier': 'testgateway2c01624b-gdutqlvkte', 'name': 'RefundTarget', 'targetConfiguration': {'mcp': {'lambda': {'lambdaArn': 'arn:aws:lambda:us-east-1:917561075114:function:RefundTool-1780740456', 'toolSchema': {'inlinePayload': [{'name': 'process_refund', 'description': 'Process a customer refund', 'inputSchema': {'type': 'object', 'properties': {'amount': {'type': 'integer', 'description': 'Refund amount in dollars'}}, 'required': ['amount']}}]}}}}, 'credentialProviderConfigurations': [{'credentialProviderType': 'GATEWAY_IAM_ROLE'}]}
2026-06-06 19:10:26,430 - bedrock_agentcore.gateway - INFO - ✓ Added target successfully (ID: TQ3TUF8HB7)
2026-06-06 19:10:26,430 - bedrock_agentcore.gateway - INFO -   Waiting for target to be ready...
2026-06-06 19:10:28,980 - bedrock_agentcore.gateway - INFO -
✅Target is ready
✓ Lambda target added

Step 7: Creating Policy Engine (RefundPolicyEngine1780740456)...
2026-06-06 19:10:28,980 - bedrock_agentcore.policy - INFO - Creating or getting Policy Engine: RefundPolicyEngine1780740456
2026-06-06 19:10:29,966 - bedrock_agentcore.policy - INFO - Creating Policy Engine: RefundPolicyEngine1780740456
2026-06-06 19:10:30,271 - bedrock_agentcore.policy - INFO - ✓ Policy Engine creation initiated: arn:aws:bedrock-agentcore:us-east-1:917561075114:policy-engine/RefundPolicyEngine1780740456-nuc7t6vwmg
2026-06-06 19:10:30,271 - bedrock_agentcore.policy - INFO - Waiting for Policy Engine to be active...
2026-06-06 19:10:32,737 - bedrock_agentcore.policy - INFO - ✓ Policy Engine is active
✓ Policy Engine: RefundPolicyEngine1780740456-nuc7t6vwmg

Step 8: Creating Cedar policy (refund_limit_policy1780740456)...
  Cedar: permit(principal, action == AgentCore::Action::"RefundTarget___process_refund", resource == AgentCore::Gateway::"arn:aws:bedrock-agentcore:us-east-1:917561075114:gateway/testgateway2c01624b-gdutqlvkte") when { context.input.amount < 1000 };

2026-06-06 19:10:32,737 - bedrock_agentcore.policy - INFO - Creating or getting Policy: refund_limit_policy1780740456
2026-06-06 19:10:32,984 - bedrock_agentcore.policy - INFO - Creating Policy: refund_limit_policy1780740456
2026-06-06 19:10:33,426 - bedrock_agentcore.policy - INFO - ✓ Policy creation initiated: arn:aws:bedrock-agentcore:us-east-1:917561075114:policy-engine/RefundPolicyEngine1780740456-nuc7t6vwmg/policy/refund_limit_policy1780740456-vy6akqrjd9
2026-06-06 19:10:33,426 - bedrock_agentcore.policy - INFO - Waiting for Policy to be active...
2026-06-06 19:10:40,351 - bedrock_agentcore.policy - INFO - ✓ Policy is active
✓ Policy: refund_limit_policy1780740456-vy6akqrjd9

Step 9: Attaching Policy Engine to Gateway (ENFORCE mode)...
2026-06-06 19:10:40,351 - bedrock_agentcore.gateway - INFO - Attaching policy engine to gateway
2026-06-06 19:10:40,351 - bedrock_agentcore.gateway - INFO - Updating gateway testgateway2c01624b-gdutqlvkte
2026-06-06 19:10:40,564 - bedrock_agentcore.gateway - INFO -   Policy Engine ARN: arn:aws:bedrock-agentcore:us-east-1:917561075114:policy-engine/RefundPolicyEngine1780740456-nuc7t6vwmg
2026-06-06 19:10:40,564 - bedrock_agentcore.gateway - INFO -   Mode: ENFORCE
2026-06-06 19:10:41,046 - bedrock_agentcore.gateway - INFO - ✓ Gateway update initiated
2026-06-06 19:10:41,046 - bedrock_agentcore.gateway - INFO -   Waiting for gateway to be ready...
2026-06-06 19:10:43,496 - bedrock_agentcore.gateway - INFO - ✓ Gateway update complete
✓ Policy Engine attached to Gateway

============================================================
✅ Setup complete!
Gateway URL:      https://testgateway2c01624b-gdutqlvkte.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
Policy Engine ID: RefundPolicyEngine1780740456-nuc7t6vwmg
Policy ID:        refund_limit_policy1780740456-vy6akqrjd9
Refund limit:     $1000

Configuration saved to: config.json

Next step: Run 'python test_policy.py'
============================================================

Step 1: OAuth認可サーバーの作成

Amazon Cognitoを使ったOAuth 2.0認可サーバーを構築します。GatewayはOAuth認証により「誰がリクエストを送ってきたか」を検証します。このステップでは Cognito User Pool・カスタムドメイン・リソースサーバー・クライアントが自動的に作成されます。DNSの伝播に約1分かかるため、ここが最も時間のかかるステップです。

Step 2: Gatewayの作成

AgentCore Gatewayを作成します。GatewayはエージェントとToolの間に立つMCPエンドポイントです。自動的にGateway専用のIAM実行ロール(AgentCoreGatewayExecutionRole)も作成されます。X-RayトレースとCloudWatch Logsによるオブザーバビリティが自動で有効化されます。

Step 3: IAM Trust Policyの修正

toolkitの fix_iam_permissions() を呼び出し、作成したGateway実行ロールの信頼ポリシーを更新します。bedrock-agentcore.amazonaws.com サービスプリンシパルがこのロールを引き受けられるよう設定します。IAMの変更は即時反映されないため、30秒間待機します。

Step 4: Policy Engine用のIAM権限追加

Step 3はTrust Policyの修正のみであり、Policy Engine評価に必要な権限は含まれていません。そのためこのステップで Gateway実行ロールに以下の3つの重要な権限を追加します。

権限

用途

bedrock-agentcore:GetPolicyEngine

ポリシーエンジンの設定を取得

bedrock-agentcore:AuthorizeAction

Cedar評価による認可判定

bedrock-agentcore:PartiallyAuthorizeActions

呼び出し可能なツール一覧の取得

Step 5: Lambda関数(返金ツール)の作成

ツールの実体となるLambda関数を作成します。この関数は amount(返金額)を受け取り、処理結果を返すシンプルな返金処理ツールです。Lambda専用のIAMロールも同時に作成され、Gateway実行ロールからの呼び出しを許可するリソースベースポリシーが自動でアタッチされます。

Step 6: Lambda TargetのGatewayへの登録

作成したLambda関数をGatewayのTargetとして登録します。このとき、ツールのスキーマ(process_refund というツール名、amount という整数パラメータが必須)を定義します。エージェントはこのスキーマを参照してツールを呼び出す方法を理解します。TargetのIDとツール名の組み合わせが後のCedarポリシーのアクション名(RefundTarget___process_refund)になります。

Step 7: Policy Engineの作成

Cedarポリシーを管理するPolicy Engineを作成します。Policy EngineはGatewayに関連付けることで、全リクエストの認可判定を行う「審判所」として機能します。数秒でACTIVE状態になります。

Step 8: Cedarポリシーの作成

実際の認可ルールをCedar言語で定義してPolicy Engineに登録します。今回作成したポリシーは以下の意味を持ちます。

permit(principal,                                    # 誰でも(全ユーザー)
  action == "RefundTarget___process_refund",         # 返金ツールを実行するとき
  resource == "arn:...gateway/testgateway..."        # このGateway経由の場合
) when {
  context.input.amount < 1000                        # 返金額が$1000未満のときのみ許可
};

CedarはデフォルトでDeny(拒否)のため、このpermitルールに合致しないリクエストは全て自動的に拒否されます。ポリシーはバックグラウンドで検証されてACTIVEになります。

Step 9: Policy EngineをGatewayに関連付け

作成したPolicy EngineをGatewayに紐付けます。ENFORCEモードを指定することで、以降のリクエストは全てPolicy Engineによる認可チェックを経てからTargetに転送されます。なおLOG_ONLYモードも存在し、本番運用前の動作確認に役立ちます。


最終的に作成されたリソース一覧

リソース

名前/ID

Cognito User Pool

us-east-1_nkOV8aCkj

AgentCore Gateway

testgateway2c01624b-gdutqlvkte

IAM実行ロール

AgentCoreGatewayExecutionRole

Lambda関数

RefundTool-1780740456

Policy Engine

RefundPolicyEngine1780740456-nuc7t6vwmg

Cedar Policy

refund_limit_policy1780740456-vy6akqrjd9

テスト実行

次に以下のテスト用スクリプトを実行します。

"""
Test Policy Engine with direct HTTP calls to Gateway
Run after setup: python test_policy.py
"""

import json
import sys
import requests
from bedrock_agentcore_starter_toolkit.operations.gateway.client import GatewayClient


def test_refund(gateway_url, bearer_token, amount):
    response = requests.post(
        gateway_url,
        headers={
            "Content-Type": "application/json",
            "Authorization": f"Bearer {bearer_token}",
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/call",
            "params": {
                "name": "RefundTarget___process_refund",
                "arguments": {"amount": amount}
            },
        },
    )
    print(f"Status Code: {response.status_code}")
    print(f"Response Body: {json.dumps(response.json(), indent=2)}")
    return response


def main():
    print("=" * 60)
    print("🧪 Testing Policy Engine")
    print("=" * 60 + "\n")

    try:
        with open("config.json", "r") as f:
            config = json.load(f)
    except FileNotFoundError:
        print("❌ Error: config.json not found!")
        print("Please run 'python setup_policy.py' first.")
        sys.exit(1)

    gateway_url  = config["gateway_url"]
    refund_limit = config["refund_limit"]
    region       = config["region"]
    client_info  = config["client_info"]

    print(f"Gateway: {gateway_url}")
    print(f"Refund limit: ${refund_limit}\n")

    print("🔑 Getting access token...")
    gateway_client = GatewayClient(region_name=region)
    token = gateway_client.get_access_token_for_cognito(client_info)
    print("✅ Token obtained\n")

    # Test 1: $500(許可されるはず)
    print("📝 Test 1: Refund $500 (Expected: ALLOW)")
    print("-" * 40)
    test_refund(gateway_url, token, 500)
    print()

    # Test 2: $2000(拒否されるはず)
    print("📝 Test 2: Refund $2000 (Expected: DENY)")
    print("-" * 40)
    test_refund(gateway_url, token, 2000)
    print()

    print("=" * 60)
    print("✅ Testing complete!")
    print("=" * 60)


if __name__ == "__main__":
    main()

python test.py で実行すると以下の結果となります。

python test_policy.py
============================================================
🧪 Testing Policy Engine
============================================================

Gateway: https://testgateway2c01624b-gdutqlvkte.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
Refund limit: $1000

🔑 Getting access token...
2026-06-06 19:12:36,265 - bedrock_agentcore.gateway - INFO - Fetching test token from Cognito...
2026-06-06 19:12:36,265 - bedrock_agentcore.gateway - INFO -   Attempting to connect to token endpoint: https://agentcore-bffc410d.auth.us-east-1.amazoncognito.com/oauth2/token
2026-06-06 19:12:37,388 - bedrock_agentcore.gateway - INFO - ✓ Got test token successfully
✅ Token obtained

📝 Test 1: Refund $500 (Expected: ALLOW)
----------------------------------------
Status Code: 200
Response Body: {
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": false,
    "content": [
      {
        "type": "text",
        "text": "{\"status\":\"success\",\"message\":\"Refund of $500 processed successfully\",\"amount\":500}"
      }
    ]
  }
}

📝 Test 2: Refund $2000 (Expected: DENY)
----------------------------------------
Status Code: 200
Response Body: {
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32002,
    "message": "Tool Execution Denied: Tool call not allowed due to policy enforcement [No policy applies to the request (denied by default).]"
  }
}

============================================================
✅ Testing complete!
============================================================

テスト1は500ドルの返金を指定しているため成功しますが、テスト2は2000ドルを返金しようとしているため失敗しています。テスト2は 返金の判断を行うLambda関数を呼び出す前に自発的に処理が停止 されています。PolicyではCedarにより以下のルールがセットされています。

permit (
    principal,
    action == AgentCore::Action::"RefundTarget___process_refund",
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:us-east-1:917561075114:gateway/testgateway2c01624b-gdutqlvkte"
)
when { context.input.amount < 1000 };

Lambda関数は以下の通り失敗処理を実装していないことがわかります。

def lambda_handler(event, context):
    amount = event.get('amount', 0)
    return {
        "status": "success",
        "message": f"Refund of ${amount} processed successfully",
        "amount": amount
    }

今回セットアップ時にObservabilityを有効化していますのでCloudWatch logs のロググループが作成されています。出力されている以下のロググループをCloudWatch logs で見てみます。

Created log group: /aws/vendedlogs/bedrock-agentcore/gateway/APPLICATION_LOGS/testgateway2c01624b-gdutqlvkte

以下のログが出力されています。

{
    "resource_arn": "arn:aws:bedrock-agentcore:us-east-1:917561075114:gateway/testgateway2c01624b-gdutqlvkte",
    "event_timestamp": 1780740760252,
    "body": {
        "isError": true,
        "log": "Tool Execution Denied: Tool call not allowed due to policy enforcement [No policy applies to the request (denied by default).]",
        "id": "1"
    },
    "account_id": "917561075114",
    "request_id": "55d2867f-dd78-413c-8fcb-e63b33582d10",
    "trace_id": "6a23f29854658c295df5ae7032641030",
    "span_id": "fc8588b5ef6a8c8b",
    "resource": {
        "attributes": {
            "service.name": "testgateway2c01624b-gdutqlvkte",
            "cloud.resource_id": "arn:aws:bedrock-agentcore:us-east-1:917561075114:gateway/testgateway2c01624b-gdutqlvkte",
            "cloud.platform": "aws_bedrock_agentcore"
        }
    },
    "attributes": {
        "aws.request.id": "55d2867f-dd78-413c-8fcb-e63b33582d10",
        "aws.account.id": "917561075114",
        "aws.resource.type": "AWS::BedrockAgentCore::Gateway"
    },
    "timeUnixNano": 1780740760252064788,
    "severityNumber": 17,
    "severityText": "ERROR",
    "traceId": "6a23f29854658c295df5ae7032641030",
    "spanId": "fc8588b5ef6a8c8b"
}

Written by
編集部

亀田 治伸

Kameda Harunobu

  • Facebook->
  • X->
  • GitHub->

Share

Facebook->X->
Back
to list
<-