Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

☁️ Google Cloud Vertex AI Feature Store Terraform Module

Provisions a single Vertex AI Feature Store (google_vertex_ai_featurestore) — a centralized, versioned repository for ML feature values used in both online (low-latency serving) and offline (training/batch) contexts. Targets hashicorp/google ~> 7.0, Terraform >= 1.12.0.

Terraform Provider Version Type Resources Posture


🧩 Overview

  • 🗄️ Provisions one Vertex AI Feature Store (google_vertex_ai_featurestore.this) — the top-level container for ML feature values, servable both online (low-latency) and offline (training/batch).
  • 🚫 Does not manage entity types or features. google_vertex_ai_featurestore_entitytype and google_vertex_ai_featurestore_entitytype_feature are separate resources with their own lifecycle — deliberately deferred out of v1.0.0 scope (see 🧠 Architecture Notes).
  • ⚖️ Enforces the online_serving_config fixed_node_count XOR scaling{min,max} mutual exclusion at plan time via validation {}, not left to an apply-time API rejection.
  • 💰 Never defaults online_serving_config. Online-serving nodes are standing infrastructure billed regardless of query volume — the empty call stays offline-only, the safe/inert result.
  • ⚠️ force_destroy defaults false — a real cascading-delete trap otherwise, since entity types/features (not tracked in this module's state) block a plain destroy.
  • 🛡️ deletion_policy = "PREVENT" by default (this resource has no deletion_protection boolean).
  • 🔒 CMEK (encryption_spec.kms_key_name) accepted as an optional variable, never defaulted to a specific key.
  • 🚧 online_storage_ttl_days intentionally excluded — Beta-launch-stage only, out of scope for this GA-only library.

💡 Why it matters: Vertex AI Feature Store lets ML teams compute a feature once and serve it consistently to both training pipelines and low-latency online prediction — avoiding train/serve skew. Because online-serving capacity is billed as standing infrastructure rather than per-request, this module treats that choice the same way terraform-google-spanner-instance treats instance capacity: never a silent default, always an explicit opt-in.


❤️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!


🗺️ Where this fits

This module is part of the new data-ml domain, authored alongside terraform-google-vertex-ai-dataset and terraform-google-vertex-ai-endpoint — the first Vertex AI slice in this catalog. It optionally consumes terraform-google-kms-keyring's crypto key id for CMEK. Entity types are a genuinely separate, out-of-scope sibling resource this module does not create (shown dashed below) — not a for_each child.

flowchart LR
 kms["terraform-google-kms-keyring"]:::upstream
 kms -->|"optional CMEK key"| fs

 subgraph domain["data-ml domain"]
 direction LR
 fs["terraform-google-vertex-ai-featurestore"]:::self
 ds["terraform-google-vertex-ai-dataset"]:::sibling
 ep["terraform-google-vertex-ai-endpoint"]:::sibling
 end

 entitytype["google_vertex_ai_featurestore_entitytype<br/>(separate resource — NOT managed by this module)"]:::excluded
 fs -.->|"out-of-scope sibling relationship"| entitytype

 classDef self fill:#4285F4,color:#FFFFFF,stroke:#174EA6,stroke-width:2px;
 classDef sibling fill:#ECEFF1,color:#263238,stroke:#90A4AE,stroke-width:1px;
 classDef upstream fill:#174EA6,color:#FFFFFF,stroke:#0D47A1,stroke-width:2px;
 classDef excluded fill:#FFFFFF,color:#616161,stroke:#BDBDBD,stroke-width:1px,stroke-dasharray: 5 5;
Loading

Validated via the Mermaid Chart MCP (validate_and_render_mermaid_diagram) before embedding.


🧬 What this builds

No for_each children — entity types/features are a separate resource this module intentionally does not manage (shown dashed below as an out-of-scope sibling relationship, not a child).

flowchart TB
 subgraph Module["terraform-google-vertex-ai-featurestore"]
 direction TB
 fs["google_vertex_ai_featurestore.this<br/>(keystone)"]:::keystone
 osc["online_serving_config (dynamic)<br/>fixed_node_count XOR scaling"]:::nested
 enc["encryption_spec (dynamic)<br/>optional CMEK"]:::nested
 to["timeouts (dynamic)<br/>create/update/delete"]:::nested
 end

 fs --> osc
 fs --> enc
 fs --> to

 entitytype["google_vertex_ai_featurestore_entitytype<br/>+..._feature<br/>(separate resource — out of scope)"]:::excluded
 fs -.->|"sibling relationship, not a for_each child"| entitytype

 classDef keystone fill:#174EA6,color:#FFFFFF,stroke:#0D47A1,stroke-width:2px;
 classDef nested fill:#ECEFF1,color:#263238,stroke:#90A4AE,stroke-width:1px;
 classDef excluded fill:#FFFFFF,color:#616161,stroke:#BDBDBD,stroke-width:1px,stroke-dasharray: 5 5;
Loading

Validated via the Mermaid Chart MCP before embedding.

Resource inventory (1 resource type):

Resource Count Role
google_vertex_ai_featurestore.this 1 Keystone — the top-level Feature Store container

✅ Provider / Versions

Requirement Value
Terraform >= 1.12.0
hashicorp/google ~> 7.0
Provider block None — the caller's root module configures google (ADC, WIF, or a service-account key per our authentication model)

Schema notes that bite:

  • online_serving_config is a fixed_node_count XOR scaling mutual exclusion. Confirmed live Argument Reference: "Only one of fixedNodeCount and scaling can be set. Setting one will reset the other." Enforced by this module's validation {}.
  • online_storage_ttl_days is Beta-launch-stage only — the live docs' second Example Usage block literally requires provider = google-beta to set it. Excluded entirely from this GA-only module.
  • force_destroy = true is required to delete a Featurestore that still contains entity types/features — confirmed live schema wording. Defaults to false.
  • No self_link attribute exists. Confirmed Attributes Reference lists only id, etag, create_time, update_time, terraform_labels, effective_labels.
  • name is Optional but not Optional+Computed — confirmed via the schema-JSON fallback. No confirmed auto-naming behavior on omission; an explicit name is recommended.

🔑 Required IAM Roles

  • roles/aiplatform.user on the target project — sufficient to create/update/delete a Featurestore.

(Sourced directly from SCOPE.md — not re-derived.)


☁️ GCP Prerequisites

  • aiplatform.googleapis.com API enabled on the target project (via terraform-google-project-services, applied before this module per the house recommended authoring order).
  • cloudkms.googleapis.com additionally required if encryption_spec is set.
  • Vertex AI enforces per-project, per-region quotas on online-serving node counts — invisible to terraform plan; verify current quota before an online_serving_config request would exceed it.

(Sourced directly from SCOPE.md — not re-derived.)


📁 Module Structure

terraform-google-vertex-ai-featurestore/
├── providers.tf # required_providers (hashicorp/google ~> 7.0) + required_version — no provider {} block
├── variables.tf # name/region args, online_serving_config mutual-exclusion validation, encryption_spec, universal tail
├── main.tf # google_vertex_ai_featurestore.this — no for_each children
├── outputs.tf # id, name, etag, create_time, update_time — no self_link
├── README.md # this file
├── SCOPE.md # cross-module contract
└── examples/ # runnable example(s) matching the Quick Start below

⚙️ Quick Start

# Caller's root module configures the google provider (ADC, WIF, or a service-account key) —
# this module never declares project/zone/credentials variables (region is this resource's own
# optional argument).

module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_customer_features"
  region = "us-central1"
}

