1. Introduction
Gemini Enterprise Agent Platform is an open platform for building, scaling, governing, and optimizing enterprise-grade AI agents grounded in your data.
Agent Runtime provides the managed execution environment for running agents, such as those built with the open-source Agent Development Kit (ADK), securely within Google Cloud.
This codelab explores how to use these core building blocks to govern an agent initiated by a user in Gemini Enterprise as it securely reaches out to internal tools.
About Agent Gateway
Agent Gateway is the networking component of the platform's Agent Governance suite. It acts as the network entry and exit point for all agent interactions, allowing security administrators to enforce centralized governance without requiring developers to manage complex networking primitives.
It facilitates two primary governed access paths:
- Client-to-Agent (ingress): Secures communications between external clients (like Cursor or the Gemini CLI) and your agents.
- Agent-to-Anywhere (egress): Secures communications between agents running on Google Cloud and servers, tools, or APIs running anywhere.
In this codelab, you will focus on the Agent-to-Anywhere (egress) mode.

To enforce security policies, Agent Gateway integrates tightly with the rest of the ecosystem:
- Agent Registry: A central library of approved agents and tools (including third-party MCP servers).
- Agent Identity: A unique, trackable persona for every agent, secured automatically with end-to-end mTLS.
- Identity-Aware Proxy (IAP) & IAM: The default enforcement layer that validates the agent's identity against fine-grained IAM permissions before allowing calls to specific tools.
- Model Armor: An AI security guardrail integrated via Service Extensions to sanitize content and protect against prompt injection attacks or data leakage.
Deployment modes (Public vs. Private networking for Cloud Run)
To make this codelab accessible, you can choose between two networking paths for your internal tools (MCP servers) deployed on Cloud Run:
- Default (Public Ingress): The MCP servers are deployed to Cloud Run with public hostnames (
ingress=all). Traffic routes from the agent to the tools via standard*.run.appURLs. This requires no custom DNS domains and is the fastest way to learn the governance concepts. - Secure (Private Networking): An optional, fully private architecture. The MCP servers are restricted (
ingress=internal-and-cloud-load-balancing) and exposed via an Internal Application Load Balancer with a Serverless NEG. This requires you to own a public DNS domain to provision a Google-managed certificate.
You will select your preferred path when configuring Terraform.
To learn more about network endpoint ingress for Cloud Run, please read our docs.
What you'll do
- Provision the core infrastructure stack using Terraform
- Build and deploy internal tools as MCP servers on Cloud Run
- Deploy an ADK agent to Agent Runtime using PSC Interface egress
- Configure Agent Gateway service extensions for identity-based access (IAM) and content screening (Model Armor)
- Trace and validate the secure end-to-end execution of the agent
What you'll need
- A web browser such as Chrome
- A Google Cloud project with billing enabled and Owner access
- Organization-level IAM permissions (the codelab grants org-scoped roles)
- A domain you control delegated to Cloud DNS (for the public managed certificate)
- Familiarity with Terraform,
gcloud, and basic Google Cloud networking
Codelab topology

