Governance centralizzata di Agent Gateway con Agent Registry cross-project per Agent Runtime

1. Introduzione

Man mano che le organizzazioni aziendali adottano l'AI generativa, le architetture si evolvono rapidamente da chatbot autonomi e monolitici a sistemi multi-agente distribuiti (da agente ad agente / A2A). In queste topologie moderne, gli agenti orchestratori di alto livello coordinano i complessi workflow aziendali delegando le attività ad agenti worker di dominio specializzati, server di strumenti Model Context Protocol (MCP) e database aziendali di backend in progetti Google Cloud indipendenti.

Tuttavia, l'utilizzo di sistemi multi-agente su larga scala introduce sfide operative, di governance e di sicurezza critiche:

  • Proliferazione di agenti e strumenti ombra: quando i team di sviluppo eseguono il deployment di agenti in progetti isolati senza un catalogo centralizzato, le organizzazioni perdono visibilità sugli strumenti e sui subagenti esistenti.
  • Egress cross-project non monitorato: consentire agli agenti percorsi di rete diretti e non ispezionati crea rischi di esfiltrazione dei dati e aggira i perimetri di sicurezza.
  • Integrazioni hardcoded fragili:l'hardcoding degli URL degli agenti downstream e degli ID del motore di ragionamento crea dipendenze fragili che si interrompono durante gli upgrade o i redeployment.
  • Mancanza di identità con privilegi minimi:i service account condivisi non forniscono la non ripudiabilità crittografica a livello di singola istanza dell'agente.