🔌 Cross-Module Contract

Consumes

Input Type Source module
CMEK crypto key name (encryption_spec.kms_key_name) — optional string (full KMS crypto key resource path) terraform-google-kms-keyring
(none required) — runs standalone with no online serving and no CMEK by default — —

Emits

Output Description Consumed by
id Featurestore resource id, projects/{{project}}/locations/{{region}}/featurestores/{{name}} None identified yet in the current catalog
name Featurestore name (as supplied, or API-assigned if var.name was left unset) Future entity-type tooling
etag Consistent read-modify-write token Diagnostic/reference use
create_time RFC3339 UTC creation timestamp Diagnostic/reference use
update_time RFC3339 UTC last-update timestamp Diagnostic/reference use

ℹ️ No self_link row — google_vertex_ai_featurestore does not expose a self_link attribute. This is a deliberate, documented deviation from the library's default "id then self_link" ordering, not an omission — same precedent as terraform-google-kms-keyring and terraform-google-spanner-instance.


📚 Example Library

1 · Minimal Featurestore, no online serving
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_offline_only_features"
  region = "us-central1"
}

ℹ️ online_serving_config defaults to null — an offline-only Featurestore (no standing online-serving nodes, no cost) is the safe starting point.

2 · Fixed-node-count online serving
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_realtime_features"
  region = "us-central1"

  online_serving_config = {
    fixed_node_count = 2
  }
}

