Gouvernance centralisée d'Agent Gateway avec Agent Registry multiprojet pour Agent Runtime

1. Introduction

À mesure que les entreprises adoptent l'IA générative, les architectures évoluent rapidement, passant de chatbots autonomes et monolithiques à des systèmes multi-agents distribués (Agent-to-Agent / A2A). Dans ces topologies modernes, des agents d'orchestration de haut niveau coordonnent des workflows d'entreprise complexes en déléguant des tâches à des agents de domaine spécialisés, à des serveurs d'outils MCP (Model Context Protocol) et à des bases de données d'entreprise backend dans des projets Google Cloud indépendants.

Cependant, l'exploitation de systèmes multi-agents à grande échelle pose des défis critiques en termes de sécurité, de gouvernance et d'exploitation :

  • Prolifération des agents et des outils fantômes : lorsque les équipes de développement déploient des agents dans des projets isolés sans catalogue centralisé, les organisations perdent en visibilité sur les outils et les sous-agents existants.
  • Sortie interprojets non surveillée : autoriser les agents à utiliser des routes réseau directes et non inspectées crée des risques d'exfiltration de données et contourne les périmètres de sécurité.
  • Intégrations codées en dur fragiles : le codage en dur des URL des agents en aval et des ID de moteur de raisonnement crée des dépendances fragiles qui se rompent lors des mises à niveau ou des redéploiements.
  • Absence d'identité basée sur le principe du moindre privilège : les comptes de service partagés ne fournissent pas de non-répudiation cryptographique au niveau de l'instance d'agent individuelle.

Pour relever ces défis, Gemini Enterprise Agent Platform fournit un plan de contrôle unifié de la gouvernance et de la connectivité, composé de quatre piliers principaux :

  1. Agent Gateway (networkservices.googleapis.com) : proxy géré d'application des règles et du réseau régional. En mode de sortie AGENT_TO_ANYWHERE, il intercepte le trafic sortant de l'agent, délègue les évaluations d'autorisation aux extensions de sécurité et achemine les requêtes au-delà des limites du projet.
  2. Agent Registry (agentregistry.googleapis.com) : catalogue de services d'entreprise unique. Il fournit un annuaire centralisé et validé de tous les outils, serveurs MCP et agents pairs disponibles dans l'organisation, ce qui permet une découverte automatique dynamique au moment de l'exécution, sans aucun point de terminaison codé en dur.
  3. Gouvernance de l'identité des agents et d'IAP v2 (iap.googleapis.com et iam.googleapis.com) : un framework d'identité et d'accès cryptographique. Les agents d'exécution reçoivent des URN de machine SPIFFE uniques et attestés (principal://...). La sortie sortante est évaluée par rapport aux règles d'accès unifié IAM (UAP/IAP v2) centralisées qui vérifient l'autorisation universelle iap.googleapis.com/resources.egressViaIAP à l'aide de conditions de catalogue CEL (Common Expression Language) enrichies (destination.agent_registry.*).
  4. Agent Runtime (moteurs de raisonnement) : plate-forme d'exécution sans serveur entièrement gérée pour les applications d'agent basées sur Python, avec des liaisons de configuration natives (agent_gateway_config) vers des passerelles centrales.

Scénario d'entreprise de l'atelier de programmation : Achats de produits alimentaires et de boissons pour plusieurs projets

Dans cet atelier de programmation, vous allez créer et gérer un écosystème d'achat multiprojet réel couvrant trois projets Google Cloud distincts :

  • Projet de gouvernance centralisée (PROJECT_GOVERNANCE) : détenu par les équipes informatiques et SecOps centrales, il héberge l'Agent Gateway central, l'Agent Registry central et les règles d'accès unifié IAM.
  • Projet d'orchestrateur client (PROJECT_CONCIERGE) : détenu par l'équipe d'approvisionnement, il héberge l'agent concierge d'achat, qui découvre dynamiquement les fournisseurs et achemine les commandes des clients.
  • Projet de fournisseur de domaine (PROJECT_SELLERS) : détenu par des fournisseurs externes ou départementaux, hébergeant l'agent de vente de burgers et l'agent de vente de pizzas.

figure1

Fig. 1 Architecture de gouvernance centralisée multiprojet

Pourquoi opter pour une gouvernance centralisée multiprojet ?

