A minimal working example on Google Cloud's Gemini Enterprise Agent Platform (ex–Vertex AI): a dummy ADK agent deployed to Agent Runtime behind an ingress Agent Gateway, with a Model Armor template attached and enforcing on that gateway.
Everything described below is in this repository and was applied against a live GCP project. Nothing is aspirational — features that are not implemented are listed explicitly under Not included and Private Preview dependencies.
terraform/main.tf Model Armor template, both gateways, VPC + PSC attachment, APIs
terraform/iam_model_armor.tf Service-agent IAM, Model Armor authz extension + authz policy
terraform/terraform.tfvars.example Copy to terraform.tfvars and fill in
terraform/.terraform.lock.hcl Provider pins (google + google-beta 7.41.0)
agent/dummy_agent/agent.py The ADK agent: one Gemini LlmAgent + one echo tool
agent/deploy.py Deploys the agent to Agent Runtime, bound to the ingress gateway
agent/showcase.py Sends a benign prompt and a jailbreak prompt to the deployed agent
agent/requirements.txt google-cloud-aiplatform[adk,agent_engines]>=1.112
No Terraform state, provider cache, or virtualenv is shipped.
Terraform (terraform/)
| Resource | Detail |
|---|---|
google_model_armor_template.demo |
RAI filters (hate speech, harassment, sexually explicit, dangerous — all MEDIUM_AND_ABOVE), prompt-injection/jailbreak filter, malicious-URI filter, basic Sensitive Data Protection |
google_network_services_agent_gateway.ingress |
CLIENT_TO_AGENT, governs client → agent traffic |
google_network_services_agent_gateway.egress |
AGENT_TO_ANYWHERE, provisioned but not bound to the agent |
google_compute_network / _subnetwork / _network_attachment |
VPC 10.10.0.0/24 + PSC-Interface attachment, required only by the egress gateway |
google_network_services_authz_extension.model_armor |
Model Armor as a Google-API callout, EXT_PROC_GRPC, fail_open = false |
google_network_security_authz_policy.model_armor_binding |
CUSTOM / CONTENT_AUTHZ, binds the extension to the ingress gateway |
google_project_iam_member.* |
Service-agent grants (see IAM below) |
google_project_service.apis |
Enables aiplatform, networkservices, networksecurity, agentregistry, modelarmor, compute, iam |
Agent (agent/) — an ADK LlmAgent on gemini-2.5-flash with a single
echo_tool. The agent code contains no gateway references: gateway routing is a
deployment-time binding on the Agent Runtime instance (agent_gateway_config in
deploy.py), not application code. The deployment sets
identity_type = AGENT_IDENTITY, which is required for gateway-governed agents.
Applied and run end-to-end on a disposable GCP project in us-central1 with
provider 7.41.0:
terraform applycreates all resources listed above.deploy.pydeploys the agent and the ingress binding succeeds.showcase.py: the benign prompt returns a normal answer; the jailbreak/prompt-injection prompt is rejected at the gateway with403 "Model Armor: Prompt violates content security configurations".
Only prompt-side screening was exercised. The template is attached as both request and response template, but response-side blocking was not triggered during testing.
- No egress binding. The egress gateway is created but the agent is not
bound to it. The
agent_to_anywhere_configline indeploy.pyis commented out — see Private Preview dependencies. - No Agent Registry destination registration. No Terraform resource exists for it in provider 7.x.
- No client authorization policy (IAP) on the ingress gateway. Access to the agent is plain project IAM; the gateway does content screening only.
- No staging bucket.
deploy.pyexpectsSTAGING_BUCKETto already exist. - No remote state backend. The Terraform uses local state.
- No CI, tests, or teardown automation.
- No least-privilege custom IAM role. A commented-out alternative is in
terraform/iam_model_armor.tf; it requiresroles/iam.roleAdminon the deploying identity, so the stack uses predefined roles instead.
These require allowlisting by Google; the repo cannot work around them.
- Binding an Agent Runtime instance to an Agent Gateway. The gateway
resources themselves apply on a normal project, but
deploy.pywill fail without the allowlist. - Registering egress destinations in Agent Registry. Binding the agent to
the egress gateway forces all outbound traffic through it, and every
destination not registered is blocked — including the Gemini endpoint, so the
container fails to start (
503 UNAVAILABLE ... Handshake read failed). Registration is a separate Private Preview capability with no Terraform resource, which is why the egress binding is left commented out.
- One ingress and one egress gateway per project + region.
- An agent cannot be unbound from a gateway once deployed. Use a disposable project.
- No cross-region. The Model Armor template, the gateways and the agent must
all be in the same region. Validated on
us-central1.
cd terraform
cp terraform.tfvars.example terraform.tfvars # set project_id
terraform init
terraform applyOn a brand-new project, enable two bootstrap APIs once first (Terraform enables the rest):
gcloud services enable cloudresourcemanager.googleapis.com serviceusage.googleapis.com --project=<PROJECT_ID>cd agent
python -m venv .venv && .venv/bin/pip install -r requirements.txt
export GOOGLE_CLOUD_PROJECT=<PROJECT_ID>
export GOOGLE_CLOUD_LOCATION=us-central1
export STAGING_BUCKET=gs://<PROJECT_ID>-agent-staging # must exist; create it first
.venv/bin/python deploy.py.venv/bin/python showcase.pyExpected output: the benign prompt returns a normal answer; the jailbreak prompt
is rejected with 403 PERMISSION_DENIED.
- Auth. Both scripts use Application Default Credentials
(
gcloud auth application-default login). The Python SDK does not honourGOOGLE_IMPERSONATE_SERVICE_ACCOUNT; to run as a service account, create animpersonated_service_accountADC file and pointGOOGLE_APPLICATION_CREDENTIALSat it. - IAM.
iam_model_armor.tfgrantsroles/networkservices.adminto both the Reasoning Engine service agent (gcp-sa-aiplatform-re) and the Vertex AI service agent (gcp-sa-aiplatform). The second one is not optional: the deploy fails with "Permission denied to get Agent Gateway ... ensure Vertex AI Service Agent has networkservices.agentGateways.get" without it. The Service Extensions service agent (gcp-sa-dep) gets the Model Armor callout roles. - Model Armor attachment is two resources, not a gateway field: the authz
extension plus the
CONTENT_AUTHZauthz policy that binds it. Without the policy the extension is never invoked.load_balancing_schememust be left unset on both — an Agent Gateway is not a load balancer, andINTERNAL_MANAGEDis rejected by the authz policy.authoritymust not be set on extensions that call Google APIs. - Extension metadata keys are
request_template_id/response_template_id. Other key names are accepted by the API but silently ignored, which leaves the extension attached and screening nothing.