Per risolvere queste sfide, la Gemini Enterprise Agent Platform fornisce un piano di controllo unificato per la governance e la connettività composto da quattro pilastri fondamentali:

  1. Agent Gateway (networkservices.googleapis.com): un proxy di applicazione di policy e di rete regionale gestito. Operando in modalità di uscita AGENT_TO_ANYWHERE, intercetta il traffico in uscita dell'agente, delega le valutazioni dell'autorizzazione alle estensioni di sicurezza e instrada le richieste attraverso i perimetri del progetto.
  2. Agent Registry (agentregistry.googleapis.com): il catalogo dei servizi aziendali unico. Fornisce una directory centralizzata e verificata di tutti gli strumenti, i server MCP e gli agenti peer disponibili nell'organizzazione, consentendo l'individuazione automatica dinamica in fase di runtime con zero endpoint hardcoded.
  3. Agent Identity & IAP v2 Governance (iap.googleapis.com e iam.googleapis.com): un framework crittografico per la gestione di identità e accessi. Gli agenti di esecuzione ricevono URN macchina SPIFFE univoci e attestati (principal://...). L'uscita in uscita viene valutata in base alle Unified Access Policies IAM (UAP / IAP v2) che verificano l'autorizzazione universale iap.googleapis.com/resources.egressViaIAP utilizzando condizioni avanzate del catalogo Common Expression Language (CEL) (destination.agent_registry.*).
  4. Agent Runtime (motori di ragionamento): una piattaforma di esecuzione serverless completamente gestita per applicazioni agentiche basate su Python, con binding di configurazione nativi (agent_gateway_config) ai gateway centrali.

Scenario aziendale del codelab: acquisto di cibi e bevande per più progetti

In questo codelab, creerai e gestirai un ecosistema di acquisto multi-progetto reale che comprende tre progetti Google Cloud distinti:

  • Progetto di governance centrale (PROJECT_GOVERNANCE): di proprietà di IT centrale e SecOps, che ospita Agent Gateway centrale, Agent Registry centrale e le Unified Access Policies IAM.
  • Progetto orchestratore consumer (PROJECT_CONCIERGE): di proprietà del team di approvvigionamento, che ospita l'agente Concierge per gli acquisti che rileva dinamicamente i fornitori e indirizza gli ordini dei clienti.
  • Progetto fornitore di dominio (PROJECT_SELLERS): di proprietà di fornitori esterni o di reparti, che ospitano l'agente venditore di hamburger e l'agente venditore di pizza.

figure1

Fig. 1 Architettura di governance centralizzata multiprogetto

Perché la governance centralizzata tra progetti?

Nelle grandi organizzazioni aziendali, i team di prodotto e i gruppi di data science creano agenti AI in decine di progetti Google Cloud indipendenti. Se ogni team ha il controllo diretto della registrazione degli strumenti, delle route di rete in uscita e delle misure di protezione della sicurezza, si crea una proliferazione di strumenti non verificati, norme DLP incoerenti, uscita VPC non monitorata e audit log frammentati.

La governance centralizzata tra progetti separa la creazione delle policy dall'esecuzione degli agenti:

  • IT centrale e SecOps creano policy di sicurezza, verificano gli strumenti e monitorano il traffico in uscita all'interno di un unico progetto di governance centralizzata.
  • I team di prodotto e applicazione si concentrano esclusivamente sulla logica di business nei loro progetti Agent Runtime indipendenti, eseguendo il binding direttamente al gateway centrale senza l'overhead operativo della gestione di VPC locali, interconnessioni o Policy Engine frammentati.

figure2

Fig. 2 Architettura e limiti della governance cross-project a tre livelli

Modello di definizione dell'ambito dell'identità a due livelli in Unified Access Policies

Quando gli agenti comunicano tramite l'Agent Gateway centrale, Identity-Aware Proxy (IAP v2) valuta l'accesso in base all'identità dell'agente, un'identità basata su SPIFFE e attestata crittograficamente rilasciata automaticamente al container di runtime, rispetto a un criterio di accesso IAM globale:

  • Livello 1: API Google Cloud di base (generiche tramite principalSet:// nella regola 1): autorizzazione di uscita a livello di progetto che consente a tutti i runtime degli agenti nei progetti spoke di raggiungere le API Google standard (aiplatform, iamcredentials, telemetry, agentregistry) per l'individuazione, la generazione di token e l'inferenza.
  • Livello 2: Strumenti aziendali e servizi A2A (con granularità fine tramite principal:// nelle regole 2 e 3): accesso con privilegi minimi rigorosi associati a singole istanze di Reasoning Engine, applicato con condizioni Common Expression Language (CEL) che hanno come target servizi di Agent Registry specifici registrati (destination.agent_registry.agent.name).

Cosa crei

  • Agent Gateway centralizzato (centralized-agw) in PROJECT_GOVERNANCE
  • Estensione del servizio di autorizzazione IAP v2 e criterio di autorizzazione in modalità ENFORCE rigorosa (failOpen: false)
  • Policy Unified Access Policies IAM di base (uap-rules.json) e associazione di policy del progetto
  • Autorizzazioni IAM del service agent tra progetti (ar_agw_cross_project_sa)
  • Bucket temporaneo Google Cloud Storage (GCS) centrale condiviso
  • Agenti di vendita di hamburger e pizza isolati in PROJECT_SELLERS
  • Agente concierge per gli acquisti con rilevamento automatico dinamico di REST in PROJECT_CONCIERGE
  • Registrazioni dei servizi in Central Agent Registry con URL mTLS tra progetti
  • Aggiornamenti dinamici dei criteri in uscita IAP v2 con verifica live e audit di Cloud Logging

figure3

Fig. 3. Sequenza di implementazione passo passo

Cosa imparerai

  • Come configurare le autorizzazioni IAM del service agent tra progetti per i gateway centralizzati
  • Come instradare il traffico in uscita di Agent Runtime tramite un Agent Gateway centrale in ambienti multiprogetto
  • Come delegare l'autorizzazione di Agent Gateway a Identity-Aware Proxy (IAP v2) utilizzando Service Extensions (iapPolicyVersion: "V2")
  • Come creare e associare le Unified Access Policies (UAP) IAM con le regole Common Expression Language (CEL) che regolano le destinazioni di Agent Registry registrate (destination.agent_registry.*)
  • Come eliminare gli ID e gli URL degli agenti codificati in modo permanente utilizzando l'individuazione automatica in fase di runtime rispetto ad Agent Registry
  • Come testare il blocco zero-trust del perimetro reale (HTTP 403 Forbidden) e verificare gli aggiornamenti delle policy live in Cloud Logging

Cosa serve

  • 3 progetti Google Cloud con la fatturazione abilitata:
    • PROJECT_GOVERNANCE: criteri di governance centrale, gateway, registro e accesso IAM
    • PROJECT_CONCIERGE: Agente di orchestrazione del concierge per gli acquisti
    • PROJECT_SELLERS: Agenti di vendita specializzati in hamburger e pizza
  • Un utente IAM o un service account con autorizzazioni roles/owner o amministrative in tutti e tre i progetti
  • Un'organizzazione Google Cloud (per la mappatura del dominio di attendibilità SPIFFE)
  • Google Cloud Shell o una macchina locale con gcloud CLI, python (3.11+) e uv installati

Concludiamo la parte introduttiva e passiamo alla sezione Configurazione e ambiente.

2. Configurazione

Sebbene questa architettura si estenda su tre progetti Google Cloud distinti, puoi eseguire il 100% dei comandi di deployment del terminale, dei download dei repository e delle operazioni di gestione temporanea da un unico terminale Cloud Shell impostato su PROJECT_GOVERNANCE. Ogni script di deployment e comando gcloud ha come target esplicito il progetto di destinazione appropriato tramite i flag della CLI (--project).

Inizia accedendo alla riga di comando del tuo progetto Google Cloud:

Impostare il contesto del progetto

# 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

Imposta le variabili di ambiente della shell

Inserisci gli identificatori specifici del progetto.

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

Queste variabili della shell verranno derivate automaticamente.

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

Crea la directory locale per i file di configurazione

# create config folder
mkdir -p cfg

Assegnare il ruolo di amministratore delle policy di accesso per Unified Access Policies

# 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

Abilita gli audit log di accesso ai dati di Cloud per IAP v2

Per impostazione predefinita, Google Cloud disabilita gli audit log di accesso ai dati per evitare costi di archiviazione non intenzionali. Poiché IAP v2 emette decisioni di autorizzazione (granted=true e granted=false) come audit log di accesso ai dati, attiva la registrazione di ADMIN_READ, DATA_READ e DATA_WRITE per iap.googleapis.com in 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)"

Abilita le API Google Cloud richieste

# 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

Convalida l'abilitazione dell'API in tutti i progetti

Se tutti e tre i progetti (PROJECT_GOVERNANCE, PROJECT_CONCIERGE e PROJECT_SELLERS) hanno esattamente le stesse API abilitate, si stabilisce la coerenza operativa e si prevengono errori di creazione di token di runtime, errori di catalogazione dello schema o interruzioni della telemetria.

Esegui questo script di convalida in Cloud Shell per verificare la parità delle API in tutti e tre i progetti:

# 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

Esempio di output di convalida:

Dovresti vedere tutte le API abilitate.

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

Configura i criteri dell'organizzazione

Le policy dell'organizzazione Google Cloud predefinite applicano vincoli che limitano le associazioni di policy di accesso IAM v3 alle risorse (constraints/iam.managed.disableAccessPolicyBinding).

Ignora eventuali restrizioni delle policy dell'organizzazione ereditate a livello di progetto impostando esplicitamente enforce: false su Consenti.

# 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 parte di configurazione è terminata. Passiamo alla sezione Registra le API di Google Core.

3. Agent Registry

Registra il servizio endpoint delle API di Google di base

Agent Gateway richiede la registrazione degli URL delle API di Google nel registro centrale degli agenti in modo che gli agenti configurati con agent_gateway_config possano instradare il traffico in uscita in modo sicuro ai servizi di backend Google Cloud principali (come aiplatform, IAM Credentials e Telemetry).

Crea core-gapi-services in 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

Acquisire l'ID risorsa dell'endpoint delle API principali

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

Informazioni su principalSet e principal in Agent Identity

In Google Cloud IAM e nella Gemini Enterprise Agent Platform, le identità macchina rilasciate ai container degli agenti in esecuzione utilizzano URN SPIFFE con attestazione crittografica valutate da Identity-Aware Proxy (IAP v2). Quando configuri i criteri di accesso unificato IAM, puoi scegliere come target un singolo principal specifico o un principalSet basato su attributi:

Dimensione

principal:// (identità di una singola macchina)

principalSet:// (gruppo basato su attributi)

Sintassi IAM

principal://...

principalSet://...

Granularità

Granulare (a livello di istanza): identifica una singola istanza specifica del contenitore del motore di ragionamento.

Grossolana (a livello di progetto): identifica tutti i motori di ragionamento che condividono un attributo di progetto comune.

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

Caso d'uso in Agent Platform

Livello 2 (strumenti aziendali e A2A): autorizzazione di agenti orchestratori specifici a richiamare strumenti di dominio di destinazione (ad es. Concierge per gli acquisti $\rightarrow$ Venditore di hamburger).

Livello 1 (infrastruttura di base): concede a tutti gli agenti di un progetto l'accesso in uscita alle API Google Cloud (core-gapi-services).

Impatto del ciclo di vita

Se un agente viene eliminato e ricreato, il suo nuovo ID motore richiede un binding della policy IAM aggiornato.

Viene applicato automaticamente agli agenti appena implementati nel progetto senza ulteriori aggiornamenti IAM.

Governance dichiarativa con Unified Access Policies (UAP / IAP v2)

In IAP v1 legacy, le policy di uscita venivano associate direttamente alle singole risorse di Agent Registry utilizzando gcloud beta iap web add-iam-policy-binding. In IAP v2 e Unified Access Policies, i binding per risorsa vengono eliminati a favore di un unico criterio di accesso IAM centralizzato (cfg/uap-rules.json).

L'autorizzazione di uscita di base per core-gapi-services verrà configurata come Regola 1 nella policy di accesso unificato nella sezione 5, garantendo che tutti i container agent abbiano stabilito le route di uscita di base prima del deployment.

Per dettagli tecnici più approfonditi sugli identificatori delle entità e sul funzionamento di Workload Identity, consulta:

Con questo si conclude la registrazione dell'endpoint delle API di base. Passa alla sezione Esegui il deployment dell'Agent Gateway centralizzato.

4. Agent Gateway

Esegui il deployment di Agent Gateway centralizzato

Esegui il deployment di Agent Gateway centralizzato (centralized-agw) in modalità di uscita AGENT_TO_ANYWHERE all'interno del progetto $PROJECT_GOVERNANCE.

Definisci il manifest di configurazione del gateway

Crea cfg/${AGW_NAME}.yaml per la governance del traffico in uscita:

# 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

Importa la configurazione di 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}

Verifica i dettagli di Agent Gateway

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

Output di esempio:

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'

Il deployment del gateway è terminato. Passa alla sezione Configura l'autorizzazione.

5. Autorizzazione

Configura l'autorizzazione di Agent Gateway e l'UAP di base

Agent Gateway protegge e gestisce il traffico in uscita di strumenti e agenti utilizzando le policy di autorizzazione (networksecurity.authzPolicies) integrate con le Unified Access Policies (UAP) di Identity-Aware Proxy (IAP v2).

Panoramica dell'architettura di autorizzazione

figure4

Fig. 4. Panoramica dell'architettura di autorizzazione

L'architettura di autorizzazione è composta da tre livelli interconnessi:

  1. Estensione del servizio IAP (authzExtension): risorsa di regione configurata con service: iap.googleapis.com, metadata: iapPolicyVersion: "V2" e failOpen: false per l'applicazione rigorosa di Zero Trust perimetrale.
  2. Policy di autorizzazione gateway (authzPolicy): risorsa di regione che ha come target il tuo Agent Gateway con policyProfile: REQUEST_AUTHZ e action: CUSTOM, indirizzando i controlli di autorizzazione all'estensione di autorizzazione IAP.
  3. Policy e binding di accesso unificato IAM (accessPolicy e policyBinding): risorsa IAM globale v3 valutata da IAP. Verifica l'autorizzazione universale iap.googleapis.com/resources.egressViaIAP rispetto alle identità SPIFFE del chiamante e alle condizioni del catalogo CEL.

Passaggio 1: crea e importa l'estensione Authz IAP v2

Crea il manifest dell'estensione di servizio con iapPolicyVersion: "V2" e failOpen: false in modalità ENFORCE rigorosa:

# 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

Importa l'estensione di autorizzazione:

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

Verifica che l'estensione Authz sia attiva:

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

Output di esempio:

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

Passaggio 2: crea e importa i criteri di autorizzazione del gateway

Crea una configurazione di policy di autorizzazione che si colleghi ad Agent Gateway e deleghi la verifica delle richieste all'estensione di autorizzazione 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

Importa la policy di autorizzazione:

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

Verifica il criterio di autorizzazione attivo:

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

Passaggio 3: crea la prima policy Unified Access Policies (regola 1: API Google di base)

Crea cfg/uap-rules.json con la regola 1 che autorizza i tre progetti principalSet a raggiungere 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

Passaggio 4: crea e associa la policy di accesso IAM

Crea la policy di accesso 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

Collega la policy di accesso a 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

Verifica che l'associazione policy sia attiva:

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

Output di esempio:

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'uscita dell'API Google Cloud di base ora è autorizzata in modo sicuro in tutti e tre i progetti in modalità ENFORCE rigorosa.

La configurazione dell'autorizzazione del gateway è terminata. Passa alla sezione Configurare le autorizzazioni IAM tra progetti.

6. IAM tra progetti

Configurare le autorizzazioni IAM tra progetti

In questa topologia multi-progetto, gli Agent Runtime risiedono nei progetti spoke (PROJECT_CONCIERGE e PROJECT_SELLERS), mentre l'Agent Gateway centrale e l'Agent Registry risiedono in PROJECT_GOVERNANCE.

Poiché i progetti Google Cloud sono perimetri di sicurezza isolati, l'accesso tra progetti deve essere concesso esplicitamente su due livelli operativi:

  1. Control Plane (Deployment Time): quando viene eseguito il deployment di un container dell'agente configurato con --agent-gateway-config, l'agente di servizio Agent Runtime Service (service-@gcp-sa-aiplatform.iam.gserviceaccount.com) del progetto spoke deve collegare il container al gateway centrale. Creiamo un ruolo personalizzato minimo (ar_agw_cross_project_sa) che concede networkservices.agentGateways.use, get e operations.get in PROJECT_GOVERNANCE.
  2. Data Plane (esecuzione runtime):
    • Rilevamento catalogo:le identità spoke richiedono roles/agentregistry.viewer in PROJECT_GOVERNANCE per risolvere dinamicamente gli endpoint dell'agente di destinazione.
    • Invocation di destinazione:l'agente Concierge ha bisogno di roles/aiplatform.user in PROJECT_SELLERS per eseguire query sui motori di ragionamento del venditore.

Creare un ruolo IAM personalizzato in 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"

Assegnare il ruolo personalizzato ai service agent di 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

Con questo si conclude la configurazione IAM tra progetti. Passiamo alla sezione Deploy Seller & Concierge Agents.

7. Agent Runtime

Esegui il deployment degli agenti venditore e concierge

Il codebase dell'applicazione multiagente e gli script di deployment utilizzati per questo codelab vengono gestiti in un repository GitHub di Google Cloud remoto. I passaggi seguenti cloneranno il repository localmente, copieranno i file necessari nella struttura della directory di lavoro corrente, puliranno i file temporanei e installeranno le dipendenze con uv.

Recuperare artefatti remoti

# 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

Crea un bucket di staging centrale condiviso

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

Come funziona l'associazione dell'Agent Gateway tra progetti

In questo passaggio, eseguirai il deployment degli agenti venditore nel progetto spoke (PROJECT_SELLERS) configurandoli per instradare l'uscita tramite l'Agent Gateway centrale in 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)

