Agent Runtime 用のプロジェクト間の Agent Registry を使用した Agent Gateway の一元化されたガバナンス

1. はじめに

企業組織が生成 AI を導入するにつれて、アーキテクチャはスタンドアロンのモノリシック chatbot から分散型マルチエージェント システム(エージェント間 / A2A)へと急速に進化しています。このような最新のトポロジでは、高レベルのオーケストレーター エージェントが、独立した Google Cloud プロジェクト全体で、タスクを専門のドメイン ワーカー エージェント、Model Context Protocol(MCP)ツールサーバー、バックエンド エンタープライズ データベースに委任することで、複雑なビジネス ワークフローを調整します。

ただし、マルチエージェント システムを大規模に運用すると、セキュリティ、ガバナンス、運用上の重大な課題が生じます。

  • シャドー エージェントとツールの拡散: 開発チームが集中型カタログなしで分離されたプロジェクトにエージェントをデプロイすると、組織はどのツールとサブエージェントが存在するかを把握できなくなります。
  • 監視されていないクロス プロジェクトの下り(外向き): エージェントに直接の検査されていないネットワーク ルートを許可すると、データ漏洩のリスクが生じ、セキュリティ境界がバイパスされます。
  • 脆弱なハードコードされた統合: 下流のエージェント URL と Reasoning Engine ID をハードコードすると、アップグレードや再デプロイ中に壊れる脆弱な依存関係が作成されます。
  • 最小権限の ID の欠如: 共有サービス アカウントは、個々のエージェント インスタンス レベルで暗号による否認防止を提供できません。

