{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ur8xi4C7S06n"
      },
      "outputs": [],
      "source": [
        "# Copyright 2025 Google LLC\n",
        "#\n",
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "#     https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JAPoU8Sm5E6e"
      },
      "source": [
        "# Get started with Agent Engine Terraform Deployment\n",
        "\n",
        "<table align=\"left\">\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_get_started_with_agent_engine_terraform_deployment.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://www.gstatic.com/pantheon/images/bigquery/welcome_page/colab-logo.svg\" alt=\"Google Colaboratory logo\"><br> Open in Colab\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://console.cloud.google.com/vertex-ai/colab/import/https:%2F%2Fraw.githubusercontent.com%2FGoogleCloudPlatform%2Fgenerative-ai%2Fmain%2Fagents%2Fagent_engine%2Ftutorial_get_started_with_agent_engine_terraform_deployment.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://lh3.googleusercontent.com/JmcxdQi-qOpctIvWKgPtrzZdJJK-J3sWE1RsfjZNwshCFgE_9fULcNpuXYTilIR2hjwN\" alt=\"Google Cloud Colab Enterprise logo\"><br> Open in Colab Enterprise\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/generative-ai/main/agents/agent_engine/tutorial_get_started_with_agent_engine_terraform_deployment.ipynb\">\n",
        "      <img src=\"https://www.gstatic.com/images/branding/gcpiconscolors/vertexai/v1/32px.svg\" alt=\"Vertex AI logo\"><br> Open in Vertex AI Workbench\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_get_started_with_agent_engine_terraform_deployment.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://raw.githubusercontent.com/primer/octicons/refs/heads/main/icons/mark-github-24.svg\" alt=\"GitHub logo\"><br> View on GitHub\n",
        "    </a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "84f0f73a0f76"
      },
      "source": [
        "| Authors |\n",
        "| --- |\n",
        "| [Ivan Nardini](https://github.com/inardini) |\n",
        "| Luca Prete |\n",
        "| Yee Sian Ng |"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tvgnzT1CKxrO"
      },
      "source": [
        "## Overview\n",
        "\n",
        "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.\n",
        "\n",
        "**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.\n",
        "\n",
        "The [Vertex AI Reasoning Engine Terraform resource](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/vertex_ai_reasoning_engine) 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.\n",
        "\n",
        "This tutorial shows how to use Terraform to deploy AI agents on Vertex AI Agent Engine.\n",
        "\n",
        "### What you will learn\n",
        "\n",
        "You will learn how to:\n",
        "\n",
        "- Set up Terraform for Vertex AI Agent Engine deployments\n",
        "- Create custom agents according to the Vertex AI Agent Engine\n",
        "- Package your Python agent code using cloudpickle\n",
        "- Deploy your first agent using Terraform\n",
        "- Handle dependencies and requirements\n",
        "- Deploy ADK (Agent Development Kit) agents with function calling\n",
        "- Clean up resources efficiently"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "61RBz8LLbxCR"
      },
      "source": [
        "## Get started"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "i_s5CsTISJzh"
      },
      "source": [
        "### Prerequisites\n",
        "\n",
        "Before you begin, ensure you have:\n",
        "\n",
        "1. A Google Cloud project with billing enabled\n",
        "2. The Vertex AI API enabled\n",
        "3. Sufficient IAM permissions (Vertex AI Administrator or Editor role)\n",
        "4. A Cloud Storage bucket for storing agent artifacts"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "No17Cw5hgx12"
      },
      "source": [
        "### Install Vertex AI SDK and other required packages"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tFy3H3aPgx12"
      },
      "outputs": [],
      "source": [
        "%pip install --upgrade --quiet 'google-cloud-aiplatform[agent_engines]>=1.120.0' 'google-adk>=1.15.1' 'cloudpickle>=3.0.0'"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xkEVoJ16SRVh"
      },
      "source": [
        "### Install Terraform\n",
        "\n",
        "If you don't have Terraform installed, download and install it from [terraform.io](https://www.terraform.io/downloads)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MK8uWkroSTam"
      },
      "outputs": [],
      "source": [
        "# For Linux distributions\n",
        "! wget https://releases.hashicorp.com/terraform/1.13.3/terraform_1.13.3_linux_amd64.zip\n",
        "! unzip terraform_1.13.3_linux_amd64.zip\n",
        "! sudo mv terraform /usr/local/bin/\n",
        "\n",
        "# Verify installation\n",
        "! terraform version"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dmWOrTJ3gx13"
      },
      "source": [
        "### Authenticate your notebook environment (Colab only)\n",
        "\n",
        "If you're running this notebook on Google Colab, run the cell below to authenticate your environment."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NyKGtVQjgx13"
      },
      "outputs": [],
      "source": [
        "import sys\n",
        "\n",
        "if \"google.colab\" in sys.modules:\n",
        "    from google.colab import auth\n",
        "\n",
        "    auth.authenticate_user()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DF4l8DTdWgPY"
      },
      "source": [
        "### Set Google Cloud project information\n",
        "\n",
        "To get started using Vertex AI, you must have an existing Google Cloud project and [enable the Vertex AI API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com).\n",
        "\n",
        "Learn more about [setting up a project and a development environment](https://cloud.google.com/vertex-ai/docs/start/cloud-environment)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Nqwi-5ufWp_B"
      },
      "outputs": [],
      "source": [
        "# Use the environment variable if the user doesn't provide Project ID.\n",
        "import os\n",
        "\n",
        "# fmt: off\n",
        "PROJECT_ID = \"[your-project-id]\"  # @param {type: \"string\", placeholder: \"[your-project-id]\", isTemplate: true}\n",
        "if not PROJECT_ID or PROJECT_ID == \"[your-project-id]\":\n",
        "    PROJECT_ID = str(os.environ.get(\"GOOGLE_CLOUD_PROJECT\"))\n",
        "\n",
        "LOCATION = os.environ.get(\"GOOGLE_CLOUD_REGION\", \"us-central1\")\n",
        "\n",
        "# Agent Engine requires your agent artifacts (pickle file and requirements) to be stored in Cloud Storage.\n",
        "BUCKET_NAME = \"[your-bucket-name]\" # @param {type: \"string\", placeholder: \"[your-bucket-name]\", isTemplate: true}\n",
        "# fmt: on"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HHQn0O9UUEaB"
      },
      "source": [
        "## Import libraries"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kLVVKxa9UH5V"
      },
      "outputs": [],
      "source": [
        "import sys\n",
        "\n",
        "import vertexai"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bHD0bXJPTQE9"
      },
      "source": [
        "## Create a Terraform workspace\n",
        "\n",
        "Create a new directory for your Terraform configurations."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SRkF2at6TSSU"
      },
      "outputs": [],
      "source": [
        "# Create a directory for your Terraform project\n",
        "! rm -rf ./agent-engine-terraform && mkdir ./agent-engine-terraform"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FxyAbM-NTVw4"
      },
      "source": [
        "## Understanding Agent Engine\n",
        "\n",
        "**Vertex AI Agent Engine** is a serverless platform that allows you to deploy and host AI agents without managing infrastructure. Key benefits include:\n",
        "\n",
        "- **Code-first approach**: Build agents using familiar Python frameworks\n",
        "- **Serverless**: No infrastructure management required\n",
        "- **Scalable**: Automatic scaling based on demand\n",
        "- **Integrated**: Works seamlessly with Vertex AI services\n",
        "- **Secure**: Enterprise-grade security and access controls\n",
        "\n",
        "Below you have the steps to cover in order to deploy an agent on Agent Engine:\n",
        "1. **Build**: Create your agent as a Python class with `__init__()`, `set_up()`, and `query()` methods following the custom agent template pattern, or use ADK\n",
        "2. **Package**: Serialize your agent instance with cloudpickle (ensuring the object is pickle-able) and store in Cloud Storage\n",
        "3. **Deploy**: Use Terraform to create a Reasoning Engine resource that references your packaged agent\n",
        "4. **Query**: Invoke your agent's `query()` method via API or SDK\n",
        "\n",
        "The deployment uses a `google_vertex_ai_reasoning_engine` Terraform resource that references your packaged agent code."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Me3BWV3pTaag"
      },
      "source": [
        "## Deploy your first custom agent\n",
        "\n",
        "Let's deploy a custom agent using Terraform following the Vertex AI Agent Engine custom agent pattern.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_2Jpd5gaTtRZ"
      },
      "source": [
        "### Create the agent code\n",
        "\n",
        "Create a custom agent class following the Vertex AI Agent Engine template pattern. Agent templates are defined as Python classes with three key methods:\n",
        "- `__init__()`: For agent configuration parameters (must be pickle-able)\n",
        "- `set_up()`: For initialization logic like establishing connections or importing packages\n",
        "- `query()`: To return the complete response as a single result\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zMW-VQYdTqqe"
      },
      "outputs": [],
      "source": [
        "! mkdir -p ./agent-engine-terraform/01-basic-agent/agent"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "b0rSb1LLTwgf"
      },
      "outputs": [],
      "source": [
        "# Init file\n",
        "with open(\"./agent-engine-terraform/01-basic-agent/agent/__init__.py\", \"w\"):\n",
        "    pass\n",
        "\n",
        "# Create a custom agent following the Vertex AI Agent Engine pattern\n",
        "simple_agent_code = \"\"\"\n",
        "from typing import Dict\n",
        "\n",
        "class SimpleAgent:\n",
        "    def __init__(\n",
        "        self,\n",
        "        model: str,\n",
        "        project: str,\n",
        "        location: str,\n",
        "    ):\n",
        "        self.model_name = model\n",
        "        self.project = project\n",
        "        self.location = location\n",
        "\n",
        "    def set_up(self):\n",
        "        import vertexai\n",
        "        from vertexai.generative_models import GenerativeModel\n",
        "\n",
        "        vertexai.init(project=self.project, location=self.location)\n",
        "        self.model = GenerativeModel(self.model_name)\n",
        "\n",
        "    def query(self, input: str) -> Dict:\n",
        "        '''Simple agent that uses Gemini to respond to queries.\n",
        "\n",
        "        Args:\n",
        "            input: The user's query string\n",
        "\n",
        "        Returns:\n",
        "            A dictionary containing the agent's response\n",
        "        '''\n",
        "        response = self.model.generate_content(\n",
        "            f\"You are a helpful AI assistant. Respond to: {input}\"\n",
        "        )\n",
        "        return {\"output\": response.text}\n",
        "\"\"\"\n",
        "\n",
        "# Write agent code to file\n",
        "with open(\"./agent-engine-terraform/01-basic-agent/agent/simple_agent.py\", \"w\") as f:\n",
        "    f.write(simple_agent_code)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "C3BI1veXT_Ge"
      },
      "source": [
        "### Create the pickle file\n",
        "\n",
        "Package your agent using cloudpickle. The agent object must be \"pickle-able\"."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6b9mMjGNPjPy"
      },
      "outputs": [],
      "source": [
        "serialize_agent_code = f\"\"\"\n",
        "import cloudpickle\n",
        "from agent.simple_agent import SimpleAgent\n",
        "\n",
        "# Create and initialize agent instance\n",
        "agent = SimpleAgent(\n",
        "    model=\"gemini-2.5-flash\",\n",
        "    project=\"{PROJECT_ID}\",\n",
        "    location=\"{LOCATION}\",\n",
        ")\n",
        "\n",
        "agent.set_up()\n",
        "\n",
        "# Serialize the agent instance\n",
        "pickle_file = \"./agent.pkl\"\n",
        "with open(pickle_file, \"wb\") as f:\n",
        "    cloudpickle.dump(agent, f)\n",
        "\"\"\"\n",
        "\n",
        "with open(\"./agent-engine-terraform/01-basic-agent/serialize_agent.py\", \"w\") as f:\n",
        "    f.write(serialize_agent_code)\n",
        "\n",
        "! cd ./agent-engine-terraform/01-basic-agent && python serialize_agent.py"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BN3-OvsjdXex"
      },
      "source": [
        "### Package dependencies\n",
        "\n",
        "Create a requirements.txt file."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "k6vL0xu-ddPw"
      },
      "outputs": [],
      "source": [
        "requirements = \"\"\"\n",
        "google-cloud-aiplatform[agent_engines]>=1.120.0\n",
        "google-adk>=1.15.1\n",
        "cloudpickle==3.0.0\n",
        "\"\"\"\n",
        "\n",
        "requirements_file = \"./agent-engine-terraform/01-basic-agent/requirements.txt\"\n",
        "with open(requirements_file, \"w\") as f:\n",
        "    f.write(requirements)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8VepK8tXrVyN"
      },
      "source": [
        "If you have a custom agent or additional dependecies, create an empty dependencies.tar.gz file."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "cXXVtC9mrWZT"
      },
      "outputs": [],
      "source": [
        "! cd ./agent-engine-terraform/01-basic-agent/ && tar -czf dependencies.tar.gz agent"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EJV0jpJMdm3i"
      },
      "source": [
        "### Write Terraform configuration\n",
        "\n",
        "Create the Terraform configuration file.\n",
        "\n",
        "> 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."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rOJRc_17drpl"
      },
      "outputs": [],
      "source": [
        "basic_deploy_config = \"\"\"\n",
        "# Configure the Terraform provider\n",
        "terraform {\n",
        "  required_providers {\n",
        "    google = {\n",
        "      source  = \"hashicorp/google\"\n",
        "      version = \"7.6.0\"\n",
        "    }\n",
        "  }\n",
        "}\n",
        "\n",
        "# Configure the Google Cloud provider\n",
        "provider \"google\" {\n",
        "  project = var.project_id\n",
        "  region  = var.region\n",
        "}\n",
        "\n",
        "# Define variables\n",
        "variable \"project_id\" {\n",
        "  description = \"Google Cloud Project ID\"\n",
        "  type        = string\n",
        "}\n",
        "\n",
        "variable \"region\" {\n",
        "  description = \"Google Cloud region\"\n",
        "  type        = string\n",
        "  default     = \"us-central1\"\n",
        "}\n",
        "\n",
        "variable \"gcs_bucket_name\" {\n",
        "  description = \"Cloud Storage bucket name\"\n",
        "  type        = string\n",
        "}\n",
        "\n",
        "# Define the class methods (operations) that the agent supports\n",
        "locals {\n",
        "    class_methods = [\n",
        "      {\n",
        "        api_mode = \"\"\n",
        "        name     = \"query\"\n",
        "        description = \"Queries the agent with the given input\"\n",
        "        parameters = {\n",
        "          type     = \"object\"\n",
        "          required = [\"input\"]\n",
        "          properties = {\n",
        "            input = {\n",
        "              type = \"string\"\n",
        "            }\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    ]\n",
        "  }\n",
        "\n",
        "# Define resources\n",
        "resource \"google_storage_bucket\" \"bucket\" {\n",
        "  name                        = var.gcs_bucket_name\n",
        "  location                    = var.region\n",
        "  uniform_bucket_level_access = true\n",
        "  force_destroy               = true\n",
        "}\n",
        "\n",
        "resource \"google_storage_bucket_object\" \"bucket_obj_requirements_txt\" {\n",
        "  name    = \"requirements.txt\"\n",
        "  bucket  = google_storage_bucket.bucket.id\n",
        "  source  = \"./requirements.txt\"\n",
        "}\n",
        "\n",
        "resource \"google_storage_bucket_object\" \"bucket_obj_pickle_pkl\" {\n",
        "  name    = \"agent.pkl\"\n",
        "  bucket  = google_storage_bucket.bucket.id\n",
        "  source  = \"./agent.pkl\"\n",
        "}\n",
        "\n",
        "resource \"google_storage_bucket_object\" \"bucket_obj_dependencies_tar_gz\" {\n",
        "  name    = \"dependencies.tar.gz\"\n",
        "  bucket  = google_storage_bucket.bucket.id\n",
        "  source  = \"./dependencies.tar.gz\"\n",
        "}\n",
        "\n",
        "# Deploy agent to Vertex AI Agent Engine\n",
        "resource \"google_vertex_ai_reasoning_engine\" \"reasoning_engine\" {\n",
        "  display_name = \"simple_agent\"\n",
        "  description  = \"A simple agent deployed with Terraform\"\n",
        "  region       = var.region\n",
        "\n",
        "  spec {\n",
        "    class_methods   = jsonencode(local.class_methods)\n",
        "    package_spec {\n",
        "      dependency_files_gcs_uri = \"${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_dependencies_tar_gz.name}\"\n",
        "      pickle_object_gcs_uri    = \"${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_pickle_pkl.name}\"\n",
        "      python_version           = \"3.12\"\n",
        "      requirements_gcs_uri     = \"${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_requirements_txt.name}\"\n",
        "    }\n",
        "  }\n",
        "}\n",
        "\n",
        "# Output the reasoning engine information\n",
        "output \"reasoning_engine_id\" {\n",
        "  description = \"The ID of the deployed reasoning engine\"\n",
        "  value       = google_vertex_ai_reasoning_engine.reasoning_engine.name\n",
        "}\n",
        "\n",
        "output \"reasoning_engine_resource\" {\n",
        "  description = \"The full resource name\"\n",
        "  value       = google_vertex_ai_reasoning_engine.reasoning_engine.id\n",
        "}\n",
        "\"\"\"\n",
        "\n",
        "with open(\"./agent-engine-terraform/01-basic-agent/main.tf\", \"w\") as f:\n",
        "    f.write(basic_deploy_config)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "51kuIoZHd6UL"
      },
      "source": [
        "### Create variables file\n",
        "\n",
        "Create a `terraform.tfvars` file with your project-specific values."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RxPXYS4PdvGO"
      },
      "outputs": [],
      "source": [
        "deploy_vars = f\"\"\"\n",
        "project_id = \"{PROJECT_ID}\"\n",
        "region     = \"{LOCATION}\"\n",
        "gcs_bucket_name = \"{BUCKET_NAME}-custom-agent\"\n",
        "\"\"\"\n",
        "\n",
        "with open(\"./agent-engine-terraform/01-basic-agent/terraform.tfvars\", \"w\") as f:\n",
        "    f.write(deploy_vars)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yN8vwdkZd_Mk"
      },
      "source": [
        "### Deploy with Terraform\n",
        "\n",
        "Run the Terraform commands to deploy your agent.\n",
        "\n",
        "> **Note**: The deployment typically takes around 5 minutes. Terraform will show you the planned changes and ask for confirmation.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4xEw7yF9m0Y1"
      },
      "outputs": [],
      "source": [
        "# Initialize Terraform\n",
        "! cd ./agent-engine-terraform/01-basic-agent && terraform init\n",
        "\n",
        "# Plan the deployment\n",
        "! cd ./agent-engine-terraform/01-basic-agent && terraform plan\n",
        "\n",
        "# Apply the configuration (will prompt for confirmation)\n",
        "! cd ./agent-engine-terraform/01-basic-agent && terraform apply"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8YfU20ij4q8e"
      },
      "source": [
        "### Verify the deployment\n",
        "\n",
        "After deployment completes, retrieve the outputs."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lCExLWcY64Sk"
      },
      "outputs": [],
      "source": [
        "! cd ./agent-engine-terraform/01-basic-agent && terraform output"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TJSVrahW65SJ"
      },
      "source": [
        "### Test your deployed agent\n",
        "\n",
        "Query the agent using the Vertex AI SDK."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ixIeYRCn6-Uk"
      },
      "outputs": [],
      "source": [
        "# fmt: off\n",
        "agent_engine_resource_name = \"[your-agent-engine-resource-name]\" # @param {type: \"string\", placeholder: \"[your-agent-engine-resource-name]\"}\n",
        "# fmt: on"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2s68CkUn7id-"
      },
      "outputs": [],
      "source": [
        "client = vertexai.Client(\n",
        "    project=PROJECT_ID,\n",
        "    location=LOCATION,\n",
        ")\n",
        "\n",
        "agent = client.agent_engines.get(name=agent_engine_resource_name)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "eN7cgXsV7zDz"
      },
      "outputs": [],
      "source": [
        "response = agent.query(input=\"What is artificial intelligence?\")\n",
        "print(f\"\\nAgent response:\\n{response['output']}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yNs0ng5h-zm5"
      },
      "source": [
        "## Deploy an ADK agent with tools\n",
        "\n",
        "This example deploys an ADK (Agent Development Kit) agent with function calling capabilities."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CKbRjCk3-21l"
      },
      "source": [
        "### Create the ADK agent\n",
        "\n",
        "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.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wg9lSnYJ-vQC"
      },
      "outputs": [],
      "source": [
        "! rm -rf ./agent-engine-terraform/02-adk-agent && mkdir -p ./agent-engine-terraform/02-adk-agent/currency_agent"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9qTSAoXe-7pf"
      },
      "outputs": [],
      "source": [
        "with open(\"./agent-engine-terraform/02-adk-agent/currency_agent/__init__.py\", \"w\") as f:\n",
        "    f.write(\"from .agent import root_agent\")\n",
        "\n",
        "adk_agent_code = \"\"\"\n",
        "from google.adk.agents import LlmAgent\n",
        "\n",
        "def get_exchange_rate(\n",
        "    currency_from: str = \"USD\",\n",
        "    currency_to: str = \"EUR\",\n",
        "    currency_date: str = \"latest\",\n",
        "):\n",
        "    '''Retrieves the exchange rate between two currencies on a specified date.\n",
        "\n",
        "    Uses the Frankfurter API (https://api.frankfurter.app/) to obtain\n",
        "    exchange rate data.\n",
        "\n",
        "    Args:\n",
        "        currency_from: The base currency (3-letter currency code).\n",
        "            Defaults to \"USD\" (US Dollar).\n",
        "        currency_to: The target currency (3-letter currency code).\n",
        "            Defaults to \"EUR\" (Euro).\n",
        "        currency_date: The date for which to retrieve the exchange rate.\n",
        "            Defaults to \"latest\" for the most recent exchange rate data.\n",
        "            Can be specified in YYYY-MM-DD format for historical rates.\n",
        "\n",
        "    Returns:\n",
        "        dict: A dictionary containing the exchange rate information.\n",
        "            Example: {\"amount\": 1.0, \"base\": \"USD\", \"date\": \"2023-11-24\",\n",
        "                \"rates\": {\"EUR\": 0.95534}}\n",
        "    '''\n",
        "    import requests\n",
        "    response = requests.get(\n",
        "        f\"https://api.frankfurter.app/{currency_date}\",\n",
        "        params={\"from\": currency_from, \"to\": currency_to},\n",
        "    )\n",
        "    return response.json()\n",
        "\n",
        "# Create ADK agent with tools\n",
        "root_agent = LlmAgent(\n",
        "    model=\"gemini-2.5-flash\",\n",
        "    instruction=\"You are a helpful assistant\",\n",
        "    name='currency_exchange_agent',\n",
        "    tools=[get_exchange_rate],\n",
        ")\n",
        "\"\"\"\n",
        "\n",
        "with open(\"./agent-engine-terraform/02-adk-agent/currency_agent/agent.py\", \"w\") as f:\n",
        "    f.write(adk_agent_code)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yhVClvD2_IA-"
      },
      "source": [
        "### Pickle and upload the ADK agent\n",
        "\n",
        "As before, let's serialize the agent class."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GLPxtGbiRhWN"
      },
      "outputs": [],
      "source": [
        "serialize_agent_code = f\"\"\"\n",
        "import cloudpickle\n",
        "from currency_agent.agent import root_agent\n",
        "import vertexai\n",
        "from vertexai.agent_engines import AdkApp\n",
        "\n",
        "# Initialize Vertex AI\n",
        "vertexai.init(project=\"{PROJECT_ID}\", location=\"{LOCATION}\")\n",
        "\n",
        "# Initialize agent instance\n",
        "agent = AdkApp(agent=root_agent)\n",
        "\n",
        "# Serialize the agent instance\n",
        "pickle_file = \"./agent.pkl\"\n",
        "with open(pickle_file, \"wb\") as f:\n",
        "    cloudpickle.dump(agent, f)\n",
        "\"\"\"\n",
        "\n",
        "with open(\"./agent-engine-terraform/02-adk-agent/serialize_agent.py\", \"w\") as f:\n",
        "    f.write(serialize_agent_code)\n",
        "\n",
        "! cd ./agent-engine-terraform/02-adk-agent && python serialize_agent.py"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kG1dSsRWAPz-"
      },
      "source": [
        "### Create requirements for ADK\n",
        "\n",
        "Next, we need to package the requirements.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0veIqxOIAPz_"
      },
      "outputs": [],
      "source": [
        "adk_requirements = \"\"\"\n",
        "google-cloud-aiplatform[agent_engines]>=1.120.0\n",
        "google-adk>=1.15.1\n",
        "cloudpickle==3.0.0\n",
        "\"\"\"\n",
        "\n",
        "adk_requirements_file = \"./agent-engine-terraform/02-adk-agent/requirements.txt\"\n",
        "with open(adk_requirements_file, \"w\") as f:\n",
        "    f.write(adk_requirements)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8XEKRirlFccL"
      },
      "source": [
        "Create an empty dependencies.tar.gz file in this case."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tDb7OoxvFccL"
      },
      "outputs": [],
      "source": [
        "! cd ./agent-engine-terraform/02-adk-agent/ && tar -czf dependencies.tar.gz currency_agent"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LTw1xyTiB3_A"
      },
      "source": [
        "### Write Terraform configuration\n",
        "\n",
        "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](https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/use/adk#supported-operations)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f8qjxj2eB3_A"
      },
      "outputs": [],
      "source": [
        "adk_deploy_config = \"\"\"\n",
        "# Configure the Terraform provider\n",
        "terraform {\n",
        "  required_providers {\n",
        "    google = {\n",
        "      source  = \"hashicorp/google\"\n",
        "      version = \"7.6.0\"\n",
        "    }\n",
        "  }\n",
        "}\n",
        "\n",
        "# Configure the Google Cloud provider\n",
        "provider \"google\" {\n",
        "  project = var.project_id\n",
        "  region  = var.region\n",
        "}\n",
        "\n",
        "# Define variables\n",
        "variable \"project_id\" {\n",
        "  description = \"Google Cloud Project ID\"\n",
        "  type        = string\n",
        "}\n",
        "\n",
        "variable \"region\" {\n",
        "  description = \"Google Cloud region\"\n",
        "  type        = string\n",
        "  default     = \"us-central1\"\n",
        "}\n",
        "\n",
        "variable \"gcs_bucket_name\" {\n",
        "  description = \"Cloud Storage bucket name\"\n",
        "  type        = string\n",
        "}\n",
        "\n",
        "# Define the class methods (operations) that the agent supports\n",
        "# ADK agents support multiple async operations for streaming queries, sessions, and memory\n",
        "locals {\n",
        "  class_methods = [\n",
        "    {\n",
        "      api_mode = \"async_stream\"\n",
        "      name     = \"async_stream_query\"\n",
        "      description = \"Streams responses asynchronously from the ADK application\"\n",
        "      parameters = {\n",
        "        type     = \"object\"\n",
        "        required = [\"message\", \"user_id\"]\n",
        "        properties = {\n",
        "          message = {\n",
        "            type        = \"string\"\n",
        "            description = \"The message to stream responses for\"\n",
        "          }\n",
        "          user_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the user\"\n",
        "          }\n",
        "          session_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the session. If not provided, a new session will be created\"\n",
        "          }\n",
        "          run_config = {\n",
        "            type        = \"object\"\n",
        "            description = \"The run config to use for the query\"\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    },\n",
        "    {\n",
        "      api_mode = \"async\"\n",
        "      name     = \"async_create_session\"\n",
        "      description = \"Creates a new session for the user\"\n",
        "      parameters = {\n",
        "        type     = \"object\"\n",
        "        required = [\"user_id\"]\n",
        "        properties = {\n",
        "          user_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the user\"\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    },\n",
        "    {\n",
        "      api_mode = \"async\"\n",
        "      name     = \"async_list_sessions\"\n",
        "      description = \"Lists all sessions for the user\"\n",
        "      parameters = {\n",
        "        type     = \"object\"\n",
        "        required = [\"user_id\"]\n",
        "        properties = {\n",
        "          user_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the user\"\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    },\n",
        "    {\n",
        "      api_mode = \"async\"\n",
        "      name     = \"async_get_session\"\n",
        "      description = \"Retrieves a specific session\"\n",
        "      parameters = {\n",
        "        type     = \"object\"\n",
        "        required = [\"user_id\", \"session_id\"]\n",
        "        properties = {\n",
        "          user_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the user\"\n",
        "          }\n",
        "          session_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the session to retrieve\"\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    },\n",
        "    {\n",
        "      api_mode = \"async\"\n",
        "      name     = \"async_delete_session\"\n",
        "      description = \"Deletes a specific session\"\n",
        "      parameters = {\n",
        "        type     = \"object\"\n",
        "        required = [\"user_id\", \"session_id\"]\n",
        "        properties = {\n",
        "          user_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the user\"\n",
        "          }\n",
        "          session_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the session to delete\"\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    },\n",
        "    {\n",
        "      api_mode = \"async\"\n",
        "      name     = \"async_add_session_to_memory\"\n",
        "      description = \"Generates memories from a session\"\n",
        "      parameters = {\n",
        "        type     = \"object\"\n",
        "        required = [\"session\"]\n",
        "        properties = {\n",
        "          session = {\n",
        "            type        = \"object\"\n",
        "            description = \"The session dictionary to add to memory\"\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    },\n",
        "    {\n",
        "      api_mode = \"async\"\n",
        "      name     = \"async_search_memory\"\n",
        "      description = \"Searches and retrieves memories for the user\"\n",
        "      parameters = {\n",
        "        type     = \"object\"\n",
        "        required = [\"user_id\", \"query\"]\n",
        "        properties = {\n",
        "          user_id = {\n",
        "            type        = \"string\"\n",
        "            description = \"The ID of the user\"\n",
        "          }\n",
        "          query = {\n",
        "            type        = \"string\"\n",
        "            description = \"The query to search memories for\"\n",
        "          }\n",
        "        }\n",
        "      }\n",
        "    }\n",
        "  ]\n",
        "}\n",
        "\n",
        "# Define resources\n",
        "resource \"google_storage_bucket\" \"bucket\" {\n",
        "  name                        = var.gcs_bucket_name\n",
        "  location                    = var.region\n",
        "  uniform_bucket_level_access = true\n",
        "  force_destroy               = true\n",
        "}\n",
        "\n",
        "resource \"google_storage_bucket_object\" \"bucket_obj_requirements_txt\" {\n",
        "  name    = \"requirements.txt\"\n",
        "  bucket  = google_storage_bucket.bucket.id\n",
        "  source  = \"./requirements.txt\"\n",
        "}\n",
        "\n",
        "resource \"google_storage_bucket_object\" \"bucket_obj_pickle_pkl\" {\n",
        "  name    = \"agent.pkl\"\n",
        "  bucket  = google_storage_bucket.bucket.id\n",
        "  source  = \"./agent.pkl\"\n",
        "}\n",
        "\n",
        "resource \"google_storage_bucket_object\" \"bucket_obj_dependencies_tar_gz\" {\n",
        "  name    = \"dependencies.tar.gz\"\n",
        "  bucket  = google_storage_bucket.bucket.id\n",
        "  source  = \"./dependencies.tar.gz\"\n",
        "}\n",
        "\n",
        "# Deploy agent to Vertex AI Agent Engine\n",
        "resource \"google_vertex_ai_reasoning_engine\" \"reasoning_engine\" {\n",
        "  display_name = \"simple_adk_agent\"\n",
        "  description  = \"A simple ADK agent deployed with Terraform\"\n",
        "  region       = var.region\n",
        "\n",
        "  spec {\n",
        "    agent_framework = \"google-adk\"\n",
        "    class_methods   = jsonencode(local.class_methods)\n",
        "    package_spec {\n",
        "      dependency_files_gcs_uri = \"${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_dependencies_tar_gz.name}\"\n",
        "      pickle_object_gcs_uri    = \"${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_pickle_pkl.name}\"\n",
        "      python_version           = \"3.12\"\n",
        "      requirements_gcs_uri     = \"${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_requirements_txt.name}\"\n",
        "\n",
        "  }\n",
        "}\n",
        "}\n",
        "\n",
        "# Output the reasoning engine information\n",
        "output \"reasoning_engine_id\" {\n",
        "  description = \"The ID of the deployed reasoning engine\"\n",
        "  value       = google_vertex_ai_reasoning_engine.reasoning_engine.name\n",
        "}\n",
        "\n",
        "output \"reasoning_engine_resource\" {\n",
        "  description = \"The full resource name\"\n",
        "  value       = google_vertex_ai_reasoning_engine.reasoning_engine.id\n",
        "}\n",
        "\"\"\"\n",
        "\n",
        "with open(\"./agent-engine-terraform/02-adk-agent/main.tf\", \"w\") as f:\n",
        "    f.write(adk_deploy_config)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "X2PBdaJIB3_A"
      },
      "source": [
        "### Create variables file\n",
        "\n",
        "Create a `terraform.tfvars` file with your project-specific values."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uzPv2pnJB3_B"
      },
      "outputs": [],
      "source": [
        "deploy_vars = f\"\"\"\n",
        "project_id = \"{PROJECT_ID}\"\n",
        "region     = \"{LOCATION}\"\n",
        "gcs_bucket_name = \"{BUCKET_NAME}-adk-agent\"\n",
        "\"\"\"\n",
        "\n",
        "with open(\"./agent-engine-terraform/02-adk-agent/terraform.tfvars\", \"w\") as f:\n",
        "    f.write(deploy_vars)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0Mal4F5hB3_B"
      },
      "source": [
        "### Deploy the ADK agent\n",
        "\n",
        "Run the Terraform commands to deploy your agent in background."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "eaBxH46LB3_B"
      },
      "outputs": [],
      "source": [
        "! 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 &"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vMz1FwtuC5Qv"
      },
      "source": [
        "### Monitor deployment\n",
        "\n",
        "You can monitor the deployment tailing the log file."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CnWg5JZqC6sN"
      },
      "outputs": [],
      "source": [
        "! tail -f ./agent-engine-terraform/02-adk-agent/terraform.log"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3WCvlSeoB3_B"
      },
      "source": [
        "### Verify the deployment\n",
        "\n",
        "After deployment completes, retrieve the outputs."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BriR4M0oB3_B"
      },
      "outputs": [],
      "source": [
        "! cd ./agent-engine-terraform/02-adk-agent && terraform output"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QJ6IV-AaDpm_"
      },
      "source": [
        "### Test your deployed agent\n",
        "\n",
        "Query the ADK agent using the Vertex AI SDK."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Yep-iNPmDpnB"
      },
      "outputs": [],
      "source": [
        "# fmt: off\n",
        "agent_engine_resource_name = \"[your-agent-engine-resource-name]\" # @param {type: \"string\", placeholder: \"[your-agent-engine-resource-name]\"}\n",
        "# fmt: on"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "X80sYGBiDpnC"
      },
      "outputs": [],
      "source": [
        "client = vertexai.Client(\n",
        "    project=PROJECT_ID,\n",
        "    location=LOCATION,\n",
        ")\n",
        "\n",
        "agent = client.agent_engines.get(name=agent_engine_resource_name)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8-h1CFkyDpnC"
      },
      "outputs": [],
      "source": [
        "async for event in agent.async_stream_query(\n",
        "    user_id=\"user_123\",\n",
        "    message=\"What is the exchange rate from US dollars to SEK today?\",\n",
        "):\n",
        "    print(event)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2a4e033321ad"
      },
      "source": [
        "## Cleaning up\n",
        "\n",
        "To avoid incurring unnecessary charges, clean up the resources when you're done.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JV9yOhQzEQDi"
      },
      "source": [
        "### Destroy specific deployment"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Aei-DjClEO0V"
      },
      "outputs": [],
      "source": [
        "# Destroy the basic deployment\n",
        "! cd ./agent-engine-terraform/01-basic-agent && terraform destroy"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FucJooqUEWgJ"
      },
      "source": [
        "### Destroy all agent deployments"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CKlrSu2zEYpO"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "\n",
        "\n",
        "def list_subfolders(folder_path):\n",
        "    \"\"\"Lists all subfolders in a given folder path.\"\"\"\n",
        "    return [\n",
        "        os.path.join(folder_path, d)\n",
        "        for d in os.listdir(folder_path)\n",
        "        if os.path.isdir(os.path.join(folder_path, d))\n",
        "    ]\n",
        "\n",
        "\n",
        "# Get all deployment folders\n",
        "folder_to_check = \"./agent-engine-terraform\"\n",
        "subfolders = list_subfolders(folder_to_check)\n",
        "\n",
        "# Destroy each deployment\n",
        "for folder in subfolders:\n",
        "    print(f\"Destroying agent in {folder}...\")\n",
        "    ! cd {folder} && terraform destroy -auto-approve\n",
        "    print(f\"Destroyed agent in {folder}!\\n\")"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "tutorial_get_started_with_agent_engine_terraform_deployment.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