Poiché la regola 1 è stata stabilita in precedenza nella nostra Unified Access Policy, le richieste di inizializzazione dei container alle API Google Cloud sono consentite tramite il gateway senza interruzioni.

Esegui il deployment degli agenti venditori di hamburger e pizza in 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}

Convalida del routing del gateway del venditore

# 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

Esegui il deployment dell'agente concierge per gli acquisti in 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}

Convalida il routing del gateway di acquisto

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

L'output deve mostrare l'identità e il progetto di runtime dell'agente Concierge e il binding all'Agent Gateway del progetto di governance.

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

Con questo si concludono i deployment degli agenti. Passiamo ora alla sezione Registrare gli agenti in Agent Registry centrale.

8. Registry tra progetti

Registrare gli agenti in Central Agent Registry

Registra tutti e tre gli agenti nell'Agent Registry centrale in PROJECT_GOVERNANCE utilizzando endpoint mTLS regionali tra progetti e numeri di progetto numerici.

Registrare i servizi come agenti non A2A in 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

Acquisire gli ID di Agent Registry sottostanti

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

Con questo si conclude la configurazione del registry. Passiamo ora alla sezione Configurare i criteri in uscita A2A.

9. Policy UAP

Configura le policy in uscita A2A in Unified Access Policy

Nell'architettura Nega per impostazione predefinita di Agent Gateway in modalità APPLICA rigorosa:

  1. Regola 1 (API Google Cloud di base): consente ai container agent in tutti e tre i progetti di raggiungere core-gapi-services.
  2. Regola 2 (agente venditore di hamburger: CONSENTI): consente all'istanza dell'agente Concierge per gli acquisti di richiamare specificamente l'agente venditore di hamburger.
  3. Agente venditore di pizza (NEGAZIONE per impostazione predefinita): escluso intenzionalmente dalle regole delle norme. In modalità ENFORCE (failOpen: false), qualsiasi tentativo del concierge di richiamare il venditore di pizza verrà interrotto immediatamente al perimetro del gateway con HTTP 403 Forbidden.