💰 fixed_node_count provisions standing online-serving capacity billed regardless of query volume — a cost-safety consideration, not a security control (see 🧱 Design Principles).

3 · Autoscaled online serving (min/max nodes)
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_variable_traffic_features"
  region = "us-central1"

  online_serving_config = {
    scaling = {
      min_node_count = 2
      max_node_count = 10
    }
  }
}

⚠️ max_node_count must be greater than min_node_count and no more than 10x min_node_count (here, 10 <= 2 x 10 — right at the boundary) — enforced by this module's validation {} at plan time, not left to an apply-time API rejection.

4 · ⚠️ Invalid: setting both fixed_node_count and scaling
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_invalid_example"
  region = "us-central1"

  online_serving_config = {
    fixed_node_count = 2
    scaling = {
      min_node_count = 2
      max_node_count = 10
    }
  }
}

⚠️ This fails terraform plan with this module's mutual-exclusion validation {} error. Only one of fixed_node_count or scaling may be set per the google_vertex_ai_featurestore API — "Setting one will reset the other." Shown here deliberately as a negative example.

5 · CMEK-encrypted Featurestore
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_regulated_features"
  region = "us-central1"

  encryption_spec = {
    kms_key_name = "projects/casey-prod-security/locations/us-central1/keyRings/casey-prod-cmek/cryptoKeys/vertex-featurestore-key"
  }
}

🔒 Both online and offline storage are secured by this key once set. Never defaulted to a specific key by this module; the key must be in the same region as the Featurestore.

6 · ⚠️ force_destroy for a Featurestore with existing entity types
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name          = "casey_decommissioned_features"
  region        = "us-central1"
  force_destroy = true
}

⚠️ Read this before setting force_destroy = true. Entity types and features are a separate resource this module does not create or track (see 🧠 Architecture Notes) — if any exist under this Featurestore, force_destroy = true deletes ALL of them, permanently, the moment this Featurestore is destroyed. Without this flag, that same destroy fails outright with a "Featurestore still has entity types" API error (see 🔍 Troubleshooting). Set this only after confirming the blast radius with whatever owns those entity types.

7 · deletion_policy = "DELETE" for a genuinely disposable dev Featurestore
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name            = "casey_dev_scratch_features"
  region          = "us-central1"
  deletion_policy = "DELETE"
}

ℹ️ This module defaults deletion_policy = "PREVENT" — the provider's own default is "DELETE". Opting back into "DELETE" restores normal terraform destroy behavior; only appropriate for a Featurestore that never holds data anyone needs to recover.

8 · deletion_policy = "ABANDON" to remove from Terraform state without deleting
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name            = "casey_migrating_to_manual_ownership"
  region          = "us-central1"
  deletion_policy = "ABANDON"
}

ℹ️ On a subsequent terraform destroy/apply that would remove this resource, Terraform drops it from state without calling the delete API — the Featurestore itself continues to exist in GCP, now unmanaged by Terraform. Useful when handing a Featurestore off to a different ownership model without destroying its data.

9 · Custom name matching the closed-format validation
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_marketing_attribution_features_v2"
  region = "us-central1"
}

ℹ️ name must be at most 60 characters, contain only lowercase letters, numbers, or underscores, and must not start with a number — enforced by this module's validation {} at plan time.

10 · Custom create/update/delete timeouts
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_slow_provisioning_features"
  region = "us-central1"

  timeouts = {
    create = "40m"
    update = "40m"
    delete = "40m"
  }
}

ℹ️ All three timeout operations are supported, each defaulting to 20 minutes per the live schema — override any subset via this object.