In this codelab, you will deploy an end-to-end mortgage underwriting agent that securely communicates with three internal tools.
You'll start by provisioning the foundational networking, including a VPC and an internal Application Load Balancer configured as your Agent Gateway. Next, you'll deploy three Model Context Protocol (MCP) servers to Cloud Run. These act as your internal proprietary tools:
- Document Management (
legacy-dms) - Corporate Email (
corporate-email) - Income Verification (
income-verification)
With the tools in place, you will deploy a Mortgage Assistant (mortgage-agent) built with the ADK to Agent Runtime. You will configure this agent to use a PSC Interface for private egress and enable runtime tool discovery via the Agent Registry.
To secure the flow, you will configure your Agent Gateway with two service extensions. First, a REQUEST_AUTHZ extension will verify the Agent Identity against per-tool IAM policies, ensuring the agent only accesses authorized tools. Second, a CONTENT_AUTHZ extension using Model Armor will screen the agent's prompts and responses.
Finally, you'll register the agent in Gemini Enterprise, trigger a mortgage-underwriting task as an end user, and verify the secure, governed execution using Cloud Trace.
This codelab is for platform and security engineers of all levels. Expect to spend roughly 100 minutes completing it.
2. Before you begin
Create a project and authenticate
Create a new GCP project (or reuse one) with billing enabled, then authenticate Cloud Shell or your local machine:
gcloud auth login
gcloud auth application-default login
gcloud config set project <your-project-id>
Enable bootstrap APIs
Terraform's foundation module enables ~30 APIs on its first apply, but a small bootstrap set is required for terraform init and the GCS state bucket:
gcloud services enable \
compute.googleapis.com \
serviceusage.googleapis.com \
cloudresourcemanager.googleapis.com \
iam.googleapis.com \
storage.googleapis.com \
dns.googleapis.com
Install required tools
Install the toolchain. On Cloud Shell most of these are already present; on a workstation:
# uv (Python package manager)
curl -LsSf https://astral.sh/uv/install.sh | sh
# skaffold
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \
sudo install skaffold /usr/local/bin/
# envsubst (gettext)
sudo apt-get install -y gettext-base
You also need Terraform >= 1.12.2, Python 3.12+, and the Google Cloud SDK (gcloud).
Set environment variables
The rest of the codelab assumes these are exported in your shell.
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
export ORG_ID=$(gcloud projects get-ancestors $PROJECT_ID | awk '$2 == "organization" {print $1}')
export REGION="us-central1"
# Only required if using the secure private networking path
export DOMAIN_NAME="agw.example.com"
Validate that all of your variables populated correctly, you should three values returned.
echo $PROJECT_ID
echo $PROJECT_NUMBER
echo $ORG_ID
If your Organization ID doesn't populate you can find it and set it manually.
gcloud organizations list
export ORG_ID=ID_FROM_OUTPUT
3. Clone the repository
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git
cd cloud-networking-solutions
cd demos/agent-gateway
A quick tour of what's in the demo directory:
src/ MCP servers (legacy-dms, corporate-email, income-verification-api) + mortgage-agent
terraform/ Root Terraform config + modules (foundation, networking, agent-gateway, model-armor, ...)
cloudrun/ Cloud Run service definitions (rendered from .yaml.tmpl via envsubst)
scripts/ grant_agent_mcp_egress.sh — per-MCP IAP egressor binding
skaffold.yaml.tmpl Skaffold pipeline that builds + deploys all three MCP services to Cloud Run
4. Create the Terraform state bucket and backend config
Create a GCS bucket to hold remote state, then copy the backend template:
gcloud storage buckets create gs://${PROJECT_ID}-tfstate \
--location=${REGION} \
--uniform-bucket-level-access
cp terraform/example.backend.conf terraform/backend.conf
Edit terraform/backend.conf with your values:
bucket = "<your-project-id>-tfstate"
prefix = "agent-gateway"
5. (Optional) Create a public Cloud DNS zone
By default for this lab Cloud Run has its ingress configuration set to all and the Agent Registry registers each MCP server at its public *.run.app URL — no additional DNS, certificates, or load balancer required. If you'd like to switch to private networking (Cloud Run with ingress = internal-and-cloud-load-balancing behind an internal Application LB), you also need a public Cloud DNS zone so Certificate Manager can validate the LB cert.
High level flow of private networking