Formulare l'identità dell'agente 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}"

Aggiorna il manifest con le regole 1 e 2

Crea un nuovo cfg/uap-rules-update-2.json per includere la regola 1 (API di base) e ora la regola 2 (agente venditore di hamburger):

# 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

Applica policy di accesso aggiornata

# 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

Verifica i dettagli della policy di accesso IAM

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

Output di esempio:

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 configurazione delle policy è terminata. Passa alla sezione Testare e verificare le policy di governance.

10. Verificare i criteri

Testare e verificare le norme di governance tramite Cloud Logging

In questa sezione, testerai le interazioni tra agenti (A2A) tra progetti in Agent Runtime AI Playground, osserverai il blocco del perimetro HTTP 403 Forbidden in modalità ENFORCE rigorosa, modificherai la policy di accesso unificata in tempo reale e convaliderai l'approvazione immediata dell'ordine.

Passaggio 1: apri Agent Runtime AI Playground in PROJECT_CONCIERGE

  1. Apri la console Google Cloud.
  2. Nella barra del selettore di progetti in alto, passa a PROJECT_CONCIERGE.
  3. Nel menu di navigazione, vai a Agent Platform > Agenti > Deployment.
  4. Fai clic su purchasing-concierge-adk.
  5. Seleziona Playground per aprire l'interfaccia di chat interattiva sul lato destro dello schermo.

