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. Targetshashicorp/google ~> 7.0, Terraform>= 1.12.0.
- 🗄️ 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_entitytypeandgoogle_vertex_ai_featurestore_entitytype_featureare separate resources with their own lifecycle — deliberately deferred out of v1.0.0 scope (see 🧠 Architecture Notes). - ⚖️ Enforces the
online_serving_configfixed_node_countXORscaling{min,max}mutual exclusion atplantime viavalidation {}, not left to anapply-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_destroydefaultsfalse— 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 nodeletion_protectionboolean). - 🔒 CMEK (
encryption_spec.kms_key_name) accepted as an optional variable, never defaulted to a specific key. - 🚧
online_storage_ttl_daysintentionally 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-instancetreats instance capacity: never a silent default, always an explicit opt-in.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- ⭐ Star this repository to help others discover this Terraform module.
- 🤝 Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- ☕ Buy me a coffee: buymeacoffee.com/microsoftexpert
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!
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;
Validated via the Mermaid Chart MCP (validate_and_render_mermaid_diagram) before embedding.
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;
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 |
| 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_configis afixed_node_countXORscalingmutual exclusion. Confirmed live Argument Reference: "Only one of fixedNodeCount and scaling can be set. Setting one will reset the other." Enforced by this module'svalidation {}.online_storage_ttl_daysis Beta-launch-stage only — the live docs' second Example Usage block literally requiresprovider = google-betato set it. Excluded entirely from this GA-only module.force_destroy = trueis required to delete a Featurestore that still contains entity types/features — confirmed live schema wording. Defaults tofalse.- No
self_linkattribute exists. Confirmed Attributes Reference lists onlyid,etag,create_time,update_time,terraform_labels,effective_labels. nameisOptionalbut notOptional+Computed— confirmed via the schema-JSON fallback. No confirmed auto-naming behavior on omission; an explicitnameis recommended.
roles/aiplatform.useron the target project — sufficient to create/update/delete a Featurestore.
(Sourced directly from SCOPE.md — not re-derived.)
aiplatform.googleapis.comAPI enabled on the target project (viaterraform-google-project-services, applied before this module per the house recommended authoring order).cloudkms.googleapis.comadditionally required ifencryption_specis set.- Vertex AI enforces per-project, per-region quotas on online-serving node counts — invisible to
terraform plan; verify current quota before anonline_serving_configrequest would exceed it.
(Sourced directly from SCOPE.md — not re-derived.)
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
# 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"
}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_linkrow —google_vertex_ai_featurestoredoes not expose aself_linkattribute. This is a deliberate, documented deviation from the library's default "id then self_link" ordering, not an omission — same precedent asterraform-google-kms-keyringandterraform-google-spanner-instance.
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_configdefaults tonull— 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_countprovisions 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_countmust be greater thanmin_node_countand no more than 10xmin_node_count(here, 10 <= 2 x 10 — right at the boundary) — enforced by this module'svalidation {}atplantime, not left to anapply-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 failsterraform planwith this module's mutual-exclusionvalidation {}error. Only one offixed_node_countorscalingmay be set per thegoogle_vertex_ai_featurestoreAPI — "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 settingforce_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 = truedeletes 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 normalterraform destroybehavior; 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/applythat 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"
}ℹ️
namemust be at most 60 characters, contain only lowercase letters, numbers, or underscores, and must not start with a number — enforced by this module'svalidation {}atplantime.
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"
}
}ℹ️
labelsis 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_countvs.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_countmust be >= 1 — this module'svalidation {}rejects0or negative values atplantime.
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 areeurope-west1. A mismatch is not caught by this library's plan-onlyvalidate; it surfaces only atapply.
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 grantedroles/cloudkms.cryptoKeyEncrypterDecrypterdirectly onmodule.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.
| 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
}| 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_linkoutput — see 🔌 Cross-Module Contract and 🧠 Architecture Notes.
force_destroy = trueis 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 runsterraform destroyagainst this module will hit a "Featurestore still has entity types" API error unlessforce_destroy = truewas 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_entitytypeandgoogle_vertex_ai_featurestore_entitytype_featureare 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 afor_eachshape that does not match the resource's own schema — the same "meaningfully created independently" reasoning already applied toterraform-google-certificate-authoritydeferring leaf certificate issuance out of the CA pool module. online_serving_configsizing is a cost-safety consideration, not a security control.fixed_node_count/scaling.min_node_countprovision STANDING infrastructure billed regardless of actual query volume — unlike most GCP services in this catalog, which are pay-per-request. This module never defaultsonline_serving_configto 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 viavalidation {}atplantime. The mutual exclusion (fixed_node_countXORscaling) and thescaling.max_node_count > min_node_count <= 10x min_node_countrelationship 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'sautoscaling_targets) unenforced.- No
self_linkattribute exists. Confirmed against the live Attributes Reference — this module's outputs deliberately omit aself_linkrow. nameisOptionalbut notOptional+Computed. Confirmed via the schema-JSON fallback. Because the field carries noComputedflag, this module does not assume any auto-naming behavior on omission the wayterraform-google-spanner-instancedoes for its own optionalname(which is confirmedOptional+Computedwith documented random-name generation) — supplying an explicit name for this resource is recommended in practice.encryption_spec.kms_key_namemust be in the same region as the Featurestore. Confirmed live schema note; not enforced by this module'svalidation {}(cross-referencing an external key ring's region is not something this module's inputs alone can verify) — a mismatch surfaces only atapply.deletion_policy = "PREVENT"is independent offorce_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_policynot"PREVENT").- IAM propagation delay. This module grants no IAM itself, but a composition that grants
roles/aiplatform.useror 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.
| 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 |
cd C:\GitHubCode\newgooglecloudmodules\terraform-google-vertex-ai-featurestore
terraform init -backend=false
terraform validate
terraform fmt -checkPin ?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.
terraform init -backend=false,terraform validate, andterraform fmt -checkare the entire offline proof gate for this module — all three pass cleanly as of this authoring session, against the actually-resolvedhashicorp/googleprovider version 7.39.0.validate/fmtconfirm 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 atapply, against a real project, never atplan. 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 realterraform planagainst 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.
$ 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"
| 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 |
google_vertex_ai_featurestoreprovider referencegoogle_vertex_ai_featurestore_entitytypeprovider reference — out of scope for this module, see Architecture Notes- Sibling modules:
terraform-google-kms-keyring,terraform-google-vertex-ai-dataset,terraform-google-vertex-ai-endpoint - This module's
SCOPE.md
💙 "Infrastructure as Code should be standardized, consistent, and secure."