To use the private networking approach:
- Create the public Cloud DNS zone — Certificate Manager validates the regional managed certificate by writing CNAMEs into it:
gcloud dns managed-zones create agw-example-com \
--dns-name="${DOMAIN_NAME}." \
--description="Public zone for ${DOMAIN_NAME}" \
--visibility=public
The corresponding private zone for mcp.${DOMAIN_NAME} (used by the MCP internal LB and DNS peering from Agent Runtime) is created automatically by Terraform — you don't need to create it by hand. With private networking off, neither the public nor the private zone is provisioned.
6. Configure Terraform variables
Copy the example tfvars and edit it:
cp terraform/example.tfvars terraform/terraform.tfvars
There are two demo paths, gated by enable_cloud_run_private_networking.
Default path: Cloud Run with public ingress
The simplest setup: For the default path you only need to edit three values in terraform.tfvars, Every other variable in the file already has a demo-friendly default.
# GCP project ID where all resources will be created.
project_id = "my-gcp-project-id"
# GCP organization ID (numeric).
organization_id = "123456789012"
# Members granted demo-wide roles
platform_admin_members = ["user:admin@example.com"]
# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = "DRY_RUN"
Private networking (optional)
Set enable_cloud_run_private_networking = true and add the variables below to provision the full secure stack:
- Internal Application LB
- Google-managed cert
- Cloud Run with
ingress = internal-and-cloud-load-balancing - Agent Gateway DNS peering.
enable_cloud_run_private_networking = true
# DNS — must end with a trailing dot, must match a Cloud DNS zone you own
dns_zone_domain = "agw.example.com."
enable_certificate_manager = true
# mcp_internal_dns_zone.domain MUST be a real subdomain of dns_zone_domain so
# Certificate Manager can issue a Google-managed cert.
mcp_internal_dns_zone = {
name = "mcp-server-internal"
domain = "mcp.agw.example.com."
}
# Must match mcp_internal_dns_zone.domain so Agent Engine resolves MCP
# hostnames over the PSC interface peering.
psc_interface_dns_zone = {
name = "mcp-server-internal"
domain = "mcp.agw.example.com."
}
mcp_lb_protocol = "HTTPS"
7. Deploy infrastructure with Terraform
Initialize, review, and apply:
cd terraform
terraform init -backend-config=backend.conf
terraform plan -out=tfplan
terraform apply tfplan
terraform apply provisions ~40 resources on the default path and takes 8–10 minutes on a fresh project (~60 resources / 15–20 minutes when enable_cloud_run_private_networking = true). It creates:
- Project foundation (APIs, service identities, quotas)
- VPC, subnets (primary, proxy-only, PSC, PSC-Interface, Agent Gateway co-location), Cloud NAT, firewall rules
- Artifact Registry repo for Cloud Run images
- Three Cloud Run services + per-service runtime SAs (ingress =
allby default;internal-and-cloud-load-balancingwhen private networking is on) - Model Armor template + IAM
- Agent Gateway, PSC-I network attachment, IAP and Model Armor extensions, both authorization policies, and the project-level
roles/iap.egressorgrant - Agent Registry endpoints (Vertex AI, IAP, Discovery Engine, ...) plus the three MCP servers (registered at
*.run.app/mcpby default; atwhen private networking is on). /mcp
Only when enable_cloud_run_private_networking = true:
- Internal regional Application LB with serverless NEG (URL-mask routing) + private DNS A records
- MCP private DNS zone (
mcp.) attached to the VPC. - Public DNS zone module (Certificate Manager DNS authorizations) + Regional Google-managed certificate
- PSC Interface DNS zone (orphan when there are no private hostnames to resolve, so it's also gated on the master flag)
- Agent Gateway DNS peering for
mcp.(auto-prepended).
8. Inspect the Agent Registry endpoints
The Agent Registry is a per-project catalog of services (Google APIs and your own MCP servers) that an agent discovers at runtime. The mortgage-agent reads it on startup and binds tools dynamically — no MCP URLs are baked into the agent code or its deploy command.
Endpoints
What Terraform ran on your behalf — for each Google API in agent_registry_google_apis, it registered five variants (global, mTLS global, regional, regional mTLS, regional REP). For example, for aiplatform:
gcloud alpha agent-registry services create aiplatform \
--project=${PROJECT_ID} --location=${REGION} \
--display-name="Vertex AI Platform" \
--endpoint-spec-type=no-spec \
--interfaces="url=https://aiplatform.googleapis.com,protocolBinding=JSONRPC"
gcloud alpha agent-registry services create aiplatform-mtls \
--project=${PROJECT_ID} --location=${REGION} \
--display-name="Vertex AI Platform mTLS" \
--endpoint-spec-type=no-spec \
--interfaces="url=https://aiplatform.mtls.googleapis.com,protocolBinding=JSONRPC"
gcloud alpha agent-registry services create ${REGION}-aiplatform \
--project=${PROJECT_ID} --location=${REGION} \
--display-name="Vertex AI Platform Locational" \
--endpoint-spec-type=no-spec \
--interfaces="url=https://${REGION}-aiplatform.googleapis.com,protocolBinding=JSONRPC"
gcloud alpha agent-registry services create aiplatform-${REGION}-rep \
--project=${PROJECT_ID} --location=${REGION} \
--display-name="Vertex AI Platform Regional (REP)" \
--endpoint-spec-type=no-spec \
--interfaces="url=https://aiplatform.${REGION}.rep.googleapis.com,protocolBinding=JSONRPC"
MCP Servers
The Terraform also registers the 3 MCP Servers for you, to register other MCP servers you can follow the steps in the documentation.
gcloud alpha agent-registry services create legacy-dms \
--project=${PROJECT_ID} \
--location=${REGION} \
--display-name="Legacy DMS" \
--mcp-server-spec-type=tool-spec \
--mcp-server-spec-content=src/