TL;DR
When security_posture_mode is not provided, the module generates an empty
security_posture_config {} block. The terraform plan succeeds but the apply
fails with:
Error: googleapi: Error 400: Must specify a field to update
This is not actionable from the error message alone. Setting a default value
(e.g. "DISABLED" or "BASIC") on the security_posture_mode variable would
either prevent the empty block generation or make the behavior explicit.
Steps to reproduce:
- Call the module without setting security_posture_mode
- terraform plan → no error
- terraform apply → Error 400
Expected behavior
Either a plan-time validation error or a sensible default
Observed behavior
Silent plan + cryptic apply error causing cluster disruption
Terraform Configuration
module "gke" {
source = "terraform-google-modules/kubernetes-engine/google//modules/private-cluster"
version = "~> 44.0"
project_id = var.project_id
name = var.name
region = var.region
regional = var.regional
zones = var.zones
network = var.network
subnetwork = var.subnetwork
ip_range_pods = var.ip_range_pods
ip_range_services = var.ip_range_services
master_ipv4_cidr_block = var.master_ipv4_cidr_block
security_posture_mode = var.security_posture_mode
kubernetes_version = var.kubernetes_version
cluster_resource_labels = local.labels
master_authorized_networks = var.master_authorized_networks
deletion_protection = var.deletion_protection
http_load_balancing = var.http_load_balancing
horizontal_pod_autoscaling = var.horizontal_pod_autoscaling
service_account = var.service_account
node_metadata = var.node_metadata
node_pools_labels = local.node_pools_labels
node_pools_oauth_scopes = var.node_pools_oauth_scopes
timeouts = {
create = lookup(var.timeouts, "create", "45m")
update = lookup(var.timeouts, "update", "100m")
delete = lookup(var.timeouts, "delete", "45m")
}
create_service_account = false
enable_private_nodes = true
network_policy = true
master_global_access_enabled = false
enable_vertical_pod_autoscaling = true
enable_intranode_visibility = true
security_posture_vulnerability_mode = "VULNERABILITY_MODE_UNSPECIFIED"
release_channel = "UNSPECIFIED"
maintenance_start_time = "2020-04-18T00:00:00Z"
maintenance_end_time = "2020-04-18T04:00:00Z"
maintenance_recurrence = "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA,SU"
node_pools = [
for np in values(var.node_pools) : merge(
np,
{
pod_range = var.ip_range_pods
}
)
]
node_pools_metadata = {
default_values = {
cluster_name = false
node_pool = false
}
}
}
dev.tfvars :
clusters = {
"dev-gke-cluster" = {
region = "europe-west3"
zones = ["europe-west3-a"]
regional = false
network = "dev"
subnetwork = "dev"
ip_range_pods = "dev-pods"
ip_range_services = "dev-services"
kubernetes_version = "1.33.8-gke.1026000"
deletion_protection = true
node_metadata = "GCE_METADATA"
security_posture_mode = "DISABLED"
node_pools = {
"spot-migrated-pool" = {
name = "spot-migrated-pool"
machine_type = "n1-standard-4"
node_locations = "europe-west3-a"
disk_size_gb = 50
disk_type = "pd-balanced"
spot = true
location_policy = "BALANCED"
max_count = 20
min_count = 4
auto_upgrade = false
enable_private_nodes = true
cpu_cfs_quota = false
insecure_kubelet_readonly_port_enabled = "TRUE"
max_parallel_image_pulls = 3
cpu_manager_policy = ""
}
}
node_pools_oauth_scopes = {
all = [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/userinfo.email"
]
}
}
}
terraform plan :
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
~ update in-place
Terraform will perform the following actions:
# module.gke["dev-gke-cluster"].module.gke.google_container_cluster.primary will be updated in-place
~ resource "google_container_cluster" "primary" {
+ min_master_version = "1.33.8-gke.1026000"
name = "dev-gke-cluster"
+ remove_default_node_pool = false
...
+ security_posture_config {}
# (26 unchanged blocks hidden)
}
Terraform Version
Terraform Provider Versions
- Installed hashicorp/google v7.30.0 (signed by HashiCorp)
- Installed hashicorp/random v3.8.1 (signed by HashiCorp)
- Installed hashicorp/kubernetes v3.1.0 (signed by HashiCorp)
Additional information
No response
TL;DR
When security_posture_mode is not provided, the module generates an empty
security_posture_config {} block. The terraform plan succeeds but the apply
fails with:
Error: googleapi: Error 400: Must specify a field to update
This is not actionable from the error message alone. Setting a default value
(e.g. "DISABLED" or "BASIC") on the security_posture_mode variable would
either prevent the empty block generation or make the behavior explicit.
Steps to reproduce:
Expected behavior
Either a plan-time validation error or a sensible default
Observed behavior
Silent plan + cryptic apply error causing cluster disruption
Terraform Configuration
Terraform Version
Terraform Provider Versions
Additional information
No response