Dans les grandes entreprises, les équipes produit et les groupes de data science créent des agents IA dans des dizaines de projets Google Cloud indépendants. Donner à chaque équipe un contrôle direct sur l'enregistrement des outils, les routes réseau de sortie et les garde-fous de sécurité entraîne une prolifération d'outils non validés, des règles de protection contre la perte de données incohérentes, une sortie VPC non surveillée et des journaux d'audit fragmentés.

La gouvernance centralisée multiprojet sépare la création des règles de l'exécution des agents :

  • Les équipes Central IT et SecOps créent des règles de sécurité, valident les outils et surveillent les sorties au sein d'un seul projet de gouvernance centralisé.
  • Les équipes produit et application se concentrent uniquement sur la logique métier dans leurs projets Agent Runtime indépendants, en se connectant directement à la passerelle centrale sans la surcharge opérationnelle liée à la gestion des VPC locaux, des interconnexions ou des moteurs de stratégies fragmentés.

figure2

Fig. 2. Architecture et limites de la gouvernance multiprojets à trois niveaux

Modèle de champ d'application de l'identité à deux niveaux dans les règles d'accès unifiées

Lorsque les agents communiquent via l'Agent Gateway centrale, Identity-Aware Proxy (IAP v2) évalue l'accès en fonction de l'Agent Identity (une identité basée sur SPIFFE et attestée par cryptographie, émise automatiquement pour le conteneur d'environnement d'exécution) par rapport à une stratégie d'accès IAM globale :

  • Niveau 1 : API Google Cloud de base (gros grain via principalSet:// dans la règle 1) : autorisation de sortie à l'échelle du projet permettant à tous les environnements d'exécution d'agent des projets spoke d'accéder aux API Google standards (aiplatform, iamcredentials, telemetry, agentregistry) pour la découverte, la génération de jetons et l'inférence.
  • Niveau 2 : Outils d'entreprise et services A2A (précis via principal:// dans les règles 2 et 3) : accès strict selon le principe du moindre privilège lié à des instances de moteur de raisonnement individuelles, appliqué avec des conditions CEL (Common Expression Language) ciblant des services de registre d'agents enregistrés spécifiques (destination.agent_registry.agent.name).

Objectif de l'atelier

  • Agent Gateway centralisé (centralized-agw) dans PROJECT_GOVERNANCE
  • Extension de service d'autorisation IAP v2 et règle Authz en mode STRICT (failOpen: false)
  • Règle d'accès unifiée IAM fondamentale (uap-rules.json) et liaison de stratégie de projet
  • Autorisations IAM de l'agent de service multiprojet (ar_agw_cross_project_sa)
  • Bucket de préproduction Google Cloud Storage (GCS) central partagé
  • Agents de vente de burgers et de pizzas isolés dans PROJECT_SELLERS
  • Agent Concierge d'achat avec découverte automatique REST dynamique dans PROJECT_CONCIERGE
  • Enregistrements de services dans le registre d'agents central avec des URL mTLS multiprojets
  • Mises à jour dynamiques des règles de sortie IAP v2 avec vérification en direct et audits Cloud Logging

figure3

Fig. 3. Séquence d'implémentation étape par étape

Objectifs

  • Configurer les autorisations IAM des agents de service multiprojets pour les passerelles centralisées
  • Comment acheminer la sortie d'Agent Runtime via un Agent Gateway central dans des environnements multiprojets
  • Déléguer l'autorisation Agent Gateway à Identity-Aware Proxy (IAP v2) à l'aide de Service Extensions (iapPolicyVersion: "V2")
  • Comment créer et lier des règles d'accès unifiées IAM (UAP) avec des règles CEL (Common Expression Language) régissant les destinations Agent Registry enregistrées (destination.agent_registry.*)
  • Comment éliminer les ID et les URL d'agent codés en dur à l'aide de la détection automatique au moment de l'exécution par rapport à Agent Registry
  • Comment tester le blocage réel du périmètre zéro confiance (HTTP 403 Forbidden) et vérifier les mises à jour des règles en direct dans Cloud Logging

Ce dont vous avez besoin

  • Trois projets Google Cloud avec facturation activée :
    • PROJECT_GOVERNANCE : gouvernance centralisée, passerelle, registre et règles d'accès IAM
    • PROJECT_CONCIERGE : agent orchestrateur de conciergerie d'achat
    • PROJECT_SELLERS : Agents de vente spécialisés dans les burgers et les pizzas
  • Un utilisateur ou un compte de service IAM disposant d'autorisations roles/owner ou d'administration sur les trois projets
  • Une organisation Google Cloud (pour le mappage du domaine de confiance SPIFFE)
  • Google Cloud Shell ou une machine locale sur laquelle sont installés la CLI gcloud, python (3.11 ou version ultérieure) et uv

La partie d'introduction est terminée. Passons maintenant à la section Configuration et environnement.

2. Configuration

Bien que cette architecture s'étende sur trois projets Google Cloud distincts, vous pouvez exécuter l'intégralité des commandes de déploiement du terminal, des téléchargements de dépôts et des opérations de préproduction à partir d'un seul terminal Cloud Shell défini sur PROJECT_GOVERNANCE. Chaque script de déploiement et commande gcloud cible explicitement le projet de destination approprié via des options de la CLI (--project).

Commencez par accéder à la ligne de commande de votre projet Google Cloud :

Définir le contexte de votre projet

# 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

Définir les variables d'environnement de shell

Saisissez les identifiants spécifiques à votre projet.

# 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"

Ces variables shell seront dérivées automatiquement.

# 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}"

Créer un répertoire local pour les fichiers de configuration

# create config folder
mkdir -p cfg

Attribuer le rôle d'administrateur des stratégies d'accès pour les règles d'accès unifiées

# 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

Activer les journaux d'accès aux données Cloud Audit pour IAP v2

Par défaut, Google Cloud désactive les journaux d'audit des accès aux données pour éviter des frais de stockage involontaires. Étant donné qu'IAP v2 émet des décisions d'autorisation (granted=true et granted=false) en tant que journaux d'audit des accès aux données, activez la journalisation ADMIN_READ, DATA_READ et DATA_WRITE pour iap.googleapis.com dans PROJECT_GOVERNANCE :

# 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)"

Activer les API Google Cloud requises

# 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

Valider l'activation des API dans tous les projets

En vous assurant que les trois projets (PROJECT_GOVERNANCE, PROJECT_CONCIERGE et PROJECT_SELLERS) ont exactement les mêmes API activées, vous établissez une cohérence opérationnelle et évitez les échecs de création de jetons d'exécution, les erreurs de catalogage de schéma ou les pertes de télémétrie.

Exécutez le script de validation suivant dans Cloud Shell pour vérifier la parité des API dans les trois projets :

# 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

Exemple de résultat de validation :

Toutes les API doivent être activées.

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

Configurer des règles d'administration

Les règles d'administration Google Cloud par défaut appliquent des contraintes qui limitent les liaisons de règles d'accès IAM v3 aux ressources (constraints/iam.managed.disableAccessPolicyBinding).

Remplacez toutes les restrictions de règles d'administration héritées au niveau du projet en définissant explicitement enforce: false sur "Autoriser".

# 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

La partie configuration est terminée. Passons maintenant à la section Enregistrer les API Google principales.

3. Agent Registry

Enregistrer le service de point de terminaison des API Google principales

Agent Gateway nécessite l'enregistrement des URL des API Google dans le registre d'agents central afin que les agents configurés avec agent_gateway_config puissent acheminer le trafic de sortie de manière sécurisée vers les services de backend Google Cloud principaux (tels que aiplatform, les identifiants IAM et la télémétrie).

Créer core-gapi-services dans Agent Registry

# 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://cloudresourcemanager.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.mtls.googleapis.com:443 \
  --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

Capturer l'ID de ressource du point de terminaison des API Core

# 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}"

Comprendre la différence entre principalSet et principal dans Agent Identity

Dans Google Cloud IAM et la plate-forme d'agents Gemini Enterprise, les identités de machine émises pour les conteneurs d'agents en cours d'exécution utilisent des URN SPIFFE attestés par cryptographie évalués par Identity-Aware Proxy (IAP v2). Lorsque vous configurez des règles d'accès unifié IAM, vous pouvez cibler un principal spécifique ou un principalSet basé sur des attributs :

Dimension

principal:// (identité de machine unique)

principalSet:// (groupe basé sur des attributs)

Syntaxe IAM

principal://...

principalSet://...

Précision

Précis (au niveau de l'instance) : identifie une instance de conteneur de moteur de raisonnement spécifique.

À granularité grossière (au niveau du projet) : identifie tous les moteurs de raisonnement partageant un attribut de projet commun.

Format d'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}

Cas d'utilisation dans Agent Platform

Niveau 2 (outils d'entreprise et A2A) : autoriser des agents d'orchestration spécifiques à appeler des outils de domaine cible (par exemple, Concierge d'achat $\rightarrow$ Vendeur de burgers).

Niveau 1 (infrastructure de base) : accorder à tous les agents d'un projet un accès sortant aux API Google Cloud (core-gapi-services).

Impact sur le cycle de vie

Si un agent est supprimé et recréé, son nouvel ID de moteur nécessite une liaison de stratégie IAM mise à jour.

S'applique automatiquement aux agents nouvellement déployés dans ce projet sans mises à jour IAM supplémentaires.

Gouvernance déclarative avec les règles d'accès unifiées (UAP/IAP v2)

Dans l'ancienne version d'IAP v1, les règles de sortie étaient associées directement à des ressources Agent Registry individuelles à l'aide de gcloud beta iap web add-iam-policy-binding. Avec IAP v2 et les règles d'accès unifiées, les liaisons par ressource sont éliminées au profit d'une règle d'accès IAM unique et centralisée (cfg/uap-rules.json).

L'autorisation de sortie de base pour core-gapi-services sera configurée en tant que Règle 1 dans la règle d'accès unifiée de la section 5, ce qui garantit que tous les conteneurs d'agent disposent de routes de sortie de base établies avant le déploiement.

Pour en savoir plus sur les identifiants de comptes principaux et le fonctionnement de Workload Identity, consultez les ressources suivantes :

L'enregistrement du point de terminaison des API principales est terminé. Passons maintenant à la section Déployer Agent Gateway centralisé.

4. Agent Gateway

Déployer une passerelle Agent Gateway centralisée

Déployez l'Agent Gateway centralisé (centralized-agw) en mode de sortie AGENT_TO_ANYWHERE dans le projet $PROJECT_GOVERNANCE.

Définir le fichier manifeste de configuration de la passerelle

Créez cfg/${AGW_NAME}.yaml pour la gouvernance du trafic de sortie :

# 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

Importer la configuration d'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}

Vérifier les détails de la passerelle d'agent

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

Exemple de résultat :

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'

Le déploiement de la passerelle est terminé. Passons maintenant à la section Configurer l'autorisation.

5. Autorisation

Configurer l'autorisation Agent Gateway et l'UAP de base

Agent Gateway sécurise et régit le trafic sortant des outils et des agents à l'aide de règles d'autorisation (networksecurity.authzPolicies) intégrées aux règles d'accès unifiées (UAP) d'Identity-Aware Proxy (IAP v2).

Présentation de l'architecture d'autorisation

figure4

Fig. 4. Présentation de l'architecture d'autorisation

L'architecture d'autorisation est composée de trois couches interconnectées :

  1. Extension de service IAP (authzExtension) : ressource régionale configurée avec service: iap.googleapis.com, metadata: iapPolicyVersion: "V2" et failOpen: false pour une application stricte du modèle zéro confiance au niveau du périmètre.
  2. Règle d'autorisation de passerelle (authzPolicy) : ressource régionale ciblant votre Agent Gateway avec policyProfile: REQUEST_AUTHZ et action: CUSTOM, qui achemine les vérifications d'autorisation vers l'extension d'autorisation IAP.
  3. Liaison et stratégie d'accès unifié IAM (accessPolicy et policyBinding) : ressource IAM v3 globale évaluée par IAP. Elle vérifie l'autorisation universelle iap.googleapis.com/resources.egressViaIAP par rapport aux identités SPIFFE de l'appelant et aux conditions du catalogue CEL.

Étape 1 : Créer et importer l'extension d'autorisation IAP v2

Créez le fichier manifeste de l'extension de service avec iapPolicyVersion: "V2" et failOpen: false en mode ENFORCE strict :

# 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

Importez l'extension d'autorisation :

# 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}

Vérifiez que l'extension Authz est active :

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

Exemple de résultat :

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

Étape 2 : Créez et importez une règle d'autorisation de passerelle

Créez une configuration AuthorizationPolicy qui s'associe à Agent Gateway et délègue la validation des requêtes à l'extension d'autorisation IAP :

# 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

Importez la règle d'autorisation :

# 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}

Vérifiez la règle d'autorisation active :

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

Étape 3 : Créez la règle d'accès unifiée initiale (Règle 1 : API Google principales)

Créez cfg/uap-rules.json avec la règle 1 autorisant les trois principalSet du projet à accéder à core-gapi-services :

# 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

Étape 4 : Créez et associez la stratégie d'accès IAM

Créez la stratégie d'accès IAM globale :

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

Liez la règle d'accès à 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

Vérifiez que la liaison de stratégie est active :

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

Exemple de résultat :

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}

La sortie de l'API Google Cloud de base est désormais autorisée de manière sécurisée dans les trois projets en mode ENFORCE strict.

La configuration de l'autorisation de la passerelle est terminée. Passons maintenant à la section Configurer les autorisations IAM multiprojets.

6. IAM multiprojets

Configurer les autorisations IAM multiprojets

Dans cette topologie multiprojet, les environnements d'exécution d'agent résident dans les projets spoke (PROJECT_CONCIERGE et PROJECT_SELLERS), tandis que l'Agent Gateway central et l'Agent Registry résident dans PROJECT_GOVERNANCE.

Étant donné que les projets Google Cloud sont des périmètres de sécurité isolés, l'accès multiprojet doit être explicitement accordé sur deux couches opérationnelles :

  1. Plan de contrôle (moment du déploiement) : lors du déploiement d'un conteneur d'agent configuré avec --agent-gateway-config, l'agent de service Agent Runtime (service-@gcp-sa-aiplatform.iam.gserviceaccount.com) du projet spoke doit associer le conteneur à la passerelle centrale. Nous créons un rôle personnalisé minimal (ar_agw_cross_project_sa) accordant networkservices.agentGateways.use, get et operations.get dans PROJECT_GOVERNANCE.
  2. Plan de données (exécution de l'environnement d'exécution) :
    • Découverte du catalogue : les identités des spokes ont besoin de l'autorisation roles/agentregistry.viewer dans PROJECT_GOVERNANCE pour résoudre dynamiquement les points de terminaison des agents cibles.
    • Appel de la cible : l'agent Concierge a besoin de roles/aiplatform.user dans PROJECT_SELLERS pour exécuter des requêtes sur les moteurs de raisonnement du vendeur.

Créer un rôle IAM personnalisé dans PROJECT_GOVERNANCE

# 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"

Attribuer un rôle personnalisé aux agents de service 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

La configuration IAM interprojets est terminée. Passons maintenant à la section Déployer les agents Seller et Concierge.

7. Agent Runtime

Déployer des agents Seller et Concierge

La codebase de l'application multi-agent et les scripts de déploiement utilisés pour cet atelier de programmation sont gérés dans un dépôt GitHub Google Cloud distant. Les étapes suivantes permettent de cloner le dépôt localement, de copier les fichiers nécessaires dans la structure du répertoire de travail actuel, de nettoyer les fichiers temporaires et d'installer les dépendances avec uv.

Récupérer des artefacts distants

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

Créer un bucket de préproduction central partagé

# 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"

Fonctionnement de la liaison Agent Gateway multiprojet

Au cours de cette étape, vous allez déployer les agents vendeurs dans le projet spoke (PROJECT_SELLERS) tout en les configurant pour acheminer le trafic sortant via l'Agent Gateway centrale dans PROJECT_GOVERNANCE :

# !-- 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)

Comme la règle 1 a été établie plus tôt dans notre stratégie d'accès unifié, les requêtes d'initialisation de conteneur vers les API Google Cloud sont autorisées via la passerelle sans interruption.

Déployer les agents de vente de burgers et de pizzas sur 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}

Valider le routage de la passerelle du vendeur

# 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

Déployer l'agent Purchasing Concierge sur 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}

Valider le routage de la passerelle d'achat

# 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}'

Le résultat doit afficher l'identité et le projet de l'Agent Runtime de Concierge, ainsi que la liaison avec l'Agent Gateway du projet de gouvernance.

{
  "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"
    }
  }
}