Passaggio 2: testa l'ordine di hamburger (corrispondenza con la regola 2 -> 200 OK)

Nella finestra della chat di Playground, invia il seguente prompt di ordine:

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

Se è necessaria una risposta di conferma, invia la seguente risposta:

Confirmed, please place the order.

In alternativa, esegui il test in modo programmatico da Cloud Shell / terminale:

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

Se è necessaria una risposta di conferma, usa questo comando:

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

Cosa succede dietro le quinte:

  1. Rilevamento dinamico:durante l'avvio della sessione, Purchasing Concierge ha eseguito una query nel Central Agent Registry in PROJECT_GOVERNANCE (tramite core-gapi-services tramite Agent Gateway autorizzato dalla regola 1) per rilevare l'endpoint mTLS regionale per burger-seller-agent.
  2. Risoluzione dell'intent e invocazione A2A: Gemini all'interno dell'agente di acquisto analizza l'intent di ordine di cibo e richiama l'agente venditore di hamburger tramite una RPC in uscita a https://${REGION}-aiplatform.mtls.googleapis.com/.../reasoningEngines/${BURGER_ENGINE_ID}.
  3. Intercettazione del gateway e propagazione SPIFFE: il traffico in uscita viene acquisito da agent_gateway_config e indirizzato all'Agent Gateway centrale in PROJECT_GOVERNANCE, con l'identità SPIFFE crittografica di Concierge (principal://...).
  4. Valutazione delle policy IAP v2:l'Agent Gateway centrale richiama l'estensione di autorizzazione IAP (authzExtension). IAP v2 valuta la regola 2 nella policy di accesso unificato IAM. Poiché il chiamante corrisponde a ${CONCIERGE_SPIFFE_PRINCIPAL} e la destinazione corrisponde a burger-seller-agent, IAP restituisce ALLOW (granted: true).
  5. Esecuzione tra progetti:l'Agent Gateway esegue il proxy della richiesta autorizzata tra progetti in PROJECT_SELLERS, dove il motore di ragionamento del venditore di hamburger elabora l'ordine e restituisce la conferma.

Risposta prevista:

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

Passaggio 3: esamina i log di controllo di Agent Gateway e IAP v2 (HTTP 200 / ALLOWED)

Esegui query sui log delle richieste di Agent Gateway in 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
  )"