これらの課題を解決するために、Gemini Enterprise Agent Platform は、次の 4 つのコア要素で構成される統合されたガバナンスと接続制御プレーンを提供します。

  1. Agent Gateway(networkservices.googleapis.com: 管理対象のリージョン ネットワークとポリシー適用プロキシ。AGENT_TO_ANYWHERE 下り(外向き)モードで動作し、エージェントの下り(外向き)トラフィックをインターセプトし、認可評価をセキュリティ拡張機能に委任し、プロジェクト境界間でリクエストをルーティングします。
  2. Agent Registry(agentregistry.googleapis.com: 単一のエンタープライズ サービス カタログ。組織全体の利用可能なすべてのツール、MCP サーバー、ピア エージェントの一元化された審査済みのディレクトリを提供し、ハードコードされたエンドポイントなしで動的なランタイム自動検出を可能にします。
  3. エージェント ID と IAP v2 のガバナンス(iap.googleapis.comiam.googleapis.com: 暗号化 ID とアクセス フレームワーク。実行エージェントは、一意の証明済みの SPIFFE マシン URN(principal://...)を受け取ります。アウトバウンド エグレスは、一元化された IAM 統合アクセス ポリシー(UAP / IAP v2)に対して評価され、リッチな Common Expression Language(CEL)カタログ条件(destination.agent_registry.*)を使用してユニバーサル権限 iap.googleapis.com/resources.egressViaIAP が検証されます。
  4. エージェント ランタイム(推論エンジン): Python ベースのエージェント型アプリケーション向けのフルマネージドのサーバーレス実行プラットフォーム。中央ゲートウェイへのネイティブ構成バインディング(agent_gateway_config)を備えています。

Codelab のビジネス シナリオ: 複数のプロジェクトでの食品と飲料の購入

この Codelab では、3 つの異なる Google Cloud プロジェクトにまたがる実際のマルチプロジェクト購入エコシステムを構築して管理します。

  • Central Governance Project (PROJECT_GOVERNANCE): 中央 IT と SecOps が所有し、Central Agent Gateway、Central Agent Registry、IAM 統合アクセス ポリシーをホストします。
  • コンシューマー オーケストレーター プロジェクト(PROJECT_CONCIERGE): 調達チームが所有し、ベンダーを動的に検出し、顧客の注文を転送する Purchasing Concierge Agent をホストします。
  • ドメイン ベンダー プロジェクト(PROJECT_SELLERS): 外部ベンダーまたは部門ベンダーが所有し、ハンバーガー販売エージェントピザ販売エージェントをホストします。

figure1

図 1. マルチプロジェクトの集中型ガバナンス アーキテクチャ

クロス プロジェクトの集中型ガバナンスが必要な理由

大規模なエンタープライズ組織では、プロダクト チームとデータ サイエンス グループが、数十もの独立した Google Cloud プロジェクトにわたって AI エージェントを構築します。各チームがツールの登録、下り(外向き)ネットワーク ルート、セキュリティ ガードレールを直接制御できるようにすると、未審査のツールが乱立し、DLP ポリシーの不整合、VPC 下り(外向き)のモニタリング不足、監査ログの断片化が生じます。

プロジェクト間の集中型ガバナンスでは、ポリシーの作成とエージェントの実行が分離されます。

  • 中央 IT と SecOps は、単一の一元化されたガバナンス プロジェクト内でセキュリティ ポリシーを作成し、ツールを審査し、下り(外向き)をモニタリングします。
  • プロダクト チームとアプリケーション チームは、独立したAgent Runtime プロジェクトでビジネス ロジックにのみ集中し、ローカル VPC、相互接続、断片化された Policy Engine の運用オーバーヘッドなしで、中央ゲートウェイに直接バインドします。

figure2

図 2. 3 層のクロス プロジェクト ガバナンス アーキテクチャと境界

統合アクセス ポリシーの 2 階層 ID スコープ モデル

エージェントが Central Agent Gateway を介して通信する場合、Identity-Aware Proxy(IAP v2)は、呼び出し元のエージェント ID(ランタイム コンテナに自動的に発行される暗号で証明された SPIFFE ベースの ID)に基づいて、グローバル IAM アクセス ポリシーに対するアクセスを評価します。

  • 階層 1: ベースライン Google Cloud API(ルール 1 の principalSet:// による粗粒度): スポーク プロジェクトのすべてのエージェント ランタイムが、検出、トークン生成、推論のために標準の Google API(aiplatformiamcredentialstelemetryagentregistry)にアクセスできるようにするプロジェクト全体の下り(外向き)承認。
  • 階層 2: ビジネスツールと A2A サービス(ルール 2 と 3 の principal:// を介してきめ細かい制御): 個々の Reasoning Engine インスタンスにバインドされた厳格な最小権限アクセス。特定の登録済み Agent Registry サービス(destination.agent_registry.agent.name)を対象とする Common Expression Language(CEL)条件で適用されます。

構築内容

  • PROJECT_GOVERNANCE の一元化された Agent Gateway(centralized-agw
  • 厳格な ENFORCE モードの IAP v2 認可サービス拡張機能と Authz ポリシー(failOpen: false
  • 基盤となる IAM 統合アクセス ポリシー(uap-rules.json)とプロジェクト ポリシー バインディング
  • プロジェクト間のサービス エージェントの IAM 権限(ar_agw_cross_project_sa
  • 共有の中央 Google Cloud Storage(GCS)ステージング バケット
  • PROJECT_SELLERS のハンバーガーとピザの販売者のエージェントを分離する
  • PROJECT_CONCIERGE で動的 REST 自動検出を使用して Concierge Agent を購入する
  • プロジェクト間の mTLS URL を使用した Central Agent Registry のサービス登録
  • ライブ検証と Cloud Logging 監査による動的 IAP v2 下り(外向き)ポリシーの更新

figure3

図 3. 詳細な実装手順

学習内容

  • 一元化されたゲートウェイのプロジェクト間のサービス エージェントの IAM 権限を構成する方法
  • マルチプロジェクト環境で中央の Agent Gateway を介して Agent Runtime の下り(外向き)をルーティングする方法
  • サービス拡張機能(iapPolicyVersion: "V2")を使用して Agent Gateway の認可を Identity-Aware Proxy(IAP v2)に委任する方法
  • 登録済みの Agent Registry の宛先(destination.agent_registry.*)を制御する Common Expression Language(CEL)ルールを使用して IAM 統合アクセス ポリシー(UAP)を作成してバインドする方法
  • Agent Registry に対して実行時の自動検出を使用して、ハードコードされたエージェント ID と URL を排除する方法
  • 実際の境界ゼロトラスト ブロック(HTTP 403 Forbidden)をテストし、Cloud Logging でライブ ポリシーの更新を確認する方法

必要なもの

  • 課金が有効になっている 3 つの Google Cloud プロジェクト:
    • PROJECT_GOVERNANCE: 一元管理、ゲートウェイ、レジストリ、IAM アクセス ポリシー
    • PROJECT_CONCIERGE: 購入コンシェルジュ オーケストレーター エージェント
    • PROJECT_SELLERS: ハンバーガーとピザのスペシャリスト販売エージェント
  • 3 つのプロジェクトすべてで roles/owner 権限または管理者権限を持つ IAM ユーザーまたはサービス アカウント
  • Google Cloud 組織(SPIFFE 信頼ドメイン マッピング用)
  • gcloud CLI、python(3.11 以降)、uv がインストールされている Google Cloud Shell またはローカルマシン

これで導入部分は終了です。次は、セットアップと環境のセクションに進みます。

2. セットアップ

このアーキテクチャは 3 つの異なる Google Cloud プロジェクトにまたがっていますが、PROJECT_GOVERNANCE に設定された単一の Cloud Shell ターミナルから、ターミナル デプロイ コマンド、リポジトリのダウンロード、ステージング オペレーションの 100% を実行できます。すべてのデプロイ スクリプトと gcloud コマンドは、CLI フラグ(--project)を介して適切な宛先プロジェクトを明示的にターゲットにします。

まず、Google Cloud プロジェクトのコマンドラインにアクセスします。

プロジェクト コンテキストを設定する

# set terminal project context to Central Governance Project
gcloud config set project SET_YOUR_GOVERNANCE_PROJECT_ID_HERE
# login to gcloud cli
gcloud auth login
# login for application default credentials
gcloud auth application-default login
# update gcloud components
gcloud components update --quiet

シェル環境変数を設定する

プロジェクト固有の識別子を入力します。

# 1. Project Identifiers
export PROJECT_GOVERNANCE="SET_YOUR_GOVERNANCE_PROJECT_ID_HERE"
export PROJECT_CONCIERGE="SET_YOUR_CONCIERGE_PROJECT_ID_HERE"
export PROJECT_SELLERS="SET_YOUR_SELLERS_PROJECT_ID_HERE"

これらのシェル変数は自動的に導出されます。

# 2. Regional & Gateway Settings
export REGION="us-central1"
export AGW_NAME="centralized-agw"
export UAP_POLICY_NAME="uap-policy-${AGW_NAME}"
export UAP_BINDING_NAME="uap-binding-${AGW_NAME}"

# 3. Retrieve Project Numbers
export PROJECT_NUMBER_GOVERNANCE=$(gcloud projects describe ${PROJECT_GOVERNANCE} --format="value(projectNumber)")
export PROJECT_NUMBER_CONCIERGE=$(gcloud projects describe ${PROJECT_CONCIERGE} --format="value(projectNumber)")
export PROJECT_NUMBER_SELLERS=$(gcloud projects describe ${PROJECT_SELLERS} --format="value(projectNumber)")

# 4. Obtain Organization ID
export ORG_ID=$(gcloud projects get-ancestors ${PROJECT_GOVERNANCE} --format="value(id, type)" | grep organization | awk '{print $1}')

# 5. Set Application Default Credentials (ADC) Quota Project
gcloud auth application-default set-quota-project ${PROJECT_GOVERNANCE}

echo "Governance Project: ${PROJECT_GOVERNANCE} (${PROJECT_NUMBER_GOVERNANCE})"
echo "Concierge Project:  ${PROJECT_CONCIERGE} (${PROJECT_NUMBER_CONCIERGE})"
echo "Sellers Project:    ${PROJECT_SELLERS} (${PROJECT_NUMBER_SELLERS})"
echo "Organization ID:    ${ORG_ID}"
echo "UAP Policy Name:    ${UAP_POLICY_NAME}"
echo "UAP Binding Name:   ${UAP_BINDING_NAME}"

構成ファイル用のローカル ディレクトリを作成する

# create config folder
mkdir -p cfg

統合アクセス ポリシーのアクセス ポリシー管理者ロールを割り当てる

# grant Access Policy Admin and Project IAM Admin to current user in Governance Project
for ROLE in "roles/iam.accessPolicyAdmin" "roles/resourcemanager.projectIamAdmin"; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="user:$(gcloud config get-value account)" \
    --role="${ROLE}" \
    --condition=None
done

IAP v2 の Cloud Audit データアクセス ログを有効にする

デフォルトでは、意図しないストレージ費用が発生しないように、Google Cloud はデータアクセス監査ログを無効にしています。IAP v2 は認可決定(granted=truegranted=false)をデータアクセス監査ログとして出力するため、PROJECT_GOVERNANCEiap.googleapis.com に対して ADMIN_READDATA_READDATA_WRITE のロギングを有効にします。

# 1. export current IAM policy for PROJECT_GOVERNANCE
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} \
  --format=json > cfg/gov_iam_policy.json
# 2. append auditConfigs for iap.googleapis.com
python3 -c "
import json
with open('cfg/gov_iam_policy.json') as f:
    policy = json.load(f)
audit_configs = [c for c in policy.get('auditConfigs', []) if c.get('service') != 'iap.googleapis.com']
audit_configs.append({
    'service': 'iap.googleapis.com',
    'auditLogConfigs': [
        {'logType': 'ADMIN_READ'},
        {'logType': 'DATA_READ'},
        {'logType': 'DATA_WRITE'}
    ]
})
policy['auditConfigs'] = audit_configs
with open('cfg/gov_iam_policy.json', 'w') as f:
    json.dump(policy, f, indent=2)
"
# 3. apply updated policy
gcloud projects set-iam-policy ${PROJECT_GOVERNANCE} cfg/gov_iam_policy.json
# 4. verify auditConfigs applied
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} --format="yaml(auditConfigs)"

必要な Google Cloud API を有効にする

# enable google apis (agent platform & security bundle, part 1)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    agentregistry.googleapis.com \
    aiplatform.googleapis.com \
    apphub.googleapis.com \
    apptopology.googleapis.com \
    cloudapiregistry.googleapis.com \
    cloudtrace.googleapis.com \
    compute.googleapis.com \
    dataform.googleapis.com \
    iam.googleapis.com \
    agentidentity.googleapis.com \
    iap.googleapis.com \
    logging.googleapis.com \
    modelarmor.googleapis.com \
    monitoring.googleapis.com \
    networksecurity.googleapis.com \
    networkservices.googleapis.com \
    notebooks.googleapis.com \
    observability.googleapis.com \
    --project=${PROJ}
done
# enable google apis (agent platform bundle, part 2)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    securitycenter.googleapis.com \
    saasservicemgmt.googleapis.com \
    storage.googleapis.com \
    telemetry.googleapis.com \
    texttospeech.googleapis.com \
    --project=${PROJ}
done
# enable google apis (foundational & agent runtime build bundle, part 3)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    artifactregistry.googleapis.com \
    cloudbuild.googleapis.com \
    cloudresourcemanager.googleapis.com \
    iamcredentials.googleapis.com \
    serviceusage.googleapis.com \
    run.googleapis.com \
    orgpolicy.googleapis.com \
    --project=${PROJ}
done

すべてのプロジェクトで API の有効化を検証する

3 つのプロジェクト(PROJECT_GOVERNANCEPROJECT_CONCIERGEPROJECT_SELLERS)すべてで同じ API が有効になっていることを確認すると、運用の一貫性が確立され、ランタイム トークンの生成の失敗、スキーマ カタログの作成エラー、テレメトリーのドロップアウトを防ぐことができます。

Cloud Shell で次の検証スクリプトを実行して、3 つのプロジェクトすべてで API のパリティを確認します。

# validate that all required APIs are enabled across all 3 projects
python3 - << 'EOF'
import subprocess
import os
import sys

REQUIRED_APIS = [
    "agentregistry.googleapis.com",
    "aiplatform.googleapis.com",
    "apphub.googleapis.com",
    "apptopology.googleapis.com",
    "cloudapiregistry.googleapis.com",
    "cloudtrace.googleapis.com",
    "compute.googleapis.com",
    "dataform.googleapis.com",
    "iam.googleapis.com",
    "agentidentity.googleapis.com",
    "iap.googleapis.com",
    "logging.googleapis.com",
    "modelarmor.googleapis.com",
    "monitoring.googleapis.com",
    "networksecurity.googleapis.com",
    "networkservices.googleapis.com",
    "notebooks.googleapis.com",
    "observability.googleapis.com",
    "securitycenter.googleapis.com",
    "saasservicemgmt.googleapis.com",
    "storage.googleapis.com",
    "telemetry.googleapis.com",
    "texttospeech.googleapis.com",
    "artifactregistry.googleapis.com",
    "cloudbuild.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "iamcredentials.googleapis.com",
    "serviceusage.googleapis.com",
    "run.googleapis.com",
    "orgpolicy.googleapis.com"
]

projects = {
    "GOVERNANCE": os.environ.get("PROJECT_GOVERNANCE", ""),
    "CONCIERGE": os.environ.get("PROJECT_CONCIERGE", ""),
    "SELLERS": os.environ.get("PROJECT_SELLERS", "")
}

enabled = {}
for role, proj in projects.items():
    if not proj:
        print(f"Error: Environment variable for {role} is not set.")
        sys.exit(1)
    res = subprocess.run(
        ["gcloud", "services", "list", "--enabled", f"--project={proj}", "--format=value(config.name)"],
        capture_output=True, text=True, check=True
    )
    enabled[role] = set(res.stdout.strip().splitlines())

print(f"\n{'API Name':<36} | {'GOVERNANCE':<12} | {'CONCIERGE':<12} | {'SELLERS':<12}")
print("-" * 78)

all_synced = True
for api in REQUIRED_APIS:
    g_status = "ENABLED" if api in enabled["GOVERNANCE"] else "MISSING"
    c_status = "ENABLED" if api in enabled["CONCIERGE"] else "MISSING"
    s_status = "ENABLED" if api in enabled["SELLERS"] else "MISSING"
    if "MISSING" in (g_status, c_status, s_status):
        all_synced = False
    print(f"{api:<36} | {g_status:<12} | {c_status:<12} | {s_status:<12}")

print("-" * 78)
if all_synced:
    print("✅ All 29 required APIs are ENABLED and synchronized across all three projects.\n")
else:
    print("❌ Discrepancies detected. Please re-run the enablement commands for missing services.\n")
    sys.exit(1)
EOF

検証出力の例:

すべての API が有効になっているはずです。

✅ All 30 required APIs are ENABLED and synchronized across all three projects.

組織のポリシーを構成する

デフォルトの Google Cloud 組織のポリシーは、IAM v3 アクセス ポリシー バインディングをリソース(constraints/iam.managed.disableAccessPolicyBinding)に制限する制約を適用します。

enforce: false を明示的に許可に設定して、プロジェクト レベルで継承された組織のポリシーの制限をオーバーライドします。

# disable iam v3 constraint (allow v3 access policies)
gcloud org-policies set-policy /dev/stdin << EOF
name: projects/${PROJECT_NUMBER_GOVERNANCE}/policies/iam.managed.disableAccessPolicyBinding
spec:
  rules:
  - enforce: false
EOF
# verify org policy constraints on project
gcloud org-policies describe iam.managed.disableAccessPolicyBinding \
  --project=${PROJECT_GOVERNANCE} --effective

これで設定は完了です。次のセクションでは、コア Google API を登録する方法について説明します。

3. Agent Registry

Core Google APIs エンドポイント サービスを登録する

Agent Gateway では、agent_gateway_config で構成されたエージェントがコア Google Cloud バックエンド サービス(aiplatform、IAM 認証情報、テレメトリーなど)に下り(外向き)トラフィックを安全にルーティングできるように、Google API URL を中央エージェント レジストリに登録する必要があります。

Agent Registry で core-gapi-services を作成する

# register core google api endpoints in agent registry with standard and :443 port variants
gcloud agent-registry services create core-gapi-services \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="gapi.core.services" \
  --description="Core Google Cloud APIs and Service Endpoints" \
  --endpoint-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://telemetry.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://telemetry.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.mtls.googleapis.com:443

Core APIs エンドポイント リソース ID を取得する

# capture the underlying Agent Registry endpoint ID
export ENDPOINT_ID=$(gcloud agent-registry services describe core-gapi-services \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --format="value(registryResource)" | awk -F'/' '{print $NF}')
echo "Core APIs Endpoint ID: ${ENDPOINT_ID}"

エージェント ID の principalSetprincipal の違いについて

Google Cloud IAM と Gemini Enterprise Agent Platform では、実行中のエージェント コンテナに発行されたマシン ID は、Identity-Aware Proxy(IAP v2)によって評価される暗号で証明された SPIFFE URN を使用します。IAM 統合アクセス ポリシーを構成するときに、特定の単一の principal または属性ベースの principalSet をターゲットにできます。

ディメンション

principal://(単一マシン ID)

principalSet://(属性ベースのグループ)

IAM 構文

principal://...

principalSet://...

粒度

きめ細かい(インスタンス レベル): 単一の特定の Reasoning Engine コンテナ インスタンスを識別します。

粗粒度(プロジェクト レベル): 共通のプロジェクト属性を共有するすべての推論エンジンを識別します。

URN パターン

principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER}/locations/${REGION}/reasoningEngines/${ENGINE_ID}

principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER}

Agent Platform のユースケース

Tier 2(ビジネスツールと A2A): 特定のオーケストレーター エージェントがターゲット ドメインツール(購入コンシェルジュ $\rightarrow$ バーガー販売者など)を呼び出すことを承認します。

Tier 1(基盤となるインフラストラクチャ): プロジェクト内のすべてのエージェントに Google Cloud APIs(core-gapi-services)への下り(外向き)アクセス権を付与します。

ライフサイクルの影響

エージェントが削除されて再作成された場合、新しいエンジン ID には更新された IAM ポリシー バインディングが必要です。

追加の IAM 更新を行わなくても、そのプロジェクトに新しくデプロイされたエージェントに自動的に適用されます。

統合アクセス ポリシー(UAP / IAP v2)による宣言型ガバナンス

以前の IAP v1 では、下り(外向き)ポリシーは gcloud beta iap web add-iam-policy-binding を使用して個々の Agent Registry リソースに直接関連付けられていました。IAP v2 と統合アクセス ポリシーでは、リソースごとのバインディングが廃止され、単一の一元化された IAM アクセス ポリシーcfg/uap-rules.json)が使用されます。

core-gapi-services の基本的な下り(外向き)承認は、セクション 5 の統合アクセス ポリシーでルール 1 として構成されます。これにより、すべてのエージェント コンテナにデプロイ前に基本的な下り(外向き)ルートが確立されます。

プリンシパル ID と Workload Identity のメカニズムの詳細については、以下をご覧ください。

これで、コア API エンドポイントの登録は完了です。次は、Agent Gateway を一元的にデプロイするセクションに進みます。

4. Agent Gateway

一元化された Agent Gateway をデプロイする

$PROJECT_GOVERNANCE プロジェクト内の AGENT_TO_ANYWHERE 上り(内向き)モードで一元化された Agent Gateway(centralized-agw)をデプロイします。

Gateway 構成マニフェストを定義する

下り(外向き)トラフィック ガバナンス用の cfg/${AGW_NAME}.yaml を作成します。

# generate agent gateway config yaml
cat > cfg/${AGW_NAME}.yaml << EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJECT_GOVERNANCE}/locations/${REGION}"
EOF

Agent Gateway の構成をインポートする

# import and create agent gateway
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Agent Gateway の詳細を確認する

# show agent gateway status
gcloud network-services agent-gateways describe ${AGW_NAME} \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

出力例:

agentGatewayCard:
  mtlsEndpoint: projects/${AGW_TP_ID}/regions/us-central1/serviceAttachments/unitkind1-swp-mtls-psc-sa
  rootCertificates:
  - |
    -----BEGIN CERTIFICATE-----
    MIIDwzCCAqugAwIBAgITNQuWGopdOZaHdcK7r7AYFhonqDANBgkqhkiG9w0BAQsF
    ...
    -----END CERTIFICATE-----
  serviceExtensionsServiceAccount: service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com
createTime: 'YYYY-MM-DDT12:34:56.789098765Z'
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
name: projects/${PROJECT_GOVERNANCE}/locations/us-central1/agentGateways/centralized-agw
protocols:
- MCP
registries:
- //agentregistry.googleapis.com/projects/${PROJECT_GOVERNANCE}/locations/us-central1
updateTime: 'YYYY-MM-DDT12:34:56.789098765Z'

これでゲートウェイのデプロイは完了です。次の認可の構成セクションに進みます。

5. 認可

Agent Gateway の認可と基盤となる UAP を構成する

Agent Gateway は、Identity-Aware Proxy(IAP v2)統合アクセス ポリシー(UAP)と統合された認可ポリシーnetworksecurity.authzPolicies)を使用して、アウトバウンド ツールとエージェントのトラフィックを保護し、管理します。

認可アーキテクチャの概要

figure4

図 4. 認可アーキテクチャの概要

認可アーキテクチャは、相互に接続された次の 3 つのレイヤで構成されています。

  1. IAP サービス拡張機能(authzExtension: 厳格な境界ゼロトラストの適用用に service: iap.googleapis.commetadata: iapPolicyVersion: "V2"failOpen: false で構成されたリージョン リソース。
  2. Gateway 認可ポリシー(authzPolicy: policyProfile: REQUEST_AUTHZaction: CUSTOM を使用して Agent Gateway をターゲットとするリージョン リソース。認可チェックを IAP Authz 拡張機能に転送します。
  3. IAM 統合アクセス ポリシーとバインディング(accessPolicypolicyBinding: IAP によって評価されるグローバル IAM v3 リソース。呼び出し元の SPIFFE ID と CEL カタログ条件に対して、ユニバーサル権限 iap.googleapis.com/resources.egressViaIAP を検証します。

ステップ 1: IAP v2 Authz 拡張機能を作成してインポートする

厳密な ENFORCE モードで iapPolicyVersion: "V2"failOpen: false を使用して、Service Extension マニフェストを作成します。

# create authz extension config file in ENFORCE mode
cat > cfg/${AGW_NAME}-svc-ext-authz-iap.yaml << EOF
name: ${AGW_NAME}-svc-ext-authz-iap
service: iap.googleapis.com
failOpen: false
timeout: 1s
metadata:
  iapPolicyVersion: "V2"
EOF

Authz Extension をインポートします。

# import IAP v2 authz extension
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap \
  --source=cfg/${AGW_NAME}-svc-ext-authz-iap.yaml \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Authz 拡張機能がアクティブであることを確認します。

# describe authz extension
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

出力例:

createTime: 'YYYY-MM-DDT12:34:56.789098765Z'
failOpen: false
metadata:
  iapPolicyVersion: V2
name: projects/${PROJECT_GOVERNANCE}/locations/us-central1/authzExtensions/centralized-agw-svc-ext-authz-iap
service: iap.googleapis.com
timeout: 1s

ステップ 2: Gateway 認可ポリシーを作成してインポートする

Agent Gateway に接続し、リクエストの検証を IAP Authz 拡張機能に委任する認可ポリシー構成を作成します。

# create authz policy manifest
cat > cfg/${AGW_NAME}-authz-policy-profile-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-profile-iap
target:
  resources:
    - "projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJECT_GOVERNANCE}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap"
EOF

認可ポリシーをインポートします。

# import authz policy
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-profile-iap \
  --source=cfg/${AGW_NAME}-authz-policy-profile-iap.yaml \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

有効な認可ポリシーを確認します。

# describe authz policy
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

ステップ 3: 最初の統合アクセス ポリシーを作成する(ルール 1: コア Google API)

ルール 1 で 3 つのプロジェクト principalSetcore-gapi-services に到達することを承認する cfg/uap-rules.json を作成します。

# create initial unified access policy rules manifest
cat > cfg/uap-rules.json << EOF
[
  {
    "description": "Rule 1: Allow agent runtimes across all 3 projects to reach Core Google APIs",
    "effect": "ALLOW",
    "principals": [
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'ENDPOINT' && ( \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/core-gapi-services' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}')"
      }
    }
  }
]
EOF

ステップ 4: IAM アクセス ポリシーを作成してバインドする

グローバル IAM アクセス ポリシーを作成します。

# create global IAM access policy
gcloud iam access-policies create ${UAP_POLICY_NAME} \
  --details-rules=cfg/uap-rules.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

アクセス ポリシーを PROJECT_GOVERNANCE にバインドします。

# bind access policy to governance project
gcloud iam policy-bindings create ${UAP_BINDING_NAME} \
  --policy="projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/${UAP_POLICY_NAME}" \
  --target-resource="//cloudresourcemanager.googleapis.com/projects/${PROJECT_GOVERNANCE}" \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

ポリシー バインディングが有効であることを確認します。

# verify policy binding
gcloud iam policy-bindings describe ${UAP_BINDING_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

出力例:

name: projects/${PROJECT_GOVERNANCE}/locations/global/policyBindings/uap-binding-centralized-agw
policy: projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/uap-policy-centralized-agw
policyKind: ACCESS_POLICY
target:
  resource: //cloudresourcemanager.googleapis.com/projects/${PROJECT_GOVERNANCE}

基盤となる Google Cloud API 下り(外向き)は、厳格な ENFORCE モードで 3 つのプロジェクトすべてで安全に承認されるようになりました。

これで、Gateway の認可設定は完了です。次の クロス プロジェクト IAM 権限を構成するセクションに進みます。

6. プロジェクト間の IAM

プロジェクト横断的な IAM 権限を構成する

このマルチプロジェクト トポロジでは、Agent Runtime はスポーク プロジェクト(PROJECT_CONCIERGEPROJECT_SELLERS)に存在し、Central Agent Gateway と Agent Registry は PROJECT_GOVERNANCE に存在します。

Google Cloud プロジェクトは分離されたセキュリティ境界であるため、プロジェクト間のアクセスは、次の 2 つの運用レイヤで明示的に付与する必要があります。

  1. コントロール プレーン(デプロイ時): --agent-gateway-config で構成されたエージェント コンテナをデプロイする場合、スポーク プロジェクトのAgent Runtime Service Agentservice-@gcp-sa-aiplatform.iam.gserviceaccount.com)がコンテナを中央ゲートウェイに接続する必要があります。PROJECT_GOVERNANCEnetworkservices.agentGateways.usegetoperations.get を付与する最小限のカスタムロール(ar_agw_cross_project_sa)を作成します。
  2. データプレーン(ランタイム実行):
    • カタログ検出: スポーク ID は、ターゲット エージェント エンドポイントを動的に解決するために PROJECT_GOVERNANCEroles/agentregistry.viewer を必要とします。
    • ターゲットの呼び出し: コンシェルジュ エージェントは、販売者の推論エンジンに対してクエリを実行するために、PROJECT_SELLERSroles/aiplatform.user を必要とします。

PROJECT_GOVERNANCE でカスタム IAM ロールを作成する

# create custom role in central governance project
gcloud iam roles create ar_agw_cross_project_sa \
  --project=${PROJECT_GOVERNANCE} \
  --title="Runtime Agent Gateway Cross-Project SA" \
  --description="Custom role for cross-project service agents to access Central Agent Gateway" \
  --permissions="networkservices.agentGateways.get,networkservices.agentGateways.use,networkservices.operations.get" \
  --stage="GA"

Agent Runtime サービス エージェントにカスタムロールを割り当てる

# 1. ensure aiplatform service identities are provisioned across all projects
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud beta services identity create --service=aiplatform.googleapis.com --project=${PROJ}
done
# 2. derive aiplatform service agent emails
export CONCIERGE_AI_SA="service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com"
export CONCIERGE_RE_SA="service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
export CONCIERGE_COMPUTE_SA="${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com"

export SELLERS_AI_SA="service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform.iam.gserviceaccount.com"
export SELLERS_RE_SA="service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
export SELLERS_COMPUTE_SA="${PROJECT_NUMBER_SELLERS}-compute@developer.gserviceaccount.com"
# 3. grant custom role & network viewer to Concierge and Sellers Service Agents
for SA in ${CONCIERGE_AI_SA} ${SELLERS_AI_SA}; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="projects/${PROJECT_GOVERNANCE}/roles/ar_agw_cross_project_sa" \
    --condition=None

  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/networkservices.viewer" \
    --condition=None
done
# 4. grant agent registry viewer on Governance Project for dynamic autodiscovery
for MEMBER in "serviceAccount:${CONCIERGE_AI_SA}" "serviceAccount:${CONCIERGE_RE_SA}" "serviceAccount:${CONCIERGE_COMPUTE_SA}" "serviceAccount:${SELLERS_AI_SA}" "serviceAccount:${SELLERS_RE_SA}" "serviceAccount:${SELLERS_COMPUTE_SA}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="${MEMBER}" \
    --role="roles/agentregistry.viewer" \
    --condition=None
done
# 5. grant agent project viewer on Governance Project for dynamic autodiscovery
for SA in ${CONCIERGE_COMPUTE_SA} ${CONCIERGE_AI_SA}; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/viewer" \
    --condition=None
done
# 6. grant aitplatform user on Sellers project to Concierge for cross-project A2A invocation
for MEMBER in "serviceAccount:${CONCIERGE_AI_SA}" "serviceAccount:${CONCIERGE_RE_SA}" "serviceAccount:${CONCIERGE_COMPUTE_SA}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}"; do
  gcloud projects add-iam-policy-binding ${PROJECT_SELLERS} \
    --member="${MEMBER}" \
    --role="roles/aiplatform.user" \
    --condition=None
done

これで、プロジェクト間の IAM の設定は完了です。次は、販売者エージェントとコンシェルジュ エージェントをデプロイするセクションに進みます。

7. Agent Runtime

販売担当者とコンシェルジュ エージェントをデプロイする

この Codelab で使用するマルチエージェント アプリケーションのコードベースとデプロイ スクリプトは、リモートの Google Cloud GitHub リポジトリで管理されています。次の手順では、リポジトリをローカルにクローンし、必要なファイルを現在の作業ディレクトリ構造にコピーし、一時ファイルをクリーンアップして、uv で依存関係をインストールします。

リモート アーティファクトを取得する

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_cuj_arun_multiproject
# copy multi-agent application files to current working directory
cp -r temp_agw_cuj_arun_multiproject/codelabs/agw-cuj-arun-multiproject ./cross-project-multiagent
# remove temporary directory
rm -rf temp_agw_cuj_arun_multiproject
# install dependencies
uv sync --directory ./cross-project-multiagent

共有の中央ステージング バケットを作成する

# create shared central staging bucket
gcloud storage buckets create gs://${PROJECT_GOVERNANCE}-shared-staging \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION}
# grant cross-project read/write access to runtime service agents
gcloud storage buckets add-iam-policy-binding gs://${PROJECT_GOVERNANCE}-shared-staging \
  --member="serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin"

gcloud storage buckets add-iam-policy-binding gs://${PROJECT_GOVERNANCE}-shared-staging \
  --member="serviceAccount:service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin"

クロス プロジェクト Agent Gateway バインディングの仕組み

このステップでは、Seller Agent をスポーク プロジェクト(PROJECT_SELLERS)にデプロイし、PROJECT_GOVERNANCE の Central Agent Gateway を介して外向きをルーティングするように構成します。

# !-- for example purposes -- NOT a command to execute --!
# snippet from deploy_burger.py
burger_config = {
    "staging_bucket": staging_bucket_uri,
    "gcs_dir_name": "burger_agent",
    "display_name": "burger-seller-agent-adk",
    "identity_type": "AGENT_IDENTITY",
    "agent_gateway_config": {
        "agent_to_anywhere_config": {
            "agent_gateway": f"projects/{args.governance_project}/locations/{args.region}/agentGateways/{args.gateway}"
        }
    },
}
deployed_burger = client.agent_engines.create(agent=burger_playground, config=burger_config)

ルール 1 は統合アクセス ポリシーで先に確立されているため、Google Cloud APIs へのコンテナ初期化リクエストはゲートウェイを介して中断されることなく許可されます。

ハンバーガーとピザの販売エージェントを PROJECT_SELLERS にデプロイする

# 1. deploy Burger Seller Agent to PROJECT_SELLERS
uv run --directory ./cross-project-multiagent python deploy_burger.py \
  --project=${PROJECT_SELLERS} \
  --region=${REGION} \
  --governance-project=${PROJECT_GOVERNANCE} \
  --gateway=projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}
# 2. deploy Pizza Seller Agent to PROJECT_SELLERS
uv run --directory ./cross-project-multiagent python deploy_pizza.py \
  --project=${PROJECT_SELLERS} \
  --region=${REGION} \
  --governance-project=${PROJECT_GOVERNANCE} \
  --gateway=projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}

販売者ゲートウェイのルーティングを検証する

# retrieve deployed seller reasoning engine IDs
export BURGER_ENGINE_ID=$(grep BURGER_SELLER_AGENT_ID cross-project-multiagent/burger_agent.env | awk -F'/' '{print $NF}')
export PIZZA_ENGINE_ID=$(grep PIZZA_SELLER_AGENT_ID cross-project-multiagent/pizza_agent.env | awk -F'/' '{print $NF}')

echo "Burger Engine ID: ${BURGER_ENGINE_ID}"
echo "Pizza Engine ID:  ${PIZZA_ENGINE_ID}"
# inspect runtime configuration for both Seller Agents
for ENGINE_ID in ${BURGER_ENGINE_ID} ${PIZZA_ENGINE_ID}; do
  curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/${REGION}/reasoningEngines/${ENGINE_ID}" \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    -H "Content-Type: application/json" \
    | jq '{displayName: .displayName, identityType: .spec.identityType, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'
done

購買コンシェルジュ エージェントを PROJECT_CONCIERGE にデプロイする

# deploy Purchasing Concierge to PROJECT_CONCIERGE
uv run --directory ./cross-project-multiagent python deploy_concierge_adk.py \
  --project=${PROJECT_CONCIERGE} \
  --region=${REGION} \
  --staging-bucket=gs://${PROJECT_GOVERNANCE}-shared-staging \
  --gateway-name=${AGW_NAME} \
  --gateway-project=${PROJECT_GOVERNANCE}

購入ゲートウェイのルーティングを検証する

# retrieve Concierge engine ID
export CONCIERGE_ENGINE_ID=$(grep CONCIERGE_AGENT_ID cross-project-multiagent/concierge_agent.env | awk -F'/' '{print $NF}')
echo "Concierge Engine ID: ${CONCIERGE_ENGINE_ID}"
# inspect runtime configuration for Purchasing Concierge
curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" \
  | jq '{displayName: .displayName, identityType: .spec.identityType, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'

出力には、Concierge エージェントのランタイム ID とプロジェクト、Governance プロジェクトの Agent Gateway へのバインディングが表示されます。

{
  "displayName": "purchasing-concierge-adk",
  "identityType": "AGENT_IDENTITY",
  "effectiveIdentity": "agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_CONCIERGE}/locations/us-central1/reasoningEngines/${CONCIERGE_ENGINE_ID}",
  "agentGatewayConfig": {
    "agentToAnywhereConfig": {
      "agentGateway": "projects/${PROJECT_GOVERNANCE}/locations/us-central1/agentGateways/centralized-agw"
    }
  }
}

これでエージェントのデプロイは完了です。次の中央 Agent Registry にエージェントを登録するセクションに進みます。

8. プロジェクト間レジストリ

Agent Registry にエージェントを登録する

クロス プロジェクトのリージョン mTLS エンドポイントと数値のプロジェクト番号を使用して、PROJECT_GOVERNANCE の中央 Agent Registry に 3 つのエージェントすべてを登録します。

Agent Registry にサービスを非 A2A エージェントとして登録する

# 1. register Burger Seller Agent
gcloud agent-registry services create burger-seller-agent \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Burger Seller Agent" \
  --description="Specialist agent that sells burgers and fries" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query
# 2. register Pizza Seller Agent
gcloud agent-registry services create pizza-seller-agent \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Pizza Seller Agent" \
  --description="Specialist agent that sells pizzas and pasta" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query
# 3. register Purchasing Concierge Agent
gcloud agent-registry services create purchasing-concierge-adk \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Purchasing Concierge Agent" \
  --description="Orchestrator concierge agent that routes purchasing requests" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query

基盤となる Agent Registry ID を取得する

# capture underlying Agent Registry Agent UUIDs
export BURGER_AGENT_ID=$(gcloud agent-registry services describe burger-seller-agent --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')
export PIZZA_AGENT_ID=$(gcloud agent-registry services describe pizza-seller-agent --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')
export CONCIERGE_AGENT_ID=$(gcloud agent-registry services describe purchasing-concierge-adk --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')

echo "Burger Agent ID:    ${BURGER_AGENT_ID}"
echo "Pizza Agent ID:     ${PIZZA_AGENT_ID}"
echo "Concierge Agent ID: ${CONCIERGE_AGENT_ID}"

これでレジストリの構成は完了です。次の A2A 下り(外向き)ポリシーを構成するセクションに進みます。

9. UAP ポリシー

統合アクセス ポリシーで A2A 下り(外向き)ポリシーを構成する

厳密な ENFORCE モードの Agent Gateway のデフォルト拒否アーキテクチャの場合:

  1. ルール 1(ベースライン Google Cloud API): 3 つのプロジェクトすべてで、エージェント コンテナが core-gapi-services に到達できるようにします。
  2. ルール 2(Burger Seller Agent: ALLOW): Purchasing Concierge Agent インスタンスが Burger Seller Agent を呼び出すことを許可します。
  3. Pizza Seller Agent(デフォルトで拒否): ポリシー ルールから意図的に除外されています。ENFORCE モード(failOpen: false)では、コンシェルジュがピザ販売者を呼び出そうとすると、ゲートウェイ境界で HTTP 403 Forbidden により直ちに終了します。

コンシェルジュ エージェントの ID を作成する

# formulate the exact SPIFFE machine identity for the Concierge Agent
export CONCIERGE_SPIFFE_PRINCIPAL="principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}"
echo "Concierge SPIFFE Principal: ${CONCIERGE_SPIFFE_PRINCIPAL}"

ルール 1 と 2 でマニフェストを更新する

新しい cfg/uap-rules-update-2.json を作成して、ルール 1(Core API)と ルール 2(Burger Seller Agent)を含めます。

# create addendum to update policy manifest with Rule 2 for Burger Agent
cat > cfg/uap-rules-update-2.json << EOF
[
  {
    "description": "Rule 2: Allow Purchasing Concierge to invoke Burger Seller Agent via Central Gateway",
    "effect": "ALLOW",
    "principals": [
      "${CONCIERGE_SPIFFE_PRINCIPAL}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'AGENT' && ( \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/burger-seller-agent' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/${BURGER_AGENT_ID}' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/agents/${BURGER_AGENT_ID}')"
      }
    }
  }
]
EOF

更新されたアクセス ポリシーを適用する

# update IAM access policy with Burger rule
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --add-details-rules=cfg/uap-rules-update-2.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

IAM アクセス ポリシーの詳細を確認する

# inspect updated access policy
gcloud iam access-policies describe ${UAP_POLICY_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

出力例:

details:
  rules:
  - conditions:
      iap.googleapis.com:
        expression: destination.is_registered == true && destination.agent_registry.resource_type
          == 'ENDPOINT' && (destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/us-central1/endpoints/core-gapi-services'
          || destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/us-central1/endpoints/${ENDPOINT_ID}')
    description: 'Rule 1: Allow agent runtimes across all 3 projects to reach Core
      Google APIs'
    effect: ALLOW
    operation:
      permissions:
      - iap.googleapis.com/resources.egressViaIAP
    principals:
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}
  - conditions:
      iap.googleapis.com:
        expression: (destination.is_registered == true) && (destination.agent_registry.resource_type
          == 'AGENT') && (destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/us-central1/agents/burger-seller-agent'
          || destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/us-central1/agents/${BURGER_AGENT_ID}')
    description: 'Rule 2: Allow Purchasing Concierge to invoke Burger Seller Agent
      via Central Gateway'
    effect: ALLOW
    operation:
      permissions:
      - iap.googleapis.com/resources.egressViaIAP
    principals:
    - principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}/locations/us-central1/reasoningEngines/${CONCIERGE_ENGINE_ID}
name: projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/uap-policy-centralized-agw

これでポリシーの設定は完了です。次は、ガバナンス ポリシーをテストして検証するセクションに進みます。

10. ポリシーを確認する

Cloud Logging を使用してガバナンス ポリシーをテストして検証する

このセクションでは、Agent Runtime AI Playground でプロジェクト間のエージェント間(A2A)インタラクションをテストし、厳格な ENFORCE モードで実際の境界 HTTP 403 Forbidden ブロックを観察し、統合アクセス ポリシーをライブで変更して、即時注文承認を検証します。

ステップ 1: PROJECT_CONCIERGE で Agent Runtime AI プレイグラウンドを開く

  1. Google Cloud Console を開きます。
  2. 上部のプロジェクト セレクタ バーで、PROJECT_CONCIERGE に切り替えます。
  3. ナビゲーション メニューで、[Agent Platform] > [エージェント] > [デプロイ] に移動します。
  4. purchasing-concierge-adk をクリックします。
  5. [Playground] を選択して、画面の右側にインタラクティブ チャット インターフェースを開きます。

ステップ 2: ハンバーガーの注文をテストする(ルール 2 に一致 -> 200 OK)

Playground のチャット ウィンドウで、次の注文プロンプトを送信します。

I would like 10 Classic Cheeseburgers. Place this order now.

確認の返信が必要な場合は、次の返信を送信します。

Confirmed, please place the order.

または、Cloud Shell / ターミナルからプログラムでテストします。

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input={'message': 'I would like 22 Spicy Cajun Burgers please. Place this order now.'})
print(response)
"

確認応答が必要な場合は、次のコマンドを使用します。

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

舞台裏で発生している状況:

  1. 動的検出: セッションの起動時に、購買コンシェルジュは PROJECT_GOVERNANCE の Agent Registry(ルール 1 で承認された Agent Gateway を介して core-gapi-services 経由)にクエリを実行して、burger-seller-agent のリージョン mTLS エンドポイントを検出しました。
  2. インテント解決と A2A 呼び出し: Purchasing Concierge 内の Gemini は、食品注文のインテントを解析し、https://${REGION}-aiplatform.mtls.googleapis.com/.../reasoningEngines/${BURGER_ENGINE_ID} へのアウトバウンド RPC を介して Burger Seller Agent を呼び出します。
  3. Gateway のインターセプトと SPIFFE の伝播: 下り(外向き)トラフィックは agent_gateway_config によってキャプチャされ、PROJECT_GOVERNANCE の中央 Agent Gateway に転送されます。このとき、コンシェルジュの暗号 SPIFFE ID(principal://...)が伝播されます。
  4. IAP v2 ポリシーの評価: Central Agent Gateway が IAP 認可拡張機能(authzExtension)を呼び出します。IAP v2 は、IAM 統合アクセス ポリシーのルール 2 を評価します。呼び出し元が ${CONCIERGE_SPIFFE_PRINCIPAL} に一致し、ターゲットが burger-seller-agent に一致するため、IAP は ALLOWgranted: true)を返します。
  5. クロス プロジェクト実行: Agent Gateway は、承認されたリクエストをクロス プロジェクトで PROJECT_SELLERS にプロキシします。ここで、Burger Seller Reasoning Engine が注文を処理して確認を返します。

想定されるレスポンス:

Your order for 10 Classic Cheeseburger(s) has been placed!
Here is a summary of your order:
- 10x Classic Cheeseburger @ IDR 85,000/each = IDR 850,000

Total: IDR 850,000
Your Order ID is: e8f9c732-f347-4cc4-acff-cfe09ccbeddd

ステップ 3: Agent Gateway と IAP v2 の監査ログを調べる(HTTP 200 / ALLOWED)

PROJECT_GOVERNANCE で Agent Gateway リクエストログをクエリします。

# query Agent Gateway logs for successful 200 OK requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND jsonPayload.authzPolicyInfo.result=\"ALLOWED\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=10 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

ログには、両方のスポーク プロジェクト(PROJECT_CONCIERGEPROJECT_SELLERS)から発信されるアウトバウンド トラフィックがキャプチャされ、Gemini 推論呼び出し(generateContent)、Cloud Trace テレメトリー(/v1/traces)、IAM 認証情報のルックアップの出力フィールドが含まれている必要があります。これらは、ルール 1(core-gapi-services)によって透過的にインターセプトされ、承認されます。

IAP v2 Cloud Audit データアクセス ログをクエリして、ポリシー バージョン POLICY_VERSION_V2 を確認します。

# query IAP v2 audit logs with shortened principal and resource fields
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/cloudaudit.googleapis.com%2Fdata_access\"
  AND protoPayload.serviceName=\"iap.googleapis.com\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    protoPayload.authenticationInfo.principalSubject.sub('\.global\..*\/reasoningEngines\/', '.[...]/reasoningEngines/'):label=CALLER,
    protoPayload.authorizationInfo[0].granted:label=GRANTED,
    protoPayload.metadata.destination.agent_registry.resource_type.basename():label=TYPE,
    protoPayload.metadata.destination.agent_registry.resource_id.basename():label=RESOURCE_ID,
    protoPayload.authorizationInfo[0].permission.basename():label=PERMISSION
  )"

出力例:

TIME      CALLER                                                            GRANTED  TYPE      RESOURCE_ID     PERMISSION
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${BURGER_ENGINE_ID}     True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${BURGER_ENGINE_ID}     True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP

ステップ 4: ピザの注文をテストする(デフォルト拒否 -> HTTP 403 Forbidden が適用される)

同じ Playground のチャット ウィンドウで、次のピザ注文のプロンプトを送信します。

I would like 10 BBQ Chicken Pizzas. Place this order now.

確認の返信が必要な場合は、次の返信を送信します。

Confirmed, please place the order.

または、Cloud Shell / ターミナルからプログラムでテストします。

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='I would like 8 Hawaiian pizzas, please. Place this order now.')
print(response)
"

確認応答が必要な場合は、次のコマンドを使用します。

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

想定されるレスポンス:

I apologize, but I am unable to process that request at the moment. It seems
there was an issue connecting to the pizza seller agent. Please try again later.

舞台裏で発生している状況:

  1. 動的検出: Purchasing Concierge は、起動時に Central Agent Registry から pizza-seller-agent エンドポイントを解決しました。
  2. インテント解決と A2A 呼び出し: Purchasing Concierge 内の Gemini が、PROJECT_SELLERS の Pizza Seller エンドポイントにピザの注文リクエストをディスパッチしようとします。
  3. Gateway Interception: アウトバウンド RPC は agent_gateway_config によってキャプチャされ、Central Agent Gateway に転送されます。
  4. IAP v2 ポリシーの評価(デフォルト拒否): 中央 Agent Gateway が IAP v2 を呼び出します。pizza-seller-agent に一致する統合アクセス ポリシーにルールがないため、IAP は DENYgranted: false)を返します。
  5. 厳格な境界ブロック: Authz 拡張機能が自動適用モードfailOpen: false)であるため、中央 Agent Gateway はアウトバウンド接続を直ちに終了し、HTTP 403 Forbidden を返します。トラフィックはゲートウェイから離れることはなく、PROJECT_SELLERS に到達することもありません。