Les déploiements d'agents sont terminés. Passons maintenant à la section Enregistrer des agents dans Agent Registry.

8. Registre interprojets

Enregistrer des agents dans le registre d'agents centralisé

Enregistrez les trois agents dans le registre d'agents central de PROJECT_GOVERNANCE à l'aide de points de terminaison mTLS régionaux interprojets et de numéros de projet numériques.

Enregistrer des services en tant qu'agents non-A2A dans Agent Registry

# 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.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --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 \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/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.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --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 \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/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.googleapis.com/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --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 \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query

Capturer les ID Agent Registry sous-jacents

# 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}"

La configuration du registre est maintenant terminée. Passez à la section Configurer les règles de sortie A2A.

9. Règles UAP

Configurer des règles de sortie A2A dans une règle d'accès unifiée

Dans l'architecture Refus par défaut d'Agent Gateway en mode ENFORCE strict :

  1. Règle 1 (API Google Cloud de référence) : permet aux conteneurs d'agent des trois projets d'accéder à core-gapi-services.
  2. Règle 2 (Agent de vente de burgers : AUTORISER) : autorise l'instance de l'agent concierge d'achat à appeler spécifiquement l'agent de vente de burgers.
  3. Agent de vente de pizzas (REFUSÉ par défaut) : intentionnellement exclu des règles de la stratégie. En mode ENFORCE (failOpen: false), toute tentative du Concierge d'appeler le Pizza Seller sera immédiatement interrompue au niveau du périmètre de la passerelle avec HTTP 403 Forbidden.