I log devono acquisire il traffico in uscita proveniente da entrambi i progetti spoke (PROJECT_CONCIERGE e PROJECT_SELLERS) con campi di uscita per le chiamate di ragionamento di Gemini (generateContent), la telemetria di Cloud Trace (/v1/traces) e le ricerche di credenziali IAM, intercettate e autorizzate in modo trasparente dalla regola 1 (core-gapi-services).

Esegui una query sui log di accesso ai dati di Cloud Audit di IAP v2 per verificare la versione del criterio 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
  )"

Esempio di output:

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

Passaggio 4: testa l'ordine di pizza (negazione predefinita -> HTTP 403 Forbidden ENFORCED)

Nella stessa finestra della chat di Playground, invia il seguente prompt per l'ordine di una pizza:

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

Se è necessaria una risposta di conferma, invia la seguente risposta:

Confirmed, please place the order.

In alternativa, esegui il test in modo programmatico da Cloud Shell / terminale:

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

Se è necessaria una risposta di conferma, usa questo comando:

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

Risposta prevista:

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.

Cosa succede dietro le quinte:

  1. Rilevamento dinamico:Purchasing Concierge ha risolto l'endpoint pizza-seller-agent da Central Agent Registry durante l'avvio.
  2. Risoluzione dell'intent e chiamata A2A: Gemini all'interno del Concierge per gli acquisti tenta di inviare la richiesta di ordine di pizza all'endpoint del venditore di pizza in PROJECT_SELLERS.
  3. Intercettazione del gateway: la RPC in uscita viene acquisita da agent_gateway_config e indirizzata all'Agent Gateway centrale.
  4. Valutazione dei criteri IAP v2 (negazione predefinita): l'Agent Gateway centrale richiama IAP v2. Poiché nella Unified Access Policy non esiste nessuna regola corrispondente a pizza-seller-agent, IAP restituisce DENY (granted: false).
  5. Blocco perimetrale rigido:poiché l'estensione di autorizzazione è in modalità ENFORCE (failOpen: false), il gateway dell'agente centrale termina immediatamente la connessione in uscita e restituisce HTTP 403 Forbidden. Il traffico non esce mai dal gateway e non raggiunge mai PROJECT_SELLERS.