ステップ 5: Agent Gateway ログでブロックされたリクエスト(HTTP 403 / DENIED)を確認する

# query Agent Gateway logs for blocked 403 requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND httpRequest.status=403
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

拒否されたログ出力の例:

TIME      METHOD  STATUS  AUTHZ   URL
HH:MM:SS  POST    403     DENIED  https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/us-central1/reasoningEngines/${PIZZA_ENGINE_ID}:query

拒否された決定について IAP v2 データアクセス監査ログをクエリします。

# query IAP v2 audit logs with shortened principal and resource fields
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/cloudaudit.googleapis.com%2Fdata_access\"
  AND protoPayload.serviceName=\"iap.googleapis.com\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    protoPayload.authenticationInfo.principalSubject.sub('\.global\..*\/reasoningEngines\/', '.[...]/reasoningEngines/'):label=CALLER,
    protoPayload.authorizationInfo[0].granted:label=GRANTED,
    protoPayload.metadata.destination.agent_registry.resource_type.basename():label=TYPE,
    protoPayload.metadata.destination.agent_registry.resource_id.basename():label=RESOURCE_ID,
    protoPayload.authorizationInfo[0].permission.basename():label=PERMISSION
  )"

拒否された監査ログ出力の例:

TIME      CALLER                                                            GRANTED  TYPE      RESOURCE_ID     PERMISSION
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  False    Agent     ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP

ステップ 6: Pizza エージェントに下り(外向き)アクセス権を動的に付与する

ルール 1(コア API)、ルール 2(ハンバーガー販売エージェント)、ルール 3(ピザ販売エージェント)を含む新しい cfg/uap-rules-update-3.json を作成します。

# create addendum to update policy manifest with Rule 3 for Pizza Agent
cat > cfg/uap-rules-update-3.json << EOF
[
  {
    "description": "Rule 3: Allow Purchasing Concierge to invoke Pizza Seller Agent via Central Gateway",
    "effect": "ALLOW",
    "principals": [
      "${CONCIERGE_SPIFFE_PRINCIPAL}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'AGENT' && ( \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/pizza-seller-agent' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/${PIZZA_AGENT_ID}' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/agents/${PIZZA_AGENT_ID}')"
      }
    }
  }
]
EOF

ポリシーの更新をライブで適用します。

# update IAM access policy with Pizza rule
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --add-details-rules=cfg/uap-rules-update-3.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

ステップ 7: Pizza エージェントに再度クエリを実行する(すぐに 200 OK 成功)

Playground のチャット ウィンドウで、ピザの注文プロンプトを再送信します。

I would like 10 BBQ Chicken Pizzas. Place this order now.

確認の返信が必要な場合は、次の返信を送信します。

Confirmed, please place the order.

または、Cloud Shell / ターミナルからプログラムでテストします。

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='I would like 11 Veggie pizzas, please. Place this order now.')
print(response)
"

確認応答が必要な場合は、次のコマンドを使用します。

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

想定されるレスポンス:

Your order has been placed!

**Order ID:** 8d6c13d7-31dc-4d80-b6a7-80d1e50b6411

**Order Details:**
*   10 x BBQ Chicken Pizza @ IDR 130,000 each = IDR 1,300,000

**Total: IDR 1,300,000**

舞台裏で発生している状況:

  1. 動的ポリシーの更新: IAM 統合アクセス ポリシーを更新すると、IAP 評価エンジンで直ちに有効になります。ダウンタイムは発生せず、コンテナを再デプロイする必要もありません。
  2. A2A 呼び出し: Concierge は、Central Agent Gateway を介してリクエストをディスパッチします。
  3. IAP v2 ポリシー評価(承認): IAP v2 はルール 3 に一致し、呼び出し元の ID とターゲット CEL 式を検証して、ALLOWgranted: true)を返します。
  4. クロス プロジェクト実行: Central Agent Gateway は、承認されたトラフィックを PROJECT_SELLERS にプロキシします。ここで、Pizza Seller が注文を処理します。

