From 275dd6a9eacf35cb1025001b30f8a5ddeceb2f69 Mon Sep 17 00:00:00 2001 From: Luca Prete Date: Wed, 22 Apr 2026 16:05:09 +0200 Subject: [PATCH] Add enable_deletion_protection variable to agent engine module (#3898) --- modules/agent-engine/README.md | 49 ++++-- modules/agent-engine/agent-managed.tf | 5 + modules/agent-engine/agent-unmanaged.tf | 5 + modules/agent-engine/main.tf | 2 +- modules/agent-engine/variables.tf | 8 +- modules/cloudsql-instance/main.tf | 1 - modules/project/service-agents.yaml | 1 - skills/maintenance/release-process/SKILL.md | 6 +- .../examples/deletion-protection.yaml | 165 ++++++++++++++++++ 9 files changed, 225 insertions(+), 17 deletions(-) create mode 100644 tests/modules/agent_engine/examples/deletion-protection.yaml diff --git a/modules/agent-engine/README.md b/modules/agent-engine/README.md index d99fe4e67..c3940ef44 100644 --- a/modules/agent-engine/README.md +++ b/modules/agent-engine/README.md @@ -25,6 +25,7 @@ The module creates Agent Engine and related dependencies. - [Container-based deployment](#container-based-deployment) - [Memory Bank](#memory-bank) - [Getting values from context](#getting-values-from-context) +- [Disable deletion protection](#disable-deletion-protection) - [Variables](#variables) - [Outputs](#outputs) @@ -412,23 +413,51 @@ module "agent_engine" { } # tftest inventory=context.yaml ``` + +## Disable deletion protection + +By default you can't neither delete your agent if it has session or your GCS bucket if it has files inside. For testing, you can anyway force the deletion of these resources: + +```hcl +module "agent_engine" { + source = "./fabric/modules/agent-engine" + name = "my-agent" + project_id = var.project_id + region = var.region + enable_deletion_protection = false + + agent_engine_config = { + agent_framework = "google-adk" + } + + deployment_config = { + package_config = { + pickle_path = "assets/src/pickle.pkl" + dependencies_path = "assets/src/dependencies.tar.gz" + requirements_path = "assets/src/requirements.txt" + } + } +} +# tftest inventory=deletion-protection.yaml +``` ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L172) | The name of the agent. | string | ✓ | | -| [project_id](variables.tf#L191) | The id of the project where to deploy the agent. | string | ✓ | | -| [region](variables.tf#L197) | The region where to deploy the agent. | string | ✓ | | +| [name](variables.tf#L178) | The name of the agent. | string | ✓ | | +| [project_id](variables.tf#L197) | The id of the project where to deploy the agent. | string | ✓ | | +| [region](variables.tf#L203) | The region where to deploy the agent. | string | ✓ | | | [agent_engine_config](variables.tf#L17) | The agent configuration. Supported values for agent_framework: 'google-adk', 'langchain', 'langgraph', 'ag2', 'llama-index', 'custom'. | object({…}) | | {} | | [bucket_config](variables.tf#L41) | The GCS bucket configuration. | object({…}) | | {} | -| [context](variables.tf#L53) | Context-specific interpolations. | object({…}) | | {} | -| [deployment_config](variables.tf#L69) | The deployment configuration. | object({…}) | | {} | -| [description](variables.tf#L129) | The Agent Engine description. | string | | "Terraform managed." | -| [encryption_key](variables.tf#L136) | The full resource name of the Cloud KMS CryptoKey. | string | | null | -| [managed](variables.tf#L142) | Whether the Terraform module should control the code updates. | bool | | true | -| [memory_bank_config](variables.tf#L149) | Configuration for the memory bank. | object({…}) | | null | -| [networking_config](variables.tf#L178) | Networking configuration. | object({…}) | | null | +| [context](variables.tf#L52) | Context-specific interpolations. | object({…}) | | {} | +| [deployment_config](variables.tf#L68) | The deployment configuration. | object({…}) | | {} | +| [description](variables.tf#L128) | The Agent Engine description. | string | | "Terraform managed." | +| [enable_deletion_protection](variables.tf#L135) | Whether deletion protection should be enabled. | bool | | true | +| [encryption_key](variables.tf#L142) | The full resource name of the Cloud KMS CryptoKey. | string | | null | +| [managed](variables.tf#L148) | Whether the Terraform module should control the code updates. | bool | | true | +| [memory_bank_config](variables.tf#L155) | Configuration for the memory bank. | object({…}) | | null | +| [networking_config](variables.tf#L184) | Networking configuration. | object({…}) | | null | | [service_account_config](variables-serviceaccount.tf#L18) | Service account configurations. | object({…}) | | {} | ## Outputs diff --git a/modules/agent-engine/agent-managed.tf b/modules/agent-engine/agent-managed.tf index 5908eb2c0..8e2f81ed8 100644 --- a/modules/agent-engine/agent-managed.tf +++ b/modules/agent-engine/agent-managed.tf @@ -21,6 +21,11 @@ resource "google_vertex_ai_reasoning_engine" "managed" { project = local.project_id description = var.description region = local.location + deletion_policy = ( + var.enable_deletion_protection + ? null + : "FORCE" + ) dynamic "encryption_spec" { for_each = var.encryption_key == null ? {} : { 1 = 1 } diff --git a/modules/agent-engine/agent-unmanaged.tf b/modules/agent-engine/agent-unmanaged.tf index c3ac76d77..4c350feb1 100644 --- a/modules/agent-engine/agent-unmanaged.tf +++ b/modules/agent-engine/agent-unmanaged.tf @@ -21,6 +21,11 @@ resource "google_vertex_ai_reasoning_engine" "unmanaged" { project = local.project_id description = var.description region = local.location + deletion_policy = ( + var.enable_deletion_protection + ? null + : "FORCE" + ) dynamic "encryption_spec" { for_each = var.encryption_key == null ? {} : { 1 = 1 } diff --git a/modules/agent-engine/main.tf b/modules/agent-engine/main.tf index 78c67492f..2195be6d1 100644 --- a/modules/agent-engine/main.tf +++ b/modules/agent-engine/main.tf @@ -63,7 +63,7 @@ resource "google_storage_bucket" "default" { project = local.project_id location = local.location uniform_bucket_level_access = var.bucket_config.uniform_bucket_level_access - force_destroy = !var.bucket_config.deletion_protection + force_destroy = !var.enable_deletion_protection } resource "google_storage_bucket_object" "dependencies" { diff --git a/modules/agent-engine/variables.tf b/modules/agent-engine/variables.tf index 3442c5f95..fa2260c39 100644 --- a/modules/agent-engine/variables.tf +++ b/modules/agent-engine/variables.tf @@ -42,7 +42,6 @@ variable "bucket_config" { description = "The GCS bucket configuration." type = object({ create = optional(bool, true) - deletion_protection = optional(bool, true) name = optional(string) uniform_bucket_level_access = optional(bool, true) }) @@ -133,6 +132,13 @@ variable "description" { default = "Terraform managed." } +variable "enable_deletion_protection" { + description = "Whether deletion protection should be enabled." + type = bool + nullable = false + default = true +} + variable "encryption_key" { description = "The full resource name of the Cloud KMS CryptoKey." type = string diff --git a/modules/cloudsql-instance/main.tf b/modules/cloudsql-instance/main.tf index 7f5802d78..ab18814bc 100644 --- a/modules/cloudsql-instance/main.tf +++ b/modules/cloudsql-instance/main.tf @@ -394,4 +394,3 @@ resource "google_sql_ssl_cert" "client_certificates" { instance = google_sql_database_instance.primary.name common_name = each.key } - diff --git a/modules/project/service-agents.yaml b/modules/project/service-agents.yaml index 06a3ce030..c3f2aa5ca 100644 --- a/modules/project/service-agents.yaml +++ b/modules/project/service-agents.yaml @@ -2124,4 +2124,3 @@ is_primary: false aliases: [] skip_iam: false - diff --git a/skills/maintenance/release-process/SKILL.md b/skills/maintenance/release-process/SKILL.md index 6249bae68..459284887 100644 --- a/skills/maintenance/release-process/SKILL.md +++ b/skills/maintenance/release-process/SKILL.md @@ -37,7 +37,7 @@ Run the changelog script, specifying the bump type (e.g., `major`, `minor`, `pat ```bash # Set your bump type here (major, minor, or patch) -BUMP_TYPE="minor" +BUMP_TYPE="minor" # Option A: Using uv (Recommended) uv run tools/changelog.py --release-from $LATEST_RELEASE --bump $BUMP_TYPE --write --token $(gh auth token) @@ -111,7 +111,7 @@ BREAKING_CHANGES=$(echo "$RELEASE_NOTES" | awk '/^### BREAKING CHANGES/{flag=1; if [ -n "$(echo "$BREAKING_CHANGES" | tr -d '[:space:]')" ]; then # Convert "### BREAKING CHANGES" to "### Breaking Changes" FORMATTED_BREAKING_CHANGES=$(echo "$BREAKING_CHANGES" | sed 's/^### BREAKING CHANGES/### Breaking Changes/') - + # Prepend breaking changes to the generated notes gh release create "$NEW_RELEASE" --title "$NEW_RELEASE" --generate-notes --notes "$FORMATTED_BREAKING_CHANGES" else @@ -129,4 +129,4 @@ Go to the [GitHub Releases UI](https://github.com/GoogleCloudPlatform/cloud-foun 3. **Release Notes:** Click the **"Generate release notes"** button. 4. **Breaking Changes:** If the `CHANGELOG.md` contains a "BREAKING CHANGES" section for this release, copy it, paste it at the **top** of the generated release notes, and change the heading to "Breaking Changes". -Click **Publish release**. \ No newline at end of file +Click **Publish release**. diff --git a/tests/modules/agent_engine/examples/deletion-protection.yaml b/tests/modules/agent_engine/examples/deletion-protection.yaml new file mode 100644 index 000000000..e5fb01bfc --- /dev/null +++ b/tests/modules/agent_engine/examples/deletion-protection.yaml @@ -0,0 +1,165 @@ +# Copyright 2026 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 +# +# http://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. + +values: + module.agent_engine.google_project_iam_member.default["roles/aiplatform.user"]: + condition: [] + member: serviceAccount:my-agent@project-id.iam.gserviceaccount.com + project: project-id + role: roles/aiplatform.user + module.agent_engine.google_project_iam_member.default["roles/storage.objectViewer"]: + condition: [] + member: serviceAccount:my-agent@project-id.iam.gserviceaccount.com + project: project-id + role: roles/storage.objectViewer + module.agent_engine.google_project_iam_member.default["roles/viewer"]: + condition: [] + member: serviceAccount:my-agent@project-id.iam.gserviceaccount.com + project: project-id + role: roles/viewer + module.agent_engine.google_service_account.service_account[0]: + account_id: my-agent + create_ignore_already_exists: null + description: null + disabled: false + display_name: my-agent + email: my-agent@project-id.iam.gserviceaccount.com + member: serviceAccount:my-agent@project-id.iam.gserviceaccount.com + project: project-id + timeouts: null + module.agent_engine.google_storage_bucket.default[0]: + autoclass: [] + cors: [] + custom_placement_config: [] + default_event_based_hold: null + effective_labels: + goog-terraform-provisioned: 'true' + enable_object_retention: null + encryption: [] + force_destroy: true + hierarchical_namespace: [] + ip_filter: [] + labels: null + lifecycle_rule: [] + location: EUROPE-WEST8 + logging: [] + name: my-agent + project: project-id + requester_pays: null + retention_policy: [] + storage_class: STANDARD + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + uniform_bucket_level_access: true + module.agent_engine.google_storage_bucket_object.dependencies[0]: + bucket: my-agent + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + contexts: [] + customer_encryption: [] + deletion_policy: null + detect_md5hash: null + event_based_hold: null + force_empty_content_type: null + metadata: null + name: dependencies.tar.gz + retention: [] + source: assets/src/dependencies.tar.gz + source_md5hash: 49a4c43e6bef605c2fa6ddabac48ba6a + temporary_hold: null + timeouts: null + module.agent_engine.google_storage_bucket_object.pickle[0]: + bucket: my-agent + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + contexts: [] + customer_encryption: [] + deletion_policy: null + detect_md5hash: null + event_based_hold: null + force_empty_content_type: null + metadata: null + name: pickle.pkl + retention: [] + source: assets/src/pickle.pkl + source_md5hash: 493cf9bf3e59e39913e61916549f95a5 + temporary_hold: null + timeouts: null + module.agent_engine.google_storage_bucket_object.requirements[0]: + bucket: my-agent + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + contexts: [] + customer_encryption: [] + deletion_policy: null + detect_md5hash: null + event_based_hold: null + force_empty_content_type: null + metadata: null + name: requirements.txt + retention: [] + source: assets/src/requirements.txt + source_md5hash: 0acf2b14e855722af60e03e8fa8b04ff + temporary_hold: null + timeouts: null + module.agent_engine.google_vertex_ai_reasoning_engine.managed[0]: + context_spec: [] + deletion_policy: FORCE + description: Terraform managed. + display_name: my-agent + effective_labels: + goog-terraform-provisioned: 'true' + encryption_spec: [] + labels: null + project: project-id + region: europe-west8 + spec: + - agent_framework: google-adk + class_methods: null + container_spec: [] + deployment_spec: [] + identity_type: null + package_spec: + - dependency_files_gcs_uri: gs://my-agent/dependencies.tar.gz + pickle_object_gcs_uri: gs://my-agent/pickle.pkl + python_version: '3.13' + requirements_gcs_uri: gs://my-agent/requirements.txt + service_account: my-agent@project-id.iam.gserviceaccount.com + source_code_spec: [] + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + module.agent_engine.time_sleep.wait_5_minutes: + create_duration: 5m + destroy_duration: null + triggers: null + +counts: + google_project_iam_member: 3 + google_service_account: 1 + google_storage_bucket: 1 + google_storage_bucket_object: 3 + google_vertex_ai_reasoning_engine: 1 + modules: 1 + resources: 10 + time_sleep: 1 + +outputs: {}