Formuler l'identité de l'agent concierge

# 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}"

Mettre à jour le fichier manifeste avec les règles 1 et 2

Pour mettre à jour la règle d'accès en toute sécurité sans écraser les règles existantes, définissez le fichier manifeste consolidé complet contenant la Règle 1 (API Google principales) et la Règle 2 (Burger Seller Agent) :

# create consolidated policy manifest with Rule 1 and Rule 2
cat > cfg/uap-rules-consolidated.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}')"
      }
    }
  },
  {
    "description": "Rule 2: Allow Purchasing Concierge to invoke Burger Seller Agent via Central Gateway",
    "effect": "ALLOW",
    "principals": [
      "${CONCIERGE_SPIFFE_PRINCIPAL}",
      "principal://agents.global.org-${ORG_ID}.system.id.goog/attribute.agentIdentity/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.agentIdentity/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}"
    ],
    "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

Appliquer la règle d'accès mise à jour

Appliquez le fichier manifeste de règles consolidé de manière déclarative à l'aide de --details-rules :

# update IAM access policy with consolidated manifest (Rule 1 + Rule 2)
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --details-rules=cfg/uap-rules-consolidated.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Vérifier les détails de la stratégie d'accès IAM

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

Exemple de résultat :

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

La configuration des règles est maintenant terminée. Passez à la section Tester et vérifier les règles de gouvernance.

10. Vérifier les règles

Tester et vérifier les règles de gouvernance via Cloud Logging

Dans cette section, vous allez tester les interactions agent à agent (A2A) entre projets dans Agent Runtime Laboratoire IA, observer le blocage réel de HTTP 403 Forbidden en mode ENFORCE strict, modifier les Règles d'accès unifiées en direct et valider l'approbation immédiate de la commande.

Étape 1 : Ouvrez le laboratoire IA Agent Runtime dans PROJECT_CONCIERGE

  1. Ouvrez la console Google Cloud.
  2. Dans la barre de sélection de projet en haut de l'écran, passez à PROJECT_CONCIERGE.
  3. Dans le menu de navigation, accédez à Agent Platform > Agents > Déploiements.
  4. Cliquez sur purchasing-concierge-adk.
  5. Sélectionnez Playground pour ouvrir l'interface de chat interactive sur le côté droit de l'écran.

Étape 2 : Testez la commande de burger (correspondance avec la règle 2 -> 200 OK)

Dans la fenêtre de chat de Playground, envoyez le prompt de commande suivant :

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

Si une réponse de confirmation est nécessaire, envoyez la réponse suivante :

Confirmed, please place the order.

Vous pouvez également effectuer un test de manière programmatique depuis Cloud Shell ou un terminal :

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)
"

Si une réponse de confirmation est nécessaire, utilisez cette commande :

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'])
"

Que se passe-t-il en arrière-plan ?

  1. Découverte dynamique : au démarrage de la session, le Purchasing Concierge a interrogé le registre d'agents central dans PROJECT_GOVERNANCE (via core-gapi-services via Agent Gateway autorisé par la règle 1) pour découvrir le point de terminaison mTLS régional pour burger-seller-agent.
  2. Résolution de l'intention et appel A2A : Gemini, au sein du Concierge d'achat, analyse l'intention de commande de nourriture et appelle l'agent Burger Seller via un RPC sortant vers https://${REGION}-aiplatform.mtls.googleapis.com/.../reasoningEngines/${BURGER_ENGINE_ID}.
  3. Interception par la passerelle et propagation SPIFFE : le trafic de sortie est capturé par agent_gateway_config et dirigé vers la passerelle d'agent centrale dans PROJECT_GOVERNANCE, en transportant l'identité SPIFFE cryptographique du Concierge (principal://...).
  4. Évaluation de la stratégie IAP v2 : l'Agent Gateway central appelle l'extension d'autorisation IAP (authzExtension). IAP v2 évalue la règle 2 de la stratégie d'accès unifié IAM. Étant donné que l'appelant correspond à ${CONCIERGE_SPIFFE_PRINCIPAL} et que la cible correspond à burger-seller-agent, IAP renvoie ALLOW (granted: true).
  5. Exécution interprojets : Agent Gateway transmet la requête autorisée au projet PROJECT_SELLERS, où le moteur de raisonnement Burger Seller traite la commande et renvoie une confirmation.

Réponse attendue :

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

Étape 3 : Inspectez les journaux d'audit d'Agent Gateway et d'IAP v2 (HTTP 200/ALLOWED)

Interrogez les journaux de requêtes de la passerelle d'agent dans PROJECT_GOVERNANCE :

# 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
  )"