ステップ 8: Agent Gateway ログでピザ リクエストが許可されていることを確認する

# query Agent Gateway logs for successful 200 OK requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND jsonPayload.authzPolicyInfo.result=\"ALLOWED\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=10 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

付与されたログの出力例:

TIME      METHOD  STATUS  AUTHZ    URL
HH:MM:SS  POST    200     ALLOWED  https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/us-central1/publishers/google/models/gemini-2.5-flash:generateContent
HH:MM:SS  POST    200     ALLOWED  https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/us-central1/reasoningEngines/${PIZZA_ENGINE_ID}:query

これでテストと検証は完了です。次のクリーンアップ セクションに進みます。

11. クリーンアップ

この Codelab で使用したリソースについて、Google Cloud アカウントに課金されないようにするには、依存関係の逆順で厳密に分解手順を実行します。

1. Reasoning Engine のデプロイをクリーンアップする

両方のランタイム プロジェクトで付属の cleanup_old_deployments.py スクリプトを実行して、推論エンジンを削除し、長時間実行オペレーションを待ちます。

# delete all Reasoning Engines deployed in Concierge and Sellers projects
uv run --directory ./cross-project-multiagent python cleanup_old_deployments.py --project=${PROJECT_CONCIERGE} --region=${REGION}
uv run --directory ./cross-project-multiagent python cleanup_old_deployments.py --project=${PROJECT_SELLERS} --region=${REGION}