11 · Labels for cost allocation and ownership tracking
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_shared_features"
  region = "us-central1"

  labels = {
    team        = "ml-platform"
    environment = "prod"
    cost_center = "data_science"
  }
}

ℹ️ labels is non-authoritative — this module only manages the labels present in this configuration, per the provider's own confirmed schema note.

12 · Fixed-node online serving plus CMEK together
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_regulated_realtime_features"
  region = "us-central1"

  online_serving_config = {
    fixed_node_count = 3
  }

  encryption_spec = {
    kms_key_name = "projects/casey-prod-security/locations/us-central1/keyRings/casey-prod-cmek/cryptoKeys/vertex-featurestore-key"
  }

  deletion_policy = "PREVENT"
}

🔒 CMEK and online serving are independent concerns — both can be set together with no interaction between this module's mutual-exclusion validation (which only governs fixed_node_count vs. scaling) and the encryption configuration.

13 · Minimal autoscaled floor (min_node_count = 1)
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_low_traffic_features"
  region = "us-central1"

  online_serving_config = {
    scaling = {
      min_node_count = 1
      max_node_count = 2
    }
  }
}

ℹ️ min_node_count must be >= 1 — this module's validation {} rejects 0 or negative values at plan time.

14 · Region matching a specific CMEK key ring location
module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_europe_regulated_features"
  region = "europe-west1"

  encryption_spec = {
    kms_key_name = "projects/casey-prod-security/locations/europe-west1/keyRings/casey-eu-cmek/cryptoKeys/vertex-featurestore-key"
  }
}

⚠️ The CMEK key must be in the same region as the Featurestore (confirmed live schema note) — here both are europe-west1. A mismatch is not caught by this library's plan-only validate; it surfaces only at apply.

15 · 🏗️ End-to-end composition

Wires an optional terraform-google-kms-keyring crypto key into this module's encryption_spec, sized with autoscaled online serving for a production ML feature-serving workload.

module "kms_keyring" {
  source = "git::https://github.com/microsoftexpert/terraform-google-kms-keyring.git?ref=v1.0.0"

  key_ring_name = "casey-prod-cmek"
  location      = "us-central1"

  crypto_keys = {
    "vertex-featurestore-key" = {
      purpose = "ENCRYPT_DECRYPT"
      labels = {
        team = "ml-platform"
      }
    }
  }
}

module "featurestore" {
  source = "git::https://github.com/microsoftexpert/terraform-google-vertex-ai-featurestore.git?ref=v1.0.0"

  name   = "casey_prod_customer_features"
  region = "us-central1"

  online_serving_config = {
    scaling = {
      min_node_count = 2
      max_node_count = 10
    }
  }

  # Consumes terraform-google-kms-keyring's crypto_key_ids output (Emits table row: `crypto_key_ids`) —
  # never a hardcoded key resource name.
  encryption_spec = {
    kms_key_name = module.kms_keyring.crypto_key_ids["vertex-featurestore-key"]
  }

  labels = {
    team        = "ml-platform"
    environment = "prod"
  }

  deletion_policy = "PREVENT"
  force_destroy   = false
}

# Hypothetical downstream consumer — no such module exists in the current catalog yet.
# output "featurestore_id" {
# value = module.featurestore.id
# }

⚠️ Before this composition can succeed, the Vertex AI service agent for the target project must be granted roles/cloudkms.cryptoKeyEncrypterDecrypter directly on module.kms_keyring.crypto_key_ids["vertex-featurestore-key"] — a binding out of scope for both modules by design. Grant it via a dedicated IAM aggregation module or the composing root module, applied after the crypto key exists and before this module's Featurestore is created; allow for IAM propagation lag (up to ~60 seconds) between the grant and the Featurestore apply.


📥 Inputs