Passaggio 5: esamina i log di Agent Gateway per le richieste bloccate (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
  )"

Esempio di output del log di accesso negato:

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

Esegui una query sugli audit log di accesso ai dati di IAP v2 per la decisione di negazione:

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

Esempio di output del log di controllo negato:

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

Passaggio 6: concedi dinamicamente l'accesso in uscita all'agente Pizza

Crea nuovi cfg/uap-rules-update-3.json per includere la regola 1 (API di base), la regola 2 (agente venditore di hamburger) e ora la regola 3 (agente venditore di pizza)

# 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

Applica l'aggiornamento della policy in tempo reale:

# 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

Passaggio 7: esegui di nuovo la query Pizza Agent (200 OK immediato)

Nella finestra della chat di Playground, invia di nuovo il prompt dell'ordine della pizza:

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

Se è necessaria una risposta di conferma, invia la seguente risposta:

Confirmed, please place the order.

In alternativa, esegui il test in modo programmatico da Cloud Shell / terminale:

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

Se è necessaria una risposta di conferma, usa questo comando:

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

Risposta prevista:

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

Cosa succede dietro le quinte:

  1. Aggiornamento dinamico dei criteri:l'aggiornamento del criterio di accesso unificato IAM ha effetto immediato nel motore di valutazione IAP senza tempi di inattività e senza ridistribuire i container.
  2. Richiamo A2A:il Concierge invia la richiesta tramite l'Agent Gateway centrale.
  3. Valutazione (approvazione) delle norme IAP v2: IAP v2 corrisponde alla regola 3, verifica l'identità del chiamante e l'espressione CEL di destinazione e restituisce ALLOW (granted: true).
  4. Esecuzione cross-project:l'Agent Gateway centrale funge da proxy per il traffico autorizzato in PROJECT_SELLERS, dove il venditore di pizza elabora l'ordine.