また、次のコマンドを使用して、Reasoning Engine をインラインで一覧表示して削除することもできます。

uv run --directory ./cross-project-multiagent python -c '
import vertexai
import os
from vertexai.preview import reasoning_engines

region = os.environ.get("REGION", "us-central1")
for proj in [os.environ.get("PROJECT_CONCIERGE"), os.environ.get("PROJECT_SELLERS")]:
    if not proj:
        continue
    print(f"Cleaning reasoning engines in {proj}...")
    vertexai.init(project=proj, location=region)
    for eng in reasoning_engines.ReasoningEngine.list():
        print(f"  Deleting {eng.resource_name} ({eng.display_name})...")
        eng.delete()
'

2. Agent Registry サービスを削除する

# delete agent registry services in Central Governance Project
for SERVICE in burger-seller-agent pizza-seller-agent purchasing-concierge-adk core-gapi-services; do
  gcloud agent-registry services delete ${SERVICE} \
    --project=${PROJECT_GOVERNANCE} \
    --location=${REGION} \
    --quiet || true
done

3. IAM 統合アクセス ポリシー バインディングとアクセス ポリシーを削除する

# 1. delete IAM policy binding
gcloud -q iam policy-bindings delete ${UAP_BINDING_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global || true

# 2. delete IAM access policy
gcloud -q iam access-policies delete ${UAP_POLICY_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global || true

4. Agent Gateway とセキュリティ ポリシーを削除する

# 1. delete authorization policy
gcloud beta network-security authz-policies delete ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE} --quiet || true

# 2. delete authorization extension
gcloud service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE} --quiet || true
# 3. delete agent gateway
gcloud network-services agent-gateways delete ${AGW_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} --quiet || true