Variable Type Default Notes
name string null Optional but not Optional+Computed — see Architecture Notes
region string null Optional + Computed; not validated against a hardcoded region list
online_serving_config object({...}) null fixed_node_count XOR scaling — see full schema below
encryption_spec object({ kms_key_name = string }) null CMEK — never defaulted to a specific key
deletion_policy string "PREVENT" Secure-default extension ("DELETE" | "ABANDON" | "PREVENT")
force_destroy bool false Cascades deletion to entity types/features when true
labels map(string) {} Non-authoritative per the provider's own schema note
timeouts object({...}) null create/update/delete, 20m provider default each
Full online_serving_config object schema
variable "online_serving_config" {
  type = object({
    fixed_node_count = optional(number)
    scaling = optional(object({
      min_node_count = number
      max_node_count = number
    }))
  })
  default = null
}
Full encryption_spec object schema
variable "encryption_spec" {
  type = object({
    kms_key_name = string
  })
  default = null
}
Full timeouts object schema
variable "timeouts" {
  type = object({
    create = optional(string)
    update = optional(string)
    delete = optional(string)
  })
  default = null
}

🧾 Outputs

Output Description Sensitive
id Featurestore resource id (projects/{{project}}/locations/{{region}}/featurestores/{{name}}) No
name Featurestore name No
etag Read-modify-write consistency token No
create_time RFC3339 UTC creation timestamp No
update_time RFC3339 UTC last-update timestamp No

ℹ️ No self_link output — see 🔌 Cross-Module Contract and 🧠 Architecture Notes.