Les journaux doivent capturer le trafic sortant provenant des deux projets spoke (PROJECT_CONCIERGE et PROJECT_SELLERS) avec des champs de sortie pour les appels de raisonnement Gemini (generateContent), la télémétrie Cloud Trace (/v1/traces) et les recherches d'identifiants IAM, qui sont interceptés et autorisés de manière transparente par la règle 1 (core-gapi-services).

Interrogez les journaux d'accès aux données Cloud Audit d'IAP v2 pour vérifier la version de la stratégie 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
  )"

Exemple de résultat :

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

Étape 4 : Testez la commande de pizza (refus par défaut -> HTTP 403 Interdit APPLIQUÉ)

Dans la même fenêtre de chat Playground, envoyez le prompt de commande de pizza suivant :

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

Si une réponse de confirmation est nécessaire, envoyez la réponse suivante :

Confirmed, please place the order.

Vous pouvez également effectuer un test de manière programmatique depuis Cloud Shell ou un terminal :

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)
"

Si une réponse de confirmation est nécessaire, utilisez cette commande :

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'])
"

Réponse attendue :

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.

Que se passe-t-il en arrière-plan ?

  1. Découverte dynamique : le concierge d'achat a résolu le point de terminaison pizza-seller-agent à partir du registre d'agents central au démarrage.
  2. Résolution de l'intention et appel A2A : Gemini dans le concierge d'achat tente d'envoyer la requête de commande de pizza au point de terminaison du vendeur de pizzas dans PROJECT_SELLERS.
  3. Interception par la passerelle : le RPC sortant est capturé par agent_gateway_config et dirigé vers la passerelle Agent Gateway centrale.
  4. Évaluation des règles IAP v2 (refus par défaut) : l'Agent Gateway central appelle IAP v2. Comme aucune règle de la règle d'accès unifiée ne correspond à pizza-seller-agent, IAP renvoie DENY (granted: false).
  5. Blocage strict du périmètre : comme l'extension d'autorisation est en mode FORCER (failOpen: false), l'Agent Gateway central met immédiatement fin à la connexion sortante et renvoie HTTP 403 Forbidden. Le trafic ne quitte jamais la passerelle et n'atteint jamais PROJECT_SELLERS.