5. プロジェクト間の IAM バインディングとカスタムロールを削除する

# 1. remove custom role and network viewer bindings for spoke service agents
for NUM in "${PROJECT_NUMBER_CONCIERGE}" "${PROJECT_NUMBER_SELLERS}"; do
  SA="service-${NUM}@gcp-sa-aiplatform.iam.gserviceaccount.com"
  
  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="projects/${PROJECT_GOVERNANCE}/roles/ar_agw_cross_project_sa" --quiet || true

  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/networkservices.viewer" --quiet || true
done
# 2. remove registry viewer permissions across both spoke projects
for NUM in "${PROJECT_NUMBER_CONCIERGE}" "${PROJECT_NUMBER_SELLERS}"; do
  for MEMBER in \
    "serviceAccount:service-${NUM}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
    "serviceAccount:service-${NUM}@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    "serviceAccount:${NUM}-compute@developer.gserviceaccount.com" \
    "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${NUM}"; do
      gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
        --member="${MEMBER}" \
        --role="roles/agentregistry.viewer" --quiet || true
  done
done
# 3. remove project viewer permissions
for MEMBER in \
  "serviceAccount:${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com" \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com"; do
    gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
      --member="${MEMBER}" \
      --role="roles/viewer" --quiet || true
done
# 4. remove spoke-to-spoke delegation in Sellers project
for MEMBER in \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
  "serviceAccount:${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com" \
  "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}"; do
    gcloud projects remove-iam-policy-binding ${PROJECT_SELLERS} \
      --member="${MEMBER}" \
      --role="roles/aiplatform.user" --quiet || true
done
# 5. delete custom IAM role after all bindings have been unlinked
gcloud iam roles delete ar_agw_cross_project_sa \
  --project=${PROJECT_GOVERNANCE} --quiet || true

設定フェーズで roles/iam.accessPolicyAdminroles/resourcemanager.projectIamAdmin を割り当てた場合は、アクティブなユーザー アカウントから削除して最小権限を復元します。

# 6. remove Access Policy Admin and Project IAM Admin roles from user
for ROLE in "roles/iam.accessPolicyAdmin" "roles/resourcemanager.projectIamAdmin"; do
  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="user:$(gcloud config get-value account)" \
    --role="${ROLE}" \
    --condition=None --quiet || true
done

6. 監査データ ロギングと組織のポリシーの制約を元に戻す

# 1. Export current Central Governance IAM policy
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} --format=json > cfg/gov_iam_policy.json
# 2. Filter out iap.googleapis.com from auditConfigs
python3 -c "
import json
with open('cfg/gov_iam_policy.json') as f:
    policy = json.load(f)

if 'auditConfigs' in policy:
    # Remove iap.googleapis.com; if nothing else remains, clear the list
    policy['auditConfigs'] = [
        ac for ac in policy['auditConfigs'] if ac.get('service') != 'iap.googleapis.com'
    ]

with open('cfg/gov_iam_policy.json', 'w') as f:
    json.dump(policy, f, indent=2)
"
# 3. Apply the updated policy to revert audit logging to default
gcloud projects set-iam-policy ${PROJECT_GOVERNANCE} cfg/gov_iam_policy.json

7. 組織のポリシーの制約を元に戻す

# revert iam v3 access policy binding org policy on project to org level setting
gcloud org-policies delete iam.managed.disableAccessPolicyBinding --project=${PROJECT_GOVERNANCE}

8. 共有 GCS ステージング バケットとローカル アーティファクトを削除する

# delete central staging bucket
gcloud storage rm -r gs://${PROJECT_GOVERNANCE}-shared-staging
# remove local configuration manifests, environment files, and application
rm -rf cfg/ cross-project-multiagent/ *.env

これでクリーンアップは完了です。次はまとめに進みます。

12. おわりに

おめでとうございます!Vertex AI Agent Runtime、Central Agent Gateway、Agent Registry、IAM 統合アクセス ポリシー(UAP)を使用して、Google Cloud にマルチプロジェクト エージェント間(A2A)アーキテクチャをデプロイして管理しました。

主なコンセプトの概要

  • 一元化された下り(外向き)境界: agentGatewayConfig を使用して、PROJECT_GOVERNANCE の中央 Agent Gateway を介してスポーク ランタイム コンテナ(PROJECT_CONCIERGEPROJECT_SELLERS)をルーティングしました。
  • 宣言型ガバナンス(UAP): リソースごとの断片化されたバインディングを、IAP v2 によってゲートウェイで評価される単一の監査可能な IAM アクセス ポリシーに置き換えました。
  • 暗号 ID: 有効期間の長い鍵ではなく、コンテナ SPIFFE ID(principal://...)を使用して最小権限の下り(外向き)を適用します。
  • 動的サービス ディスカバリ: 中央 Agent Registry を介して実行時にピア Agent Endpoint を解決し、ハードコードされた URL とプロジェクト ID を排除します。
  • ランタイム ポリシーの俊敏性: ポリシーの更新により、pizza-seller-agent をデフォルト拒否(403 Forbidden)から許可(200 OK)にリアルタイムで移行し、コンテナの再起動はゼロでした。

cosmopup

Cosmopup のコメント: 「エージェントは素晴らしい。エージェントがプロジェクト間の作業をすべて行ってくれるので、私は昼寝という主な目標に集中できます。」

次のステップとドキュメント