🧠 Architecture Notes

  • force_destroy = true is required to delete a Featurestore that still contains entity types/features — the single most important operational note in this module. Confirmed live schema wording: "If set to true, any EntityTypes and Features for this Featurestore will also be deleted." Because entity types/features are managed by a separate resource this module does not track in its own state (see below), an operator who created entity types via other tooling and then runs terraform destroy against this module will hit a "Featurestore still has entity types" API error unless force_destroy = true was set deliberately, in advance, with the full cascading-deletion blast radius understood.
  • Entity types and features are a deliberately deferred, out-of-scope sibling relationship, not a silent gap. google_vertex_ai_featurestore_entitytype and google_vertex_ai_featurestore_entitytype_feature are independent resources with their own lifecycle — created/updated far more frequently and at higher cardinality than the Featurestore container itself. This module intentionally stays a single-keystone standalone rather than reaching for a for_each shape that does not match the resource's own schema — the same "meaningfully created independently" reasoning already applied to terraform-google-certificate-authority deferring leaf certificate issuance out of the CA pool module.
  • online_serving_config sizing is a cost-safety consideration, not a security control. fixed_node_count/scaling.min_node_count provision STANDING infrastructure billed regardless of actual query volume — unlike most GCP services in this catalog, which are pay-per-request. This module never defaults online_serving_config to a non-null value; the provider's own default (no online-serving nodes, offline-only Featurestore) is preserved.
  • online_serving_config's mutual exclusion and arithmetic constraint are both enforced via validation {} at plan time. The mutual exclusion (fixed_node_count XOR scaling) and the scaling.max_node_count > min_node_count <= 10x min_node_count relationship are both stated as unambiguous hard constraints in the live provider docs — a deliberate choice to enforce both here, distinct from this catalog's precedent of leaving softer, guidance-only numeric relationships (e.g. Spanner's autoscaling_targets) unenforced.
  • No self_link attribute exists. Confirmed against the live Attributes Reference — this module's outputs deliberately omit a self_link row.
  • name is Optional but not Optional+Computed. Confirmed via the schema-JSON fallback. Because the field carries no Computed flag, this module does not assume any auto-naming behavior on omission the way terraform-google-spanner-instance does for its own optional name (which is confirmed Optional+Computed with documented random-name generation) — supplying an explicit name for this resource is recommended in practice.
  • encryption_spec.kms_key_name must be in the same region as the Featurestore. Confirmed live schema note; not enforced by this module's validation {} (cross-referencing an external key ring's region is not something this module's inputs alone can verify) — a mismatch surfaces only at apply.
  • deletion_policy = "PREVENT" is independent of force_destroy. The former blocks any destroy outright regardless of the Featurestore's contents; the latter only matters once a destroy is actually permitted to proceed (deletion_policy not "PREVENT").
  • IAM propagation delay. This module grants no IAM itself, but a composition that grants roles/aiplatform.user or a CMEK crypto-key-level role immediately before this module's apply may see a transient permission-denied error for up to ~60 seconds after the grant.

🧱 Design Principles

Concern Secure default Opt-out (explicit)
Featurestore destroy guard (no deletion_protection field exists) deletion_policy = "PREVENT" Caller sets "DELETE" or "ABANDON"
Cascading deletion of entity types/features on destroy force_destroy = false Caller sets true, understanding the full blast radius
Online-serving capacity (cost-safety, not a security control) Never defaulted — online_serving_config = null (offline-only, no standing nodes) Caller sets exactly one of fixed_node_count or scaling
CMEK Accepted as an optional variable, never defaulted to a specific key Caller supplies encryption_spec.kms_key_name explicitly
Beta-launch-stage fields online_storage_ttl_days excluded entirely from this GA-only module N/A — use google-beta directly outside this library if required
Empty call online_serving_config/encryption_spec/timeouts all default null — an offline-only, Google-managed-encryption Featurestore, the safe, inert result Caller supplies each explicitly

🚀 Runbook

cd C:\GitHubCode\newgooglecloudmodules\terraform-google-vertex-ai-featurestore
terraform init -backend=false
terraform validate
terraform fmt -check

Pin ?ref=v1.0.0 when consuming this module — never a branch. This library is plan-only; a human applies from CI with valid Workload Identity Federation or ADC credentials.


🧪 Testing

  • terraform init -backend=false, terraform validate, and terraform fmt -check are the entire offline proof gate for this module — all three pass cleanly as of this authoring session, against the actually-resolved hashicorp/google provider version 7.39.0.
  • validate/fmt confirm internal type/reference consistency and canonical formatting only. Neither can catch GCP API-level rejections — most notably, a "Featurestore still has entity types" destroy failure only surfaces at apply, against a real project, never at plan. Quota, org policy, IAM propagation, and CMEK region mismatches are equally invisible to this offline gate.
  • The examples/ directory exists so a consuming GitHub Actions workflow can run a real terraform plan against a real project as part of that pipeline's own review gate; this library only guarantees the example is syntactically and structurally sound in isolation.

💬 Example Output

$ terraform output

id = "projects/casey-prod-ml/locations/us-central1/featurestores/casey_prod_customer_features"
name = "casey_prod_customer_features"
etag = "AYABAgMEBQYHCAkKCwwNDg8="
create_time = "2026-07-12T14:03:11.482910123Z"
update_time = "2026-07-12T14:03:11.482910123Z"

🔍 Troubleshooting

Symptom Cause Fix
terraform destroy/apply fails with a "Featurestore still has entity types" (or similar) API error Entity types/features exist under this Featurestore (created via separate tooling this module does not track) and force_destroy is left at its default false Set force_destroy = true deliberately, understanding the cascading-deletion blast radius, or delete the entity types/features first via their own tooling
terraform destroy fails even though force_destroy = true deletion_policy is still "PREVENT" (this module's default) Set deletion_policy = "DELETE" (or "ABANDON") explicitly in a prior apply, then retry the destroy
Error: online_serving_config: only one of fixed_node_count or scaling may be set... at plan time Both fixed_node_count and scaling were supplied in the same online_serving_config object Set only one; leave the other unset (null)
Error: online_serving_config.scaling.max_node_count: must be greater than min_node_count and less than or equal to 10x min_node_count at plan time scaling.max_node_count violates the confirmed live schema arithmetic constraint Adjust max_node_count to satisfy min_node_count < max_node_count <= 10 * min_node_count
Error: online_serving_config.scaling.min_node_count: must be greater than or equal to 1 at plan time scaling.min_node_count was set to 0 or a negative number Set min_node_count >= 1
terraform plan shows no error but apply fails with a CMEK-related permission or region-mismatch error encryption_spec.kms_key_name's region does not match var.region, or the Vertex AI service agent lacks the required Cloud KMS IAM role on the key Confirm the key ring's location matches var.region; grant roles/cloudkms.cryptoKeyEncrypterDecrypter to the Vertex AI service agent on the key before apply, allowing for IAM propagation lag
apply fails with a quota-exceeded error on online-serving nodes despite a clean plan Vertex AI enforces per-project, per-region online-serving node quotas invisible to terraform plan Verify current quota in the target project/region before increasing fixed_node_count or scaling.max_node_count

🔗 Related Docs


💙 "Infrastructure as Code should be standardized, consistent, and secure."

About

Terraform module: terraform-google-vertex-ai-featurestore

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages