A first Terraform apply against a new Google Cloud project, in the order that works

Six steps that remove the four failures that stop a first apply: enable the services, create and version the state bucket, wire the gcs backend and migrate, authenticate without a key, pin the provider, then plan before apply. Each step says what it prevents.

markdownguidev1T9SJ-G1Q2stage: deploytrust: officialfamily: iacservice: terraforminterface: iacterraform applybackend "gcs"terraform init -migrate-stategcloud services enablegcloud storage buckets createprovider pinningterraform plan

Created 21 Oct 2026 · Modified 23 Oct 2026 · 1 version

A first Terraform apply, in order

Step 1 — enable the services first

gcloud services list
gcloud services enable SERVICE

Several at once is supported: gcloud services enable bigquery.googleapis.com pubsub.googleapis.com. You need Service Usage Admin (roles/serviceusage.serviceUsageAdmin).

Prevents: the commonest first-apply failure, which looks like a resource problem and is a project problem (reference/iac/errors.md §1). Put the enablements in the configuration, so project number twelve does not need a runbook.

Step 2 — create the state bucket, and turn versioning on

Create the bucket before the backend block exists. Then:

  • enable object versioning — the vendor's own tutorial does, so that "earlier versions of the state are

preserved in the bucket";

  • lock it down: IAM only, and public access prevention

(reference/storage/cloud-storage-access-control.md).

Prevents: the one unrecoverable failure in a Terraform estate. A truncated state write on an unversioned bucket has nothing to restore from.

Step 3 — wire the backend, then migrate

terraform {
  backend "gcs" {
    bucket = "BUCKET_NAME"
  }
}
terraform init -migrate-state

Prevents: losing the local state you already have. init without -migrate-state leaves it behind.

Step 4 — authenticate without a key

Pick one, in this order of preference:

  1. the attached identity — the runner is a Google Cloud resource (Cloud Build, a GCE instance, GKE with

Workload Identity Federation), so it already has one;

  1. workload identity federation — the runner is outside Google Cloud

(guides/identity/github-actions-without-keys.md);

  1. impersonation from your own credentials, for local work.

Prevents: a key in a repository, a CI secret store, or a developer's home directory. Keys are "a security risk if not managed correctly", and the vendor names all three alternatives above.

Step 5 — pin the provider

The hub's rule, not the vendor's — the Terraform-on-Google-Cloud page makes no version recommendation. Pin an exact provider version in required_providers.

Prevents: a plan that differs between two runs of the same configuration, which is the hardest IaC bug to reason about because nothing in your repository changed.

Step 6 — plan, read the plan, then apply

Read every destroy and replace line before applying. On Google Cloud specifically, watch for:

  • a zonal disk being replaced: it cannot move zone, so a zone change is a destroy-and-create of the data

(reference/overview/regions-zones-and-locations.md §3);

  • anything in Cloud KMS: key rings cannot be deleted, deleted key names cannot be reused, and protection

level, purpose and type are immutable, so a "change" is a new key (reference/identity/secret-manager-and-kms.md §2);

  • a project being destroyed. Nothing in this hub's register justifies that in a first apply.

If Infra Manager is doing the applying, step 6 is its preview object, and steps 2 and 3 are unnecessary because it keeps the state itself (reference/iac/terraform-on-google-cloud.md §3).

Before you write a module

Check Blueprints — Google publishes "deployable, reusable Terraform modules" — for the thing you are about to model. A landing zone, a network or a GKE cluster is almost certainly already there, and reading one is also the fastest way to learn the idioms this guide assumes.

Read this document as markdown — the same bytes an agent receives.