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:

RemovedReplacement
google_iap_brand, google_iap_client, google_iap_client datasourceManage OAuth brand/client via Cloud Console only β€” no longer Terraform-managed
google_ml_engine_modelgoogle_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_schedulegoogle_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 block
  • google_iam_workforce_pool_provider_scim_tenant.claim_mapping β€” now required at creation
  • google_workflows_workflow.source_contents β€” now required
  • google_secret_manager_secret_version.secret_data_wo_version β€” now required alongside secret_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_details
  • google_compute_service_attachment.nat_subnets
  • google_compute_service_attachment.consumer_reject_lists
  • google_container_cluster.logging_config.enable_components
  • google_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_scheme
  • google_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

  1. 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}
    
  2. Run terraform init -upgrade, then terraform validate before plan. init only installs the new provider; validate is 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.
  3. 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.
  4. Set load_balancing_scheme explicitly on any google_compute_backend_service or google_compute_global_forwarding_rule before running plan, so the default-value change doesn't propose an unwanted scheme migration.
  5. Run terraform plan in 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.
  6. 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.0 can 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_cluster blocks for positional indexing on enable_components before 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_scheme especially) 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 -upgrade in 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-google 8.0 genuinely deletes fields like reservation_block_count and scale_tier β€” the deprecation warnings in 7.x were the grace period; in 8.0 validate and plan reject them outright.
  • Missing the secret_data_wo_version type 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:

  1. 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.
  2. 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:

  1. Confirm the field was never set explicitly in your config β€” that's the default-value change, not drift.
  2. 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:

  1. Search the config for positional indexing (enable_components[0]) β€” that's almost always the set-conversion issue.
  2. Replace it with contains(tolist(...), ...) or a for expression 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

Area7.x8.0
BeyondCorp Enterprisegoogle_beyondcorp_app_connection/connector/gatewayRemoved β€” use google_beyondcorp_security_gateway
Classic Notebooksgoogle_notebooks_instance etc.Removed β€” use google_workbench_instance
enable_components (GKE logging/monitoring)Ordered listUnordered set
LB load_balancing_scheme defaultEXTERNALEXTERNAL_MANAGED
secret_data_wo_versionNumberString, and required
Failure surfaces atMixedMostly terraform validate / plan (fails fast, before apply)

Further Reading