Passaggio 8: esamina i log di Agent Gateway per le richieste di pizza concesse

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

Esempio di output del log di concessione:

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

Con questo si concludono i test e la verifica. Passiamo ora alla sezione Pulizia.

11. Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo codelab, esegui i passaggi di eliminazione nell'ordine inverso di dipendenza:

1. Pulire i deployment del motore di ragionamento

Esegui lo script cleanup_old_deployments.py incluso in entrambi i progetti di runtime per eliminare i motori di inferenza e attendi le operazioni a lunga esecuzione:

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

In alternativa, puoi elencare ed eliminare i motori di ragionamento in linea:

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. Elimina i servizi 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. Elimina l'associazione di policy Unified Access Policies IAM e la policy di accesso

# 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. Elimina l'Agent Gateway e le policy di sicurezza

# 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. Rimuovi le associazioni IAM tra progetti e il ruolo personalizzato

# 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

Se hai assegnato roles/iam.accessPolicyAdmin e roles/resourcemanager.projectIamAdmin durante la fase di configurazione, rimuovili dal tuo account utente attivo per ripristinare il principio del privilegio minimo:

# 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. Ripristinare la registrazione dei dati di controllo e i vincoli dei criteri dell'organizzazione

# 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. Ripristina i vincoli dei criteri dell'organizzazione

# 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. Elimina il bucket di staging GCS condiviso e gli artefatti locali

# 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

Con questo si conclude la parte di pulizia. Passiamo ora alla conclusione.

12. Conclusione

Complimenti! Hai eseguito il deployment e la gestione di un'architettura Agent-to-Agent (A2A) multi-progetto su Google Cloud utilizzando Vertex AI Agent Runtime, Central Agent Gateway, Agent Registry e IAM Unified Access Policies (UAP).

Riepilogo dei concetti chiave

  • Perimetro di uscita centralizzato: contenitori di runtime spoke (PROJECT_CONCIERGE, PROJECT_SELLERS) instradati tramite un gateway dell'agente centrale in PROJECT_GOVERNANCE utilizzando agentGatewayConfig.
  • Governance dichiarativa (UAP): ha sostituito le associazioni frammentate per risorsa con una singola policy di accesso IAM controllabile valutata al gateway da IAP v2.
  • Identità crittografica:uscita con privilegio minimo forzata utilizzando le identità SPIFFE del contenitore (principal://...) anziché chiavi di lunga durata.
  • Rilevamento dinamico dei servizi:risolve gli Agent Endpoint peer in fase di runtime tramite l'Agent Registry centrale, eliminando URL e ID progetto hardcoded.
  • Agilità delle policy di runtime:transizione di pizza-seller-agent da Default Deny (403 Forbidden) ad Allowed (200 OK) in tempo reale tramite l'aggiornamento delle policy, senza riavvii dei container.

cosmopup

Cosmopup dice: "Gli agenti sono fantastici: si occupano di tutto il lavoro interprogetto mentre io mi concentro sul mio obiettivo principale: fare un pisolino!"

Passaggi successivi e documentazione