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 monolithiques autonomes à des systèmes multi-agents distribués (Agent-to-Agent / A2A). Dans ces topologies modernes, les agents orchestrateurs de haut niveau coordonnent des workflows métier complexes en déléguant des tâches à des agents worker de domaine spécialisés, à des serveurs d'outils MCP (Model Context Protocol) et à des bases de données d'entreprise de backend dans des projets Google Cloud indépendants.

Toutefois, l'exploitation de systèmes multi-agents à grande échelle pose des problèmes critiques 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 la visibilité sur les outils et les sous-agents existants.
  • Sortie inter-projets non surveillée : autoriser les agents à emprunter des itinéraires réseau directs et non inspectés 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 du moteur de raisonnement crée des dépendances fragiles qui se rompent lors des mises à niveau ou des redéploiements.
  • Absence d'identité appliquant le principe de moindre privilège : les comptes de service partagés ne permettent pas de fournir une non-répudiation cryptographique au niveau de chaque instance d'agent.

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

  1. Passerelle de l'agent (networkservices.googleapis.com) : proxy géré d'application des règles et de réseau régional. En mode 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 à travers les périmètres du projet.
  2. Agent Registry (agentregistry.googleapis.com) : catalogue de services unique pour les entreprises. Il fournit un répertoire centralisé et validé de tous les outils, serveurs MCP et agents pairs disponibles dans l'organisation, ce qui permet une découverte automatique et dynamique de l'exécution sans aucun point de terminaison codé en dur.
  3. Gouvernance de l'identité de l'agent et de l'IAP v2 (iap.googleapis.com et iam.googleapis.com) : framework cryptographique d'identité et d'accès. Les agents d'exécution reçoivent des URN de machine SPIFFE uniques et attestées (principal://...). La sortie sortante est évaluée par rapport aux stratégies d'accès unifiées 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 agentiques basées sur Python, avec des liaisons de configuration natives (agent_gateway_config) aux passerelles centrales.

Scénario d'atelier de programmation : achats de produits alimentaires et de boissons multiprojets

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

  • Projet de gouvernance centralisée (PROJECT_GOVERNANCE) : appartient aux équipes informatiques et SecOps centrales, et héberge l'Agent Gateway central, l'Agent Registry central et les Règles d'accès unifiées IAM.
  • Projet Consumer Orchestrator (PROJECT_CONCIERGE) : appartient à l'équipe chargée des achats et héberge l'agent de concierge d'achat, qui découvre dynamiquement les fournisseurs et achemine les commandes des clients.
  • Projet de fournisseur de domaine (PROJECT_SELLERS) : appartient à des fournisseurs externes ou à des services, et héberge les agents de vente de burgers et de pizzas.

figure1

Fig. 1. Architecture de gouvernance centralisée multiprojet

Pourquoi une gouvernance centralisée entre les projets ?

Dans les grandes entreprises, les équipes produit et les groupes de data science créent des agents d'IA dans des dizaines de projets Google Cloud indépendants. En donnant à chaque équipe un contrôle direct sur l'enregistrement des outils, les routes réseau de sortie et les mesures de sécurité, on crée une prolifération d'outils non vérifié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 multi-projets sépare la création de règles de l'exécution des agents :

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

figure2

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

Modèle de portée d'identité à deux niveaux dans les règles d'accès unifiées

Lorsque les agents communiquent via l'Agent Gateway central, Identity-Aware Proxy (IAP v2) évalue l'accès en fonction de l'identité de l'agent de l'appelant (une identité basée sur SPIFFE, attestée de manière cryptographique et émise automatiquement pour le conteneur d'exécution) par rapport à une règle d'accès IAM globale :

  • Niveau 1 : API Google Cloud de base (autorisation d'accès sortant à l'échelle du projet, principalSet:// dans la règle 1) : autorisation d'accès sortant à l'échelle du projet permettant à tous les environnements d'exécution d'agent des projets spokes 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 professionnels et services A2A (accès précis via principal:// dans les règles 2 et 3) : accès strict au moindre privilège lié à des instances Reasoning Engine individuelles, appliqué avec des conditions CEL (Common Expression Language) ciblant des services Agent Registry spécifiques enregistrés (destination.agent_registry.agent.name).

Objectif de l'atelier

  • Passerelle Agent Gateway centralisée (centralized-agw) dans PROJECT_GOVERNANCE
  • Extension du service d'autorisation IAP v2 et règle d'autorisation en mode STRICT ENFORCE (failOpen: false)
  • Règle d'accès unifiée IAM de base (uap-rules.json) et liaison de stratégie de projet
  • Autorisations IAM des agents de service interprojets (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 de conciergerie pour les achats avec autodiscovery REST dynamique dans PROJECT_CONCIERGE
  • Enregistrements de services dans Central Agent Registry avec des URL mTLS multiprojets
  • Mises à jour dynamiques des règles de sortie IAP v2 avec validation en direct et audits Cloud Logging

figure3

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

Objectifs

  • Configurer les autorisations IAM de l'agent de service multiprojet pour les passerelles centralisées
  • Comment acheminer la sortie Agent Runtime via une passerelle Agent Gateway centrale dans des environnements multiprojets
  • Déléguer l'autorisation Agent Gateway à Identity-Aware Proxy (IAP v2) à l'aide des Service Extensions (iapPolicyVersion: "V2")
  • Créer et associer des règles CEL (Common Expression Language) avec des stratégies d'accès unifiées IAM régissant les destinations enregistrées dans Agent Registry (destination.agent_registry.*)
  • Éliminer les ID et URL d'agent codés en dur à l'aide de la découverte automatique au moment de l'exécution par rapport à Agent Registry
  • Tester le blocage du périmètre zéro trust réel (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 la facturation activée :
    • PROJECT_GOVERNANCE : gouvernance centralisée, passerelle, registre et stratégies d'accès IAM
    • PROJECT_CONCIERGE : agent d'orchestration du service de conciergerie pour les achats
    • PROJECT_SELLERS : agents vendeurs spécialisés dans les burgers et les pizzas
  • Un compte de service ou un utilisateur IAM disposant des autorisations roles/owner ou d'autorisations d'administrateur pour les trois projets
  • Une organisation Google Cloud (pour le mappage du domaine de confiance SPIFFE)
  • Google Cloud Shell ou une machine locale avec gcloud CLI, python (3.11+) et uv installés

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

2. Configuration

Bien que cette architecture s'étende sur trois projets Google Cloud distincts, vous pouvez exécuter 100% des commandes de déploiement de terminal, des téléchargements de dépôt et des opérations de préparation à partir d'un seul terminal Cloud Shell défini sur PROJECT_GOVERNANCE. Chaque script de déploiement et chaque commande gcloud cible explicitement le projet de destination approprié via des options de 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 des 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 règles 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'audit des accès aux données Cloud 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 imprévus. É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 de l'API dans tous les projets

En vous assurant que les mêmes API sont activées dans les trois projets (PROJECT_GOVERNANCE, PROJECT_CONCIERGE et PROJECT_SELLERS), vous garantissez la 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 données 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 devraient ê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 stratégie d'accès IAM v3 aux ressources (constraints/iam.managed.disableAccessPolicyBinding).

Remplacez les restrictions héritées des règles d'administration 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 à la section Enregistrer les API Google Core.

3. Agent Registry

Enregistrer le service de point de terminaison des API Google Core

Agent Gateway nécessite que les URL des API Google soient enregistrées dans le registre central des agents afin que les agents configurés avec agent_gateway_config puissent acheminer le trafic sortant 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://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

ID de ressource du point de terminaison des API Core de Capture

# 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 Gemini Enterprise Agent Platform, les identités machine attribuées aux conteneurs d'agent en cours d'exécution utilisent des URN SPIFFE attestées de manière cryptographique, évaluées par Identity-Aware Proxy (IAP v2). Lorsque vous configurez des règles d'accès unifiées IAM, vous pouvez cibler un principal unique spécifique ou un principalSet basé sur des attributs :

Dimension

principal:// (identité d'une seule machine)

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

Syntaxe IAM

principal://...

principalSet://...

Précision

Précision fine (au niveau de l'instance) : identifie une instance de conteneur Reasoning Engine spécifique.

Précision approximative (au niveau du projet) : identifie tous les moteurs de raisonnement partageant un attribut de projet commun.

Format 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 Business et A2A) : autorisation d'agents orchestrateurs spécifiques à appeler des outils de domaine cible (par exemple, Concierge d'achat → Vendeur de burgers).

Niveau 1 (infrastructure de base) : accordez à 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.

Elle s'applique automatiquement aux agents nouvellement déployés dans ce projet, sans nécessiter de mises à jour IAM supplémentaires.

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

Dans l'ancienne version 1 d'IAP, 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. Sous IAP v2 et stratégies d'accès unifiées, les liaisons par ressource sont supprimées au profit d'une stratégie 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. Cela garantit que tous les conteneurs d'agent disposent de routes de sortie de base avant le déploiement.

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

L'enregistrement du point de terminaison des API principales est terminé. Passons à la section Déployer la passerelle d'agent centralisée.

4. Agent Gateway

Déployer une passerelle Agent Gateway centralisée

Déployez la passerelle Agent Gateway centralisée (centralized-agw) en mode 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 de l'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 l'Agent Gateway

# 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 à la section Configurer l'autorisation.

5. Autorisation

Configurer l'autorisation de l'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 (RAU) 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 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, en acheminant les vérifications d'autorisation vers l'extension d'autorisation IAP.
  3. Liaison et stratégie d'accès unifiées IAM (accessPolicy et policyBinding) : ressource IAM v3 globale évaluée par IAP. Il 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éez et importez 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éer et importer la règle d'autorisation de passerelle

Créez une configuration de règle d'autorisation qui s'associe à l'Agent Gateway et délègue la validation des requêtes à l'extension 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

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éer 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 de projet à atteindre 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

Associez 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 l'association de règles 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}

L'accès sortant aux API Google Cloud de base est désormais autorisé de manière sécurisée pour les trois projets en mode ENFORCE strict.

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

6. IAM entre plusieurs projets

Configurer les autorisations IAM multiprojets

Dans cette topologie multiprojet, les Agent Runtimes résident dans les projets spokes (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 inter-projets doit être accordé de manière explicite sur deux couches opérationnelles :

  1. Plan de contrôle (au moment du déploiement) : lorsque vous déployez un conteneur d'agent configuré avec --agent-gateway-config, l'agent de service d'exécution de l'agent (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) qui accorde networkservices.agentGateways.use, get et operations.get dans PROJECT_GOVERNANCE.
  2. Plan de données (exécution du runtime) :
    • Découverte du catalogue : les identités Spoke ont besoin de roles/agentregistry.viewer dans PROJECT_GOVERNANCE pour résoudre dynamiquement les points de terminaison de l'agent cible.
    • Invocation 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 marchand.

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 à la section Déployer les agents Seller et Concierge.

7. Agent Runtime

Déployer des agents vendeurs et de conciergerie

Le code de base de l'application multi-agents et les scripts de déploiement utilisés pour cet atelier de programmation sont conservés dans un dépôt GitHub Google Cloud distant. Les étapes suivantes cloneront le dépôt en local, copieront les fichiers nécessaires dans la structure du répertoire de travail actuel, effectueront le nettoyage des fichiers temporaires et installeront 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_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

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 de passerelle Agent Gateway multiprojet

Dans cette étape, vous allez déployer les agents du vendeur dans le projet spoke (PROJECT_SELLERS) tout en les configurant pour qu'ils acheminent le trafic de sortie via l'Agent Gateway central 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)

Étant donné que la règle 1 a été établie plus tôt dans notre Unified Access Policy, les requêtes d'initialisation de conteneur adressées aux API Google Cloud sont autorisées via la passerelle sans interruption.

Déployer des agents vendeurs 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 marchand

# 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 d'exécution de l'agent Concierge, ainsi que la liaison à 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 à la section Enregistrer les agents dans le registre central des agents.

8. Registre interprojets

Enregistrer des agents dans le registre central des agents

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

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

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 terminée. Passons à la section Configurer les règles de sortie A2A.

9. Règles du PAU

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

Dans l'architecture Default Deny de la passerelle d'agent en mode ENFORCE strict :

  1. Règle 1 (API Google Cloud de référence) : autorise les conteneurs d'agents dans les trois projets à atteindre core-gapi-services.
  2. Règle 2 (Agent de vente de burgers : AUTORISER) : autorise spécifiquement l'instance de l'agent de concierge d'achat à appeler l'agent de vente de burgers.
  3. Agent de vente de pizzas (REFUSÉ par défaut) : intentionnellement exclu des règles du règlement. En mode ENFORCE (failOpen: false), toute tentative du concierge d'appeler le vendeur de pizzas sera immédiatement interrompue au niveau 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

Créez un cfg/uap-rules-update-2.json pour inclure la Règle 1 (API Core) et la Règle 2 (Agent Burger Seller) :

# 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

Appliquer la stratégie d'accès mise à jour

# 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

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 terminée. Passons à la section Tester et valider les règles de gouvernance.

10. Valider les règles

Tester et valider les règles de gouvernance via Cloud Logging

Dans cette section, vous allez tester les interactions Agent-to-Agent (A2A) entre projets dans l'IA Playground Agent Runtime, observer le blocage HTTP 403 Forbidden réel du périmètre en mode strict ENFORCE, modifier les Règles d'accès unifiées en direct et valider l'approbation immédiate des commandes.

Étape 1 : Ouvrez le playground d'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 la droite 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 des tests par programmation à partir de Cloud Shell / du 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 service Purchasing Concierge a interrogé le registre central des agents dans PROJECT_GOVERNANCE (via core-gapi-services par le biais de la passerelle d'agent autorisée 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 invocation A2A : Gemini intégré au service de conciergerie d'achat analyse l'intention de commande de nourriture et invoque l'agent Burger Seller via un RPC sortant vers https://${REGION}-aiplatform.mtls.googleapis.com/.../reasoningEngines/${BURGER_ENGINE_ID}.
  3. Interception de la passerelle et propagation SPIFFE : le trafic de sortie est capturé par agent_gateway_config et dirigé vers la passerelle d'agent central dans PROJECT_GOVERNANCE, en transportant l'identité SPIFFE cryptographique du concierge (principal://...).
  4. Évaluation des règles 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ée 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 inter-projets : l'Agent Gateway sert de proxy pour la requête autorisée inter-projets dans PROJECT_SELLERS, où le moteur de raisonnement du vendeur de burgers 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 Agent Gateway et IAP v2 (HTTP 200 / AUTORISÉ)

Interrogez les journaux des requêtes Agent Gateway 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 projets spokes (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'audit Cloud Logging d'accès aux données IAP v2 pour vérifier la version de la règle 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 : Tester la commande de pizza (refus par défaut -> HTTP 403 interdit APPLIQUÉ)

Dans la même fenêtre de chat Playground, envoyez le prompt suivant pour commander une 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 des tests par programmation à partir de Cloud Shell / du 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 service de conciergerie des achats a résolu le point de terminaison pizza-seller-agent à partir du registre central des agents au démarrage.
  2. Résolution de l'intention et invocation A2A : Gemini dans le concierge d'achat tente d'envoyer la demande de commande de pizza au point de terminaison du vendeur de pizzas dans PROJECT_SELLERS.
  3. Interception de 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) : la passerelle Central Agent Gateway appelle IAP V2. Comme aucune règle ne correspond à pizza-seller-agent dans la règle d'accès unifiée, IAP renvoie DENY (granted: false).
  5. Blocage strict du périmètre : comme l'extension Authz est en mode ENFORCE (failOpen: false), la passerelle d'agent 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 de la passerelle de l'agent pour les requêtes bloquées (HTTP 403 / REFUSÉ)

# 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 résultat de journal 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 connaître 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 résultat de journal d'audit 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 dynamiquement l'accès à la sortie à l'agent Pizza

Créez de nouveaux cfg/uap-rules-update-3.json pour inclure la Règle 1 (API Core), la Règle 2 (Agent de vente de burgers) et maintenant la Règle 3 (Agent de vente de pizzas).

# 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 (succès immédiat 200 OK)

Dans la fenêtre de chat de l'atelier, 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 des tests par programmation à partir de Cloud Shell / du 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 stratégies : la mise à jour de la stratégie d'accès unifiée IAM prend effet immédiatement dans le moteur d'évaluation IAP, sans temps d'arrêt et sans redéployer de conteneurs.
  2. Invocation A2A : le Concierge envoie la requête via l'Agent Gateway central.
  3. Évaluation de la stratégie IAP v2 (approbation) : IAP v2 correspond à la règle 3, valide l'identité de l'appelant et l'expression CEL cible, et renvoie ALLOW (granted: true).
  4. Exécution inter-projets : l'Agent Gateway central sert de proxy pour le trafic autorisé dans PROJECT_SELLERS, où le vendeur de pizzas traite la commande.

Étape 8 : Inspecter les journaux de l'Agent Gateway pour les demandes de 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://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

Les tests et la validation sont terminés. Passons à la section Nettoyage.

11. Nettoyage

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

1. Nettoyer les déploiements Reasoning Engine

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 les moteurs de raisonnement en ligne :

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 une liaison de stratégie d'accès unifiée IAM et une stratégie 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 l'Agent Gateway et les règles 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 et le rôle personnalisé inter-projets

# 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é les rôles roles/iam.accessPolicyAdmin et roles/resourcemanager.projectIamAdmin lors de la phase de configuration, supprimez-les de votre compte utilisateur actif pour rétablir le principe de 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 liées aux règles 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 sur le nettoyage est terminée. Passons à la conclusion !

12. Conclusion

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

Résumé des concepts clés

  • Périmètre de sortie centralisé : conteneurs d'exécution de spokes routés (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 stratégie d'accès IAM unique et auditable, évaluée au niveau de la passerelle par IAP v2.
  • Identité cryptographique : sortie avec le moindre privilège appliqué à l'aide des identités SPIFFE de conteneur (principal://...) plutôt que des clés à longue durée de vie.
  • 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 centraux, ce qui élimine les URL et les ID de projet codés en dur.
  • Agilité des règles d'exécution : la valeur pizza-seller-agent est passée de "Refus par défaut" (403 Forbidden) à "Autorisé" (200 OK) en temps réel via une mise à jour des règles, sans redémarrage des conteneurs.

cosmopup

Cosmopup : "Les agents sont géniaux. Ils s'occupent de toutes les tâches interprojets pendant que je me concentre sur mon objectif principal : faire la sieste !"

Étapes suivantes et documentation