Chapter 11
Get started with Agent Engine Terraform Deployment
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.Get started with Agent Engine Terraform Deployment
| Authors |
|---|
| Ivan Nardini |
| Luca Prete |
| Yee Sian Ng |
Overview
Deploying AI agents on Vertex AI Agent Engine through Terraform provides a powerful infrastructure-as-code (IaC) approach to manage your agent deployments. Instead of clicking through the UI or writing custom API calls, you can define your entire Agent Engine deployment in configuration files that are version-controlled, repeatable, and easily automated.
Vertex AI Agent Engine is a fully managed, serverless platform for deploying and hosting AI agents. You can build agents using custom Python classes following the Agent Engine template pattern, or use popular frameworks like the Agent Developer Kit (ADK), LangChain, or LlamaIndex. The platform handles scaling, infrastructure management, and provides enterprise-grade security and monitoring.
The Vertex AI Reasoning Engine Terraform resource simplifies deploying AI agents by providing a declarative configuration interface. With Terraform, you can package your agent code, manage dependencies, configure compute resources, and maintain consistent deployments across environments—all through simple configuration files.
This tutorial shows how to use Terraform to deploy AI agents on Vertex AI Agent Engine.
What you will learn
You will learn how to:
- Set up Terraform for Vertex AI Agent Engine deployments
- Create custom agents according to the Vertex AI Agent Engine
- Package your Python agent code using cloudpickle
- Deploy your first agent using Terraform
- Handle dependencies and requirements
- Deploy ADK (Agent Development Kit) agents with function calling
- Clean up resources efficiently
Get started
Prerequisites
Before you begin, ensure you have:
- A Google Cloud project with billing enabled
- The Vertex AI API enabled
- Sufficient IAM permissions (Vertex AI Administrator or Editor role)
- A Cloud Storage bucket for storing agent artifacts
Install Vertex AI SDK and other required packages
%pip install --upgrade --quiet 'google-cloud-aiplatform[agent_engines]>=1.120.0' 'google-adk>=1.15.1' 'cloudpickle>=3.0.0'Install Terraform
If you don't have Terraform installed, download and install it from terraform.io.
# For Linux distributions
! wget https://releases.hashicorp.com/terraform/1.13.3/terraform_1.13.3_linux_amd64.zip
! unzip terraform_1.13.3_linux_amd64.zip
! sudo mv terraform /usr/local/bin/
# Verify installation
! terraform versionAuthenticate your notebook environment (Colab only)
If you're running this notebook on Google Colab, run the cell below to authenticate your environment.
import sys
if "google.colab" in sys.modules:
from google.colab import auth
auth.authenticate_user()Set Google Cloud project information
To get started using Vertex AI, you must have an existing Google Cloud project and enable the Vertex AI API.
Learn more about setting up a project and a development environment.
# Use the environment variable if the user doesn't provide Project ID.
import os
# fmt: off
PROJECT_ID = "[your-project-id]" # @param {type: "string", placeholder: "[your-project-id]", isTemplate: true}
if not PROJECT_ID or PROJECT_ID == "[your-project-id]":
PROJECT_ID = str(os.environ.get("GOOGLE_CLOUD_PROJECT"))
LOCATION = os.environ.get("GOOGLE_CLOUD_REGION", "us-central1")
# Agent Engine requires your agent artifacts (pickle file and requirements) to be stored in Cloud Storage.
BUCKET_NAME = "[your-bucket-name]" # @param {type: "string", placeholder: "[your-bucket-name]", isTemplate: true}
# fmt: onImport libraries
import sys
import vertexaiCreate a Terraform workspace
Create a new directory for your Terraform configurations.
# Create a directory for your Terraform project
! rm -rf ./agent-engine-terraform && mkdir ./agent-engine-terraformUnderstanding Agent Engine
Vertex AI Agent Engine is a serverless platform that allows you to deploy and host AI agents without managing infrastructure. Key benefits include:
- Code-first approach: Build agents using familiar Python frameworks
- Serverless: No infrastructure management required
- Scalable: Automatic scaling based on demand
- Integrated: Works seamlessly with Vertex AI services
- Secure: Enterprise-grade security and access controls
Below you have the steps to cover in order to deploy an agent on Agent Engine:
- Build: Create your agent as a Python class with
__init__(),set_up(), andquery()methods following the custom agent template pattern, or use ADK - Package: Serialize your agent instance with cloudpickle (ensuring the object is pickle-able) and store in Cloud Storage
- Deploy: Use Terraform to create a Reasoning Engine resource that references your packaged agent
- Query: Invoke your agent's
query()method via API or SDK
The deployment uses a google_vertex_ai_reasoning_engine Terraform resource that references your packaged agent code.
Deploy your first custom agent
Let's deploy a custom agent using Terraform following the Vertex AI Agent Engine custom agent pattern.
Create the agent code
Create a custom agent class following the Vertex AI Agent Engine template pattern. Agent templates are defined as Python classes with three key methods:
__init__(): For agent configuration parameters (must be pickle-able)set_up(): For initialization logic like establishing connections or importing packagesquery(): To return the complete response as a single result
! mkdir -p ./agent-engine-terraform/01-basic-agent/agent# Init file
with open("./agent-engine-terraform/01-basic-agent/agent/__init__.py", "w"):
pass
# Create a custom agent following the Vertex AI Agent Engine pattern
simple_agent_code = """
from typing import Dict
class SimpleAgent:
def __init__(
self,
model: str,
project: str,
location: str,
):
self.model_name = model
self.project = project
self.location = location
def set_up(self):
import vertexai
from vertexai.generative_models import GenerativeModel
vertexai.init(project=self.project, location=self.location)
self.model = GenerativeModel(self.model_name)
def query(self, input: str) -> Dict:
'''Simple agent that uses Gemini to respond to queries.
Args:
input: The user's query string
Returns:
A dictionary containing the agent's response
'''
response = self.model.generate_content(
f"You are a helpful AI assistant. Respond to: {input}"
)
return {"output": response.text}
"""
# Write agent code to file
with open("./agent-engine-terraform/01-basic-agent/agent/simple_agent.py", "w") as f:
f.write(simple_agent_code)Create the pickle file
Package your agent using cloudpickle. The agent object must be "pickle-able".
serialize_agent_code = f"""
import cloudpickle
from agent.simple_agent import SimpleAgent
# Create and initialize agent instance
agent = SimpleAgent(
model="gemini-2.5-flash",
project="{PROJECT_ID}",
location="{LOCATION}",
)
agent.set_up()
# Serialize the agent instance
pickle_file = "./agent.pkl"
with open(pickle_file, "wb") as f:
cloudpickle.dump(agent, f)
"""
with open("./agent-engine-terraform/01-basic-agent/serialize_agent.py", "w") as f:
f.write(serialize_agent_code)
! cd ./agent-engine-terraform/01-basic-agent && python serialize_agent.pyPackage dependencies
Create a requirements.txt file.
requirements = """
google-cloud-aiplatform[agent_engines]>=1.120.0
google-adk>=1.15.1
cloudpickle==3.0.0
"""
requirements_file = "./agent-engine-terraform/01-basic-agent/requirements.txt"
with open(requirements_file, "w") as f:
f.write(requirements)If you have a custom agent or additional dependecies, create an empty dependencies.tar.gz file.
! cd ./agent-engine-terraform/01-basic-agent/ && tar -czf dependencies.tar.gz agentWrite Terraform configuration
Create the Terraform configuration file.
Note: It is important you define the class methods (operations) that the agent supports. In this case, we register just the query which allows you to query the agent with the given input and config.
basic_deploy_config = """
# Configure the Terraform provider
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "7.6.0"
}
}
}
# Configure the Google Cloud provider
provider "google" {
project = var.project_id
region = var.region
}
# Define variables
variable "project_id" {
description = "Google Cloud Project ID"
type = string
}
variable "region" {
description = "Google Cloud region"
type = string
default = "us-central1"
}
variable "gcs_bucket_name" {
description = "Cloud Storage bucket name"
type = string
}
# Define the class methods (operations) that the agent supports
locals {
class_methods = [
{
api_mode = ""
name = "query"
description = "Queries the agent with the given input"
parameters = {
type = "object"
required = ["input"]
properties = {
input = {
type = "string"
}
}
}
}
]
}
# Define resources
resource "google_storage_bucket" "bucket" {
name = var.gcs_bucket_name
location = var.region
uniform_bucket_level_access = true
force_destroy = true
}
resource "google_storage_bucket_object" "bucket_obj_requirements_txt" {
name = "requirements.txt"
bucket = google_storage_bucket.bucket.id
source = "./requirements.txt"
}
resource "google_storage_bucket_object" "bucket_obj_pickle_pkl" {
name = "agent.pkl"
bucket = google_storage_bucket.bucket.id
source = "./agent.pkl"
}
resource "google_storage_bucket_object" "bucket_obj_dependencies_tar_gz" {
name = "dependencies.tar.gz"
bucket = google_storage_bucket.bucket.id
source = "./dependencies.tar.gz"
}
# Deploy agent to Vertex AI Agent Engine
resource "google_vertex_ai_reasoning_engine" "reasoning_engine" {
display_name = "simple_agent"
description = "A simple agent deployed with Terraform"
region = var.region
spec {
class_methods = jsonencode(local.class_methods)
package_spec {
dependency_files_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_dependencies_tar_gz.name}"
pickle_object_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_pickle_pkl.name}"
python_version = "3.12"
requirements_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_requirements_txt.name}"
}
}
}
# Output the reasoning engine information
output "reasoning_engine_id" {
description = "The ID of the deployed reasoning engine"
value = google_vertex_ai_reasoning_engine.reasoning_engine.name
}
output "reasoning_engine_resource" {
description = "The full resource name"
value = google_vertex_ai_reasoning_engine.reasoning_engine.id
}
"""
with open("./agent-engine-terraform/01-basic-agent/main.tf", "w") as f:
f.write(basic_deploy_config)Create variables file
Create a terraform.tfvars file with your project-specific values.
deploy_vars = f"""
project_id = "{PROJECT_ID}"
region = "{LOCATION}"
gcs_bucket_name = "{BUCKET_NAME}-custom-agent"
"""
with open("./agent-engine-terraform/01-basic-agent/terraform.tfvars", "w") as f:
f.write(deploy_vars)Deploy with Terraform
Run the Terraform commands to deploy your agent.
Note: The deployment typically takes around 5 minutes. Terraform will show you the planned changes and ask for confirmation.
# Initialize Terraform
! cd ./agent-engine-terraform/01-basic-agent && terraform init
# Plan the deployment
! cd ./agent-engine-terraform/01-basic-agent && terraform plan
# Apply the configuration (will prompt for confirmation)
! cd ./agent-engine-terraform/01-basic-agent && terraform applyVerify the deployment
After deployment completes, retrieve the outputs.
! cd ./agent-engine-terraform/01-basic-agent && terraform outputTest your deployed agent
Query the agent using the Vertex AI SDK.
# fmt: off
agent_engine_resource_name = "[your-agent-engine-resource-name]" # @param {type: "string", placeholder: "[your-agent-engine-resource-name]"}
# fmt: onclient = vertexai.Client(
project=PROJECT_ID,
location=LOCATION,
)
agent = client.agent_engines.get(name=agent_engine_resource_name)response = agent.query(input="What is artificial intelligence?")
print(f"\nAgent response:\n{response['output']}")Deploy an ADK agent with tools
This example deploys an ADK (Agent Development Kit) agent with function calling capabilities.
Create the ADK agent
Agent Development Kit provides some prebuilt class to build agents. In this scenario we use LlmAgent class to build an agent capable of providing information about the currency rates.
! rm -rf ./agent-engine-terraform/02-adk-agent && mkdir -p ./agent-engine-terraform/02-adk-agent/currency_agentwith open("./agent-engine-terraform/02-adk-agent/currency_agent/__init__.py", "w") as f:
f.write("from .agent import root_agent")
adk_agent_code = """
from google.adk.agents import LlmAgent
def get_exchange_rate(
currency_from: str = "USD",
currency_to: str = "EUR",
currency_date: str = "latest",
):
'''Retrieves the exchange rate between two currencies on a specified date.
Uses the Frankfurter API (https://api.frankfurter.app/) to obtain
exchange rate data.
Args:
currency_from: The base currency (3-letter currency code).
Defaults to "USD" (US Dollar).
currency_to: The target currency (3-letter currency code).
Defaults to "EUR" (Euro).
currency_date: The date for which to retrieve the exchange rate.
Defaults to "latest" for the most recent exchange rate data.
Can be specified in YYYY-MM-DD format for historical rates.
Returns:
dict: A dictionary containing the exchange rate information.
Example: {"amount": 1.0, "base": "USD", "date": "2023-11-24",
"rates": {"EUR": 0.95534}}
'''
import requests
response = requests.get(
f"https://api.frankfurter.app/{currency_date}",
params={"from": currency_from, "to": currency_to},
)
return response.json()
# Create ADK agent with tools
root_agent = LlmAgent(
model="gemini-2.5-flash",
instruction="You are a helpful assistant",
name='currency_exchange_agent',
tools=[get_exchange_rate],
)
"""
with open("./agent-engine-terraform/02-adk-agent/currency_agent/agent.py", "w") as f:
f.write(adk_agent_code)Pickle and upload the ADK agent
As before, let's serialize the agent class.
serialize_agent_code = f"""
import cloudpickle
from currency_agent.agent import root_agent
import vertexai
from vertexai.agent_engines import AdkApp
# Initialize Vertex AI
vertexai.init(project="{PROJECT_ID}", location="{LOCATION}")
# Initialize agent instance
agent = AdkApp(agent=root_agent)
# Serialize the agent instance
pickle_file = "./agent.pkl"
with open(pickle_file, "wb") as f:
cloudpickle.dump(agent, f)
"""
with open("./agent-engine-terraform/02-adk-agent/serialize_agent.py", "w") as f:
f.write(serialize_agent_code)
! cd ./agent-engine-terraform/02-adk-agent && python serialize_agent.pyCreate requirements for ADK
Next, we need to package the requirements.
adk_requirements = """
google-cloud-aiplatform[agent_engines]>=1.120.0
google-adk>=1.15.1
cloudpickle==3.0.0
"""
adk_requirements_file = "./agent-engine-terraform/02-adk-agent/requirements.txt"
with open(adk_requirements_file, "w") as f:
f.write(adk_requirements)Create an empty dependencies.tar.gz file in this case.
! cd ./agent-engine-terraform/02-adk-agent/ && tar -czf dependencies.tar.gz currency_agentWrite Terraform configuration
Create the Terraform configuration file for ADK agent. In this scenario, we have an additional parameter in the spec, agent_framework, indicating we are deploying an ADK agent. Also we define the supported operations as described in the documentation.
adk_deploy_config = """
# Configure the Terraform provider
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "7.6.0"
}
}
}
# Configure the Google Cloud provider
provider "google" {
project = var.project_id
region = var.region
}
# Define variables
variable "project_id" {
description = "Google Cloud Project ID"
type = string
}
variable "region" {
description = "Google Cloud region"
type = string
default = "us-central1"
}
variable "gcs_bucket_name" {
description = "Cloud Storage bucket name"
type = string
}
# Define the class methods (operations) that the agent supports
# ADK agents support multiple async operations for streaming queries, sessions, and memory
locals {
class_methods = [
{
api_mode = "async_stream"
name = "async_stream_query"
description = "Streams responses asynchronously from the ADK application"
parameters = {
type = "object"
required = ["message", "user_id"]
properties = {
message = {
type = "string"
description = "The message to stream responses for"
}
user_id = {
type = "string"
description = "The ID of the user"
}
session_id = {
type = "string"
description = "The ID of the session. If not provided, a new session will be created"
}
run_config = {
type = "object"
description = "The run config to use for the query"
}
}
}
},
{
api_mode = "async"
name = "async_create_session"
description = "Creates a new session for the user"
parameters = {
type = "object"
required = ["user_id"]
properties = {
user_id = {
type = "string"
description = "The ID of the user"
}
}
}
},
{
api_mode = "async"
name = "async_list_sessions"
description = "Lists all sessions for the user"
parameters = {
type = "object"
required = ["user_id"]
properties = {
user_id = {
type = "string"
description = "The ID of the user"
}
}
}
},
{
api_mode = "async"
name = "async_get_session"
description = "Retrieves a specific session"
parameters = {
type = "object"
required = ["user_id", "session_id"]
properties = {
user_id = {
type = "string"
description = "The ID of the user"
}
session_id = {
type = "string"
description = "The ID of the session to retrieve"
}
}
}
},
{
api_mode = "async"
name = "async_delete_session"
description = "Deletes a specific session"
parameters = {
type = "object"
required = ["user_id", "session_id"]
properties = {
user_id = {
type = "string"
description = "The ID of the user"
}
session_id = {
type = "string"
description = "The ID of the session to delete"
}
}
}
},
{
api_mode = "async"
name = "async_add_session_to_memory"
description = "Generates memories from a session"
parameters = {
type = "object"
required = ["session"]
properties = {
session = {
type = "object"
description = "The session dictionary to add to memory"
}
}
}
},
{
api_mode = "async"
name = "async_search_memory"
description = "Searches and retrieves memories for the user"
parameters = {
type = "object"
required = ["user_id", "query"]
properties = {
user_id = {
type = "string"
description = "The ID of the user"
}
query = {
type = "string"
description = "The query to search memories for"
}
}
}
}
]
}
# Define resources
resource "google_storage_bucket" "bucket" {
name = var.gcs_bucket_name
location = var.region
uniform_bucket_level_access = true
force_destroy = true
}
resource "google_storage_bucket_object" "bucket_obj_requirements_txt" {
name = "requirements.txt"
bucket = google_storage_bucket.bucket.id
source = "./requirements.txt"
}
resource "google_storage_bucket_object" "bucket_obj_pickle_pkl" {
name = "agent.pkl"
bucket = google_storage_bucket.bucket.id
source = "./agent.pkl"
}
resource "google_storage_bucket_object" "bucket_obj_dependencies_tar_gz" {
name = "dependencies.tar.gz"
bucket = google_storage_bucket.bucket.id
source = "./dependencies.tar.gz"
}
# Deploy agent to Vertex AI Agent Engine
resource "google_vertex_ai_reasoning_engine" "reasoning_engine" {
display_name = "simple_adk_agent"
description = "A simple ADK agent deployed with Terraform"
region = var.region
spec {
agent_framework = "google-adk"
class_methods = jsonencode(local.class_methods)
package_spec {
dependency_files_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_dependencies_tar_gz.name}"
pickle_object_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_pickle_pkl.name}"
python_version = "3.12"
requirements_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_requirements_txt.name}"
}
}
}
# Output the reasoning engine information
output "reasoning_engine_id" {
description = "The ID of the deployed reasoning engine"
value = google_vertex_ai_reasoning_engine.reasoning_engine.name
}
output "reasoning_engine_resource" {
description = "The full resource name"
value = google_vertex_ai_reasoning_engine.reasoning_engine.id
}
"""
with open("./agent-engine-terraform/02-adk-agent/main.tf", "w") as f:
f.write(adk_deploy_config)Create variables file
Create a terraform.tfvars file with your project-specific values.
deploy_vars = f"""
project_id = "{PROJECT_ID}"
region = "{LOCATION}"
gcs_bucket_name = "{BUCKET_NAME}-adk-agent"
"""
with open("./agent-engine-terraform/02-adk-agent/terraform.tfvars", "w") as f:
f.write(deploy_vars)Deploy the ADK agent
Run the Terraform commands to deploy your agent in background.
! nohup bash -c "cd ./agent-engine-terraform/02-adk-agent && terraform init && terraform plan && terraform apply -auto-approve" > ./agent-engine-terraform/02-adk-agent/terraform.log 2>&1 &Monitor deployment
You can monitor the deployment tailing the log file.
! tail -f ./agent-engine-terraform/02-adk-agent/terraform.logVerify the deployment
After deployment completes, retrieve the outputs.
! cd ./agent-engine-terraform/02-adk-agent && terraform outputTest your deployed agent
Query the ADK agent using the Vertex AI SDK.
# fmt: off
agent_engine_resource_name = "[your-agent-engine-resource-name]" # @param {type: "string", placeholder: "[your-agent-engine-resource-name]"}
# fmt: onclient = vertexai.Client(
project=PROJECT_ID,
location=LOCATION,
)
agent = client.agent_engines.get(name=agent_engine_resource_name)async for event in agent.async_stream_query(
user_id="user_123",
message="What is the exchange rate from US dollars to SEK today?",
):
print(event)Cleaning up
To avoid incurring unnecessary charges, clean up the resources when you're done.
Destroy specific deployment
# Destroy the basic deployment
! cd ./agent-engine-terraform/01-basic-agent && terraform destroyDestroy all agent deployments
import os
def list_subfolders(folder_path):
"""Lists all subfolders in a given folder path."""
return [
os.path.join(folder_path, d)
for d in os.listdir(folder_path)
if os.path.isdir(os.path.join(folder_path, d))
]
# Get all deployment folders
folder_to_check = "./agent-engine-terraform"
subfolders = list_subfolders(folder_to_check)
# Destroy each deployment
for folder in subfolders:
print(f"Destroying agent in {folder}...")
! cd {folder} && terraform destroy -auto-approve
print(f"Destroyed agent in {folder}!\n")