Étape 5 : Inspecter les journaux d'Agent Gateway pour détecter les requêtes bloquées (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
  )"

Exemple de sortie de journal de refus :

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

Interrogez les journaux d'audit des accès aux données IAP v2 pour la décision de refus :

# 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
  )"

Exemple de sortie de journal d'audit de refus :

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

Étape 6 : Accorder l'accès en sortie à l'agent Pizza de manière dynamique

Créez un cfg/uap-rules-update-3.json pour inclure la Règle 1 (API principales), la Règle 2 (agent Burger Seller) et maintenant la Règle 3 (agent Pizza Seller).

# 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

Appliquez la mise à jour de la règle en direct :

# 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

Étape 7 : Interroger à nouveau l'agent Pizza (réussite immédiate 200 OK)

Dans la fenêtre de chat de Playground, renvoyez le prompt de commande de pizza :

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

Si une réponse de confirmation est nécessaire, envoyez la réponse suivante :

Confirmed, please place the order.

Vous pouvez également effectuer un test de manière programmatique depuis Cloud Shell ou un terminal :

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)
"

Si une réponse de confirmation est nécessaire, utilisez cette commande :

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'])
"

Réponse attendue :

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**

Que se passe-t-il en arrière-plan ?

  1. Actualisation dynamique des règles : la mise à jour de la règle d'accès unifié IAM prend effet immédiatement dans le moteur d'évaluation IAP, sans aucun temps d'arrêt ni redéploiement de conteneurs.
  2. Appel A2A : le concierge transmet la requête via Agent Gateway central.
  3. Évaluation de la stratégie IAP v2 (approbation) : IAP v2 correspond à la règle 3, vérifie l'identité de l'appelant et l'expression CEL cible, puis renvoie ALLOW (granted: true).
  4. Exécution multiprojet : Central Agent Gateway transmet le trafic autorisé à PROJECT_SELLERS, où le vendeur de pizzas traite la commande.

Étape 8 : Inspecter les journaux d'Agent Gateway pour les requêtes Pizza accordées

# 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
  )"

Exemple de résultat de journal d'accès accordé :

TIME      METHOD  STATUS  AUTHZ    URL
HH:MM:SS  POST    200     ALLOWED  https://aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/global/publishers/google/models/gemini-3.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

Les tests et la vérification sont terminés. Passons maintenant à la section Nettoyage.

11. Nettoyage

Pour éviter que les ressources utilisées dans cet atelier de programmation ne soient facturées sur votre compte Google Cloud, exécutez les étapes de suppression dans l'ordre inverse de la dépendance :

1. Nettoyer les déploiements de moteurs de raisonnement

Exécutez le script cleanup_old_deployments.py inclus dans les deux projets d'exécution pour supprimer les moteurs de raisonnement et attendre la fin de leurs opérations de longue durée :

# 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}

Vous pouvez également lister et supprimer des moteurs de raisonnement de manière intégrée :

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. Supprimer les services 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. Supprimer la liaison de règle d'accès unifiée IAM et la règle d'accès

# 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. Supprimer Agent Gateway et les stratégies de sécurité

# 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. Supprimer les liaisons IAM interprojets et le rôle personnalisé

# 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

Si vous avez attribué roles/iam.accessPolicyAdmin et roles/resourcemanager.projectIamAdmin lors de la phase de configuration, supprimez-les de votre compte utilisateur actif pour restaurer le principe du moindre privilège :

# 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. Rétablir la journalisation des données d'audit et les contraintes liées aux règles d'administration

# 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. Rétablir les contraintes de règle d'administration

# 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. Supprimer le bucket de préproduction GCS partagé et les artefacts locaux

# 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

La partie concernant le nettoyage est terminée. Passons maintenant à la Conclusion.

12. Conclusion

Félicitations ! Vous avez déployé et géré une architecture agent IA à agent IA (A2A) multiprojet sur Google Cloud à l'aide de Vertex AI Agent Runtime, de Central Agent Gateway, d'Agent Registry et des règles d'accès unifié (UAP) IAM.

Résumé des concepts clés

  • Périmètre de sortie centralisé : vous avez routé les conteneurs d'exécution des spokes (PROJECT_CONCIERGE, PROJECT_SELLERS) via une passerelle Agent Gateway centrale dans PROJECT_GOVERNANCE à l'aide de agentGatewayConfig.
  • Gouvernance déclarative (UAP) : les liaisons fragmentées par ressource ont été remplacées par une seule stratégie d'accès IAM auditable, évaluée au niveau de la passerelle par IAP v2.
  • Identité cryptographique : sortie avec moindre privilège appliquée à l'aide d'identités SPIFFE de conteneur (principal://...) plutôt que de clés de longue durée.
  • Découverte dynamique des services : les points de terminaison des agents pairs sont résolus au moment de l'exécution via le registre d'agents centralisé, ce qui élimine les URL et les ID de projet codés en dur.
  • Agilité des règles d'exécution : transition de pizza-seller-agent de "Refus par défaut" (403 Forbidden) à "Autorisé" (200 OK) en temps réel via une mise à jour des règles, sans redémarrage du conteneur.

cosmopup

Cosmopup dit : "Les agents sont formidables. Ils s'occupent de tout le travail entre les projets pendant que je me concentre sur mon objectif principal : faire la sieste !"

Étapes suivantes et documentation