Terraform Google Provider 8.0: Breaking Changes and Upgrade Guide
Terraform Google Provider 8.0: Breaking Changes and Upgrade Guide
HashiCorp announced the Terraform provider for Google Cloud 8.0 as generally available on September 22, 2026. If you've got GCP infrastructure sitting on an unpinned >= 7.0 constraint, the next terraform init -upgrade (or even a routine CI run, if you don't pin) can pull in 8.0 and start failing on resources that worked fine yesterday. This walks through what actually got removed or changed, and how to move a real config onto it without an apply going sideways.
Why this version bump hurts more than most
Most Google provider minor releases add resources and leave everything else alone. 8.0 is a genuine cleanup release: it deletes several resource types that had been deprecated for multiple 7.x cycles, converts a handful of list-typed fields to sets (which changes how indexing and for_each behave against them), and tightens a few fields from optional to required. None of this is subtle β once the 8.0 provider is installed, terraform validate (and plan, which validates first) rejects any config that still references a removed resource type with an Invalid resource type error, so you find out before anything touches infrastructure, not mid-apply. That's better than a silent behavior change, but only if you actually read the error instead of reflexively rolling the provider back.
Breaking change 1: BeyondCorp Enterprise resources removed
google_beyondcorp_app_connection, google_beyondcorp_app_connector, and google_beyondcorp_app_gateway (plus their datasource equivalents) are gone. They're replaced by the newer Security Gateway model:
1# Removed in 8.0 β fails at terraform validate / plan
2resource "google_beyondcorp_app_connection" "example" {
3 name = "my-connection"
4 # ...
5}
6
7# Replacement
8resource "google_beyondcorp_security_gateway" "example" {
9 security_gateway_id = "my-gateway"
10 # ...
11}
12
13resource "google_beyondcorp_security_gateway_application" "example" {
14 security_gateway_id = google_beyondcorp_security_gateway.example.security_gateway_id
15 application_id = "my-app"
16 # ...
17}
This isn't a drop-in rename β Security Gateway is a different resource model (gateway + application, instead of connection + connector + gateway), so plan on rewriting this block rather than search-and-replacing the resource type.
Breaking change 2: IAP brand/client and ML Engine and Notebooks resources removed
Three more removals that are easy to miss if they're buried in a shared module:
| Removed | Replacement |
|---|---|
google_iap_brand, google_iap_client, google_iap_client datasource | Manage OAuth brand/client via Cloud Console only β no longer Terraform-managed |
google_ml_engine_model | google_vertex_ai_endpoint or a Vertex AI Model Garden resource |
google_notebooks_environment, google_notebooks_instance, google_notebooks_runtime (and their IAM resources) | google_workbench_instance |
google_vertex_ai_schedule | google_colab_schedule |
The Notebooks β Workbench migration is the one most teams actually hit, since classic Notebooks instances were a common way to stand up a quick JupyterLab box on GCP. google_workbench_instance has a different schema (machine config, VPC network config, and disk config are all nested differently), so treat it as a new resource to write, not a rename.
Breaking change 3: fields removed, renamed, or made required
1# google_data_loss_prevention_job_trigger β renamed field
2actions {
3 publish_findings_to_dataplex_catalog {} # was: publish_findings_to_cloud_data_catalog
4}
5
6# google_netapp_storage_pool β renamed field
7resource "google_netapp_storage_pool" "example" {
8 scale_type = "ADJUSTABLE" # was: scale_tier
9}
10
11# google_compute_reservation β moved into a computed block
12# was: reservation_block_count = 5 (top-level, settable)
13# now: read via resource_status[0].reservation_block_count (computed only)
A few fields also switched from optional to required, which surfaces as a plan-time validation error rather than a silent default:
google_cloud_run_v2_worker_pool.http_get.http_headers.nameβ now required on every header blockgoogle_iam_workforce_pool_provider_scim_tenant.claim_mappingβ now required at creationgoogle_workflows_workflow.source_contentsβ now requiredgoogle_secret_manager_secret_version.secret_data_wo_versionβ now required alongsidesecret_data_wo, and its type changed from number to string (quote the value)
That last one bites people specifically because it's a silent type change, not a missing-field error β secret_data_wo_version = 1 needs to become secret_data_wo_version = "1", and the same string conversion applies to google_bigquery_data_transfer_config.sensitive_params.0.secret_access_key_wo_version.
Breaking change 4: list fields became sets
These fields changed from ordered lists to unordered sets, which breaks any config that indexes them by position (field[0]) or relies on count.index-style access:
google_cloud_security_compliance_framework.cloud_control_detailsgoogle_compute_service_attachment.nat_subnetsgoogle_compute_service_attachment.consumer_reject_listsgoogle_container_cluster.logging_config.enable_componentsgoogle_container_cluster.monitoring_config.enable_components
The google_container_cluster ones are the most likely to hit a real GKE config, since logging_config/monitoring_config enable_components lists are common in production cluster definitions. If your config does something like monitoring_config[0].enable_components[0], switch to contains() or tolist() instead of positional indexing:
1# Fragile after 8.0 β set has no guaranteed order
2enable_components[0] == "SYSTEM_COMPONENTS"
3
4# Works regardless of set ordering
5contains(tolist(enable_components), "SYSTEM_COMPONENTS")
Breaking change 5: default values changed
Two load balancer defaults flipped from EXTERNAL to EXTERNAL_MANAGED:
google_compute_backend_service.load_balancing_schemegoogle_compute_global_forwarding_rule.load_balancing_scheme
If your config never set load_balancing_scheme explicitly and relied on the implicit default, terraform plan after upgrading will show a diff trying to change the scheme on existing infrastructure β which for a forwarding rule usually means a destroy/recreate, not an in-place update. Set the value explicitly to whatever your infrastructure is actually running as before you touch anything else, so the plan comes back clean instead of proposing a scheme migration you didn't ask for.
google_bigquery_dataset.default_collation also stopped being auto-computed, so a dataset that relied on GCP inferring a collation will now show a diff on first plan too.
Step-by-step upgrade process
- Pin the version explicitly before upgrading anything else:
1terraform { 2 required_providers { 3 google = { 4 source = "hashicorp/google" 5 version = "~> 8.0" 6 } 7 } 8} - Run
terraform init -upgrade, thenterraform validatebeforeplan.initonly installs the new provider;validateis what fails immediately on any resource type the 8.0 schema no longer recognizes β that's your removed-resource checklist, generated for free instead of guessed from the table above. - Grep your modules (including vendored/third-party ones) for the renamed fields:
scale_tier,publish_findings_to_cloud_data_catalog,reservation_block_count,secret_data_wo_version. - Set
load_balancing_schemeexplicitly on anygoogle_compute_backend_serviceorgoogle_compute_global_forwarding_rulebefore running plan, so the default-value change doesn't propose an unwanted scheme migration. - Run
terraform planin a non-prod project first and read every line β a forwarding-rule scheme diff or a set-vs-list reordering can look alarming but be a no-op; a Notebooks or BeyondCorp resource that silently vanished from state would not. - Roll out per-module, not repo-wide, if this is a monorepo β a single removed resource in a shared module blocks every root config that consumes it.
Best practices
- Pin both the provider and any community modules β an unpinned module pulling
>= 7.0can silently start emitting 8.0-only syntax underneath a config you didn't touch. - Treat Notebooks β Workbench and BeyondCorp App Connector β Security Gateway as rewrites, not renames β writing the new resource from the schema is faster and safer than trying to reuse the old block's shape.
- Audit
google_container_clusterblocks for positional indexing onenable_componentsbefore upgrading GKE-managing configs specifically β that's the change most likely to hide in otherwise-untouched production code. - Set explicit values for anything that changed defaults (
load_balancing_schemeespecially) rather than letting a diff apply silently β a load balancer scheme change can mean a destroy/recreate on live traffic.
Common mistakes to avoid
- Running
terraform init -upgradein prod because a dev environment looked clean. Set defaults and set-vs-list changes can differ by resource configuration even within the same module. - Assuming a removed field is just deprecated.
terraform-provider-google8.0 genuinely deletes fields likereservation_block_countandscale_tierβ the deprecation warnings in 7.x were the grace period; in 8.0validateandplanreject them outright. - Missing the
secret_data_wo_versiontype change because it's a quiet numberβstring conversion, not an error message pointing at a removed resource. - Indexing a converted set field by position and getting a confusing "index out of range" or silently-wrong value instead of a clear breaking-change error, because Terraform doesn't know your old config assumed ordering.
Troubleshooting
If terraform validate or plan fails with an Invalid resource type error after upgrading:
- Check it against the removed-resources list above β BeyondCorp App Connection/Connector/Gateway, IAP brand/client, ML Engine model, and classic Notebooks are the most common hits.
- If it's a resource pulled in from a shared or registry module, pin that module to a version published before the provider's own 8.0 GA, or update the module to the new resource.
If terraform plan shows an unexpected diff on load_balancing_scheme or default_collation:
- Confirm the field was never set explicitly in your config β that's the default-value change, not drift.
- Set it explicitly to match current live infrastructure before applying anything else, so this plan doesn't get bundled with unrelated changes.
If a google_container_cluster apply behaves differently than expected on enable_components:
- Search the config for positional indexing (
enable_components[0]) β that's almost always the set-conversion issue. - Replace it with
contains(tolist(...), ...)or aforexpression that doesn't assume order.
FAQ
Do I need to migrate off Notebooks resources before upgrading the provider?
Yes, functionally β 8.0 removes google_notebooks_instance/environment/runtime outright, so terraform validate/plan will reject a config that still references them. Migrate to google_workbench_instance first, or stay pinned to ~> 7.0 until you do.
Will this affect OpenTofu configs the same way?
Yes β hashicorp/google is a standard Terraform Registry provider consumed the same way by OpenTofu, so a config pulling google 8.x into OpenTofu hits identical breaking changes.
Is the load_balancing_scheme default change going to destroy my load balancer?
Only if you apply the resulting plan without setting the value explicitly first. Read the plan output β if it shows EXTERNAL β EXTERNAL_MANAGED as a forced replacement, set the field to your current actual value before applying anything.
What's the fastest way to find every removed resource in a large codebase?
Run terraform init -upgrade && terraform validate against each root module β validate fails fast on unsupported resource types, which is a more reliable check than grepping for resource names across dozens of .tf files by hand.
Can I stay on 7.x for now?
Yes. Pin version = "~> 7.0" in required_providers and stay there deliberately β just make sure nothing else in the repo has a looser >= 7.0 constraint that would override it.
Key takeaways
| Area | 7.x | 8.0 |
|---|---|---|
| BeyondCorp Enterprise | google_beyondcorp_app_connection/connector/gateway | Removed β use google_beyondcorp_security_gateway |
| Classic Notebooks | google_notebooks_instance etc. | Removed β use google_workbench_instance |
enable_components (GKE logging/monitoring) | Ordered list | Unordered set |
LB load_balancing_scheme default | EXTERNAL | EXTERNAL_MANAGED |
secret_data_wo_version | Number | String, and required |
| Failure surfaces at | Mixed | Mostly terraform validate / plan (fails fast, before apply) |