Infrastructure as Code

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

See the section bibliography for the reference material consulted while preparing these pages.

Examples on this page target the gcloud CLI 586, Terraform 1.x with the hashicorp/google provider v8 (8.0.0 was released on 2026-08-26 and v7.x still receives patches), and Infrastructure Manager, all checked on 2026-10-01. Re-verify with gcloud --version and terraform version before relying on exact behaviour. The Bookshelf scenario used here (a static front end, catalog-api, orders-api and the notifier worker) is described in the section introduction; my-project-id and us-central1 are placeholders.

Declarative vs. imperative management

Google Cloud resources can be managed imperatively, by running commands such as gcloud artifacts repositories create, or declaratively, by describing the desired end state and letting a tool work out what to create, change or delete. A script only performs the actions it was written for, so running it again against a changed environment fails or does the wrong thing. A declarative definition can be applied repeatedly: the engine compares it with recorded state, which makes previews, review in pull requests and drift detection possible.

Tool What you write What runs

Terraform (OpenTofu)

HCL with the hashicorp/google provider

Terraform on your machine or in CI, with state kept in a file or a Cloud Storage bucket

Infrastructure Manager

The same Terraform configuration

Terraform runs managed by Google Cloud, with state kept for you

Config Connector

Kubernetes YAML resources

A controller in a GKE cluster that reconciles the resources continuously

Deployment Manager

YAML, Jinja or Python templates

Google-hosted engine, being shut down (see Deployment Manager shutdown and DM Convert)

Older guides present Deployment Manager as the native Google Cloud IaC service. It is no longer supported, and the current approach is Terraform, either run by yourself or managed by Infrastructure Manager.

Terraform with the Google provider

Terraform is the most widely used IaC tool for Google Cloud, and the one Google’s own blueprints and tooling target. This page gives only what is needed to follow the rest of the section; in-depth Terraform coverage (modules, workspaces, testing, pipelines) is tracked separately in GitHub issue #171 of this documentation repository and is not repeated here.

Provider configuration and authentication

The hashicorp/google provider (and its sibling google-beta, which exposes Preview features) is configured with a default project and region. It authenticates with Application Default Credentials, so no service-account key file is needed: locally use gcloud auth application-default login or impersonate a service account, and in CI use Workload Identity Federation or an attached service account, see Identity and Access.

gcloud auth application-default login

# or, safer for shared environments: impersonate a deployer service account
export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT="terraform-deployer@my-project-id.iam.gserviceaccount.com"

See the provider reference for the supported authentication options.

Remote state in Cloud Storage

Terraform records what it manages in a state file. Keep it in a versioned Cloud Storage bucket so a team shares one copy and the gcs backend can lock it during a run. The bucket is created once, outside the configuration it stores state for.

PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
STATE_BUCKET="bookshelf-tfstate-$PROJECT_ID"

gcloud storage buckets create "gs://$STATE_BUCKET" \
  --project="$PROJECT_ID" \
  --location="$REGION" \
  --uniform-bucket-level-access

gcloud storage buckets update "gs://$STATE_BUCKET" --versioning

Terraform workflow

The cycle is terraform init (download providers, connect the backend), terraform plan (preview), terraform apply and terraform destroy. State can contain sensitive values, so restrict access to the state bucket with IAM.

In the Google Cloud console

The console does not run Terraform itself, but it helps in two ways:

  1. Open any resource’s page (for example Cloud Run, then a service), choose the Equivalent code or Equivalent command line option offered by many create forms, and read the generated gcloud or Terraform for what you configured.

  2. Use Cloud Shell (the terminal icon at the top right), which has Terraform and the gcloud CLI preinstalled and already authenticated, to run the commands below without installing anything.

With the Terraform CLI: the Bookshelf baseline

The configuration below creates the shared Bookshelf baseline: an Artifact Registry Docker repository bookshelf, a runtime service account for catalog-api, permission for that account to read the repository, and a Cloud Run service that starts from Google’s public hello image until the real image is built. Enable the APIs first:

gcloud services enable run.googleapis.com artifactregistry.googleapis.com iam.googleapis.com

Save the following as main.tf:

terraform {
  required_version = ">= 1.9"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 8.0"
    }
  }

  backend "gcs" {
    bucket = "bookshelf-tfstate-my-project-id" # the state bucket created above
    prefix = "bookshelf/baseline"
  }
}

variable "project_id" {
  type = string
}

variable "region" {
  type    = string
  default = "us-central1"
}

provider "google" {
  project = var.project_id
  region  = var.region
}

resource "google_artifact_registry_repository" "bookshelf" {
  repository_id = "bookshelf"
  format        = "DOCKER"
  location      = var.region
  description   = "Bookshelf container images"
}

resource "google_service_account" "catalog_api" {
  account_id   = "catalog-api"
  display_name = "Bookshelf catalog-api runtime"
}

resource "google_artifact_registry_repository_iam_member" "catalog_api_reader" {
  repository = google_artifact_registry_repository.bookshelf.name
  location   = var.region
  role       = "roles/artifactregistry.reader"
  member     = "serviceAccount:${google_service_account.catalog_api.email}"
}

resource "google_cloud_run_v2_service" "catalog_api" {
  name                = "catalog-api"
  location            = var.region
  deletion_protection = false # allow terraform destroy in this sample

  template {
    service_account = google_service_account.catalog_api.email

    containers {
      # replace with ${var.region}-docker.pkg.dev/${var.project_id}/bookshelf/catalog-api:TAG
      image = "us-docker.pkg.dev/cloudrun/container/hello"
    }
  }
}

output "catalog_api_url" {
  value = google_cloud_run_v2_service.catalog_api.uri
}

Then run:

terraform init
terraform plan -var="project_id=$PROJECT_ID" -out=tfplan
terraform apply tfplan

See google_artifact_registry_repository, google_cloud_run_v2_service and the version 8 upgrade guide. The service is not public: calling it needs an identity token, see Cloud Run.

A reusable Bookshelf module

When more than one environment needs the baseline, wrap the resources above in a module directory (modules/bookshelf-baseline/ with main.tf, variables.tf and outputs.tf) and call it once per environment with a different project_id, region and name prefix. Prefer one project per environment, see Cloud Concepts. Google also publishes ready-made modules; see Blueprints and Cloud Foundation Fabric.

Infrastructure Manager

Infrastructure Manager (Infra Manager) is a Google Cloud service that runs Terraform for you. You submit a Terraform configuration from a local directory, a Cloud Storage bucket or a Git repository; the service executes it under a service account you choose, stores the state, and records every revision of the deployment. It is the supported replacement for Deployment Manager. A preview shows the plan without changing anything, and a deployment can be locked, updated or deleted as a unit. Check the pricing page for the current model, since it can change.

Roles and service account

Two identities are involved. The person (or pipeline) that runs gcloud infra-manager needs roles/config.admin. The deployment runs as a service account that needs roles/config.agent plus whatever roles the resources require (here Artifact Registry admin, Cloud Run admin, service account admin and service account user, or a narrower custom role).

In the Google Cloud console

  1. Select the project in the project picker and enable the Infrastructure Manager API.

  2. Open Infrastructure Manager from the navigation menu (search for it in the search bar if it is not pinned).

  3. Choose Create deployment, enter a name (bookshelf-baseline) and a region.

  4. Choose a Terraform source: Git repository or Cloud Storage bucket (local directories are available from the CLI).

  5. Pick the service account the deployment runs as, enter input values such as project_id, and choose Create.

  6. Use Preview before applying changes, then open the deployment to see its resources, revisions and logs.

With the gcloud CLI

PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
SA=infra-manager-deployer

gcloud services enable config.googleapis.com

gcloud iam service-accounts create "$SA" --display-name="Infrastructure Manager deployer"

gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member="serviceAccount:$SA@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/config.agent"
# add the roles the Terraform resources need (Artifact Registry, Cloud Run, IAM)

gcloud infra-manager previews create bookshelf-baseline-preview \
  --location="$REGION" \
  --service-account="projects/$PROJECT_ID/serviceAccounts/$SA@$PROJECT_ID.iam.gserviceaccount.com" \
  --local-source=./bookshelf-baseline \
  --input-values="project_id=$PROJECT_ID"

gcloud infra-manager deployments apply bookshelf-baseline \
  --location="$REGION" \
  --service-account="projects/$PROJECT_ID/serviceAccounts/$SA@$PROJECT_ID.iam.gserviceaccount.com" \
  --local-source=./bookshelf-baseline \
  --input-values="project_id=$PROJECT_ID"

gcloud infra-manager deployments describe bookshelf-baseline --location="$REGION"
gcloud infra-manager resources list --deployment=bookshelf-baseline --location="$REGION"

See the Infrastructure Manager documentation and the gcloud infra-manager reference for the exact flags in your CLI version. Use --git-source-repo, --git-source-directory and --git-source-ref instead of --local-source to deploy from Git. The configuration given to Infrastructure Manager manages its own state, so remove the backend "gcs" block from main.tf for this route.

Deployment Manager shutdown and DM Convert

Deployment Manager is the older Google Cloud IaC service (YAML configurations with Jinja or Python templates). Its lifecycle, from the official deprecation notice:

Date Milestone

2026-04-01

End of support: no new features or non-critical fixes, and support tickets are only accepted for migration blockers

2026-06-30

New customers can no longer start using the service

2027-03-31

End of extended support for migration blockers

2027-06-30

Shutdown of the service

Existing deployments keep running until the shutdown, but should be migrated now. Google’s DM Convert tool automates most of the work in three steps: convert the YAML, Jinja or Python configuration to Terraform HCL, import the existing resources into an Infrastructure Manager deployment so they are not recreated, and delete the old Deployment Manager deployment while abandoning (not deleting) its resources. Some Deployment Manager concepts have no direct Terraform equivalent, so expect to review and edit the generated HCL, and run a preview before applying.

The Runtime Configurator API, which stores configuration variables and watchers for Deployment Manager, is part of the same product and goes away with it. Do not adopt it for new work; use Parameter Manager and Secret Manager instead, see Runtime Configuration.

Config Connector

Config Connector lets you manage Google Cloud resources as Kubernetes custom resources. A controller in a GKE cluster reconciles each resource (StorageBucket, PubSubTopic, ArtifactRegistryRepository, …​) against Google Cloud continuously, so manual drift is corrected automatically. It suits teams that already operate everything through kubectl and GitOps; for everything else Terraform is simpler. It is available as a GKE add-on.

CLUSTER=bookshelf-gke
REGION=us-central1

gcloud container clusters update "$CLUSTER" \
  --location="$REGION" \
  --update-addons=ConfigConnector=ENABLED

kubectl annotate namespace default cnrm.cloud.google.com/project-id="$PROJECT_ID"

See Install Config Connector. The controller needs a Google service account bound with Workload Identity Federation for GKE and the roles for the resources it manages. A resource then looks like this:

apiVersion: artifactregistry.cnrm.cloud.google.com/v1beta1
kind: ArtifactRegistryRepository
metadata:
  name: bookshelf
spec:
  format: DOCKER
  location: us-central1

Apply it with kubectl apply -f repository.yaml. See the resource reference for its fields.

Blueprints and Cloud Foundation Fabric

Rather than writing everything from scratch, start from vetted modules. The Google Cloud Terraform blueprints and modules cover networks, projects, GKE and more, and the Enterprise foundations blueprint sets up an organization-level landing zone (folders, org policies, shared VPC, logging) with Terraform. Cloud Foundation Fabric is an open-source collection of Terraform modules and end-to-end examples maintained by Google Cloud engineers. Pin module versions, and review what they create before applying them, because they can create billable resources and organization-wide policy. See Terraform blueprints and modules for Google Cloud.

Bootstrapping Terraform from existing resources

For resources that were created by hand, export their configuration rather than rewriting it. gcloud can print many resources as YAML that mirrors the API representation, and the resource-config command exports Terraform HCL:

gcloud services enable cloudasset.googleapis.com
gcloud components install config-connector

gcloud run services describe catalog-api --region="$REGION" --format=export > catalog-api.yaml

gcloud beta resource-config bulk-export \
  --project="$PROJECT_ID" \
  --resource-format=terraform \
  --resource-types=ArtifactRegistryRepository,StorageBucket \
  --path=./exported

See Export your Google Cloud resources into Terraform format. Export does not support every resource type, is not available on Windows, and the output is a starting point: review it, remove defaults, then import the resources into state with terraform import or import blocks.

Pricing, quotas and launch stages

Terraform and the provider are free; you pay for the resources they create and for the Cloud Storage bucket that holds state. Infrastructure Manager’s charges and the free allowance are listed on the Infrastructure Manager pricing page, which should be read on the day you plan costs. Config Connector as a GKE add-on is billed through the cluster it runs on. Infrastructure Manager is generally available; its limits (deployments per region, concurrent runs) are in the quotas page, and Terraform runs time out when a single apply takes too long, so split large configurations.

Clean up

Remove resources in the reverse of the order they were created:

# Terraform baseline
terraform destroy -var="project_id=$PROJECT_ID"

# Infrastructure Manager deployment and preview
gcloud infra-manager deployments delete bookshelf-baseline --location="$REGION"
gcloud infra-manager previews delete bookshelf-baseline-preview --location="$REGION"

# Config Connector add-on and resource
kubectl delete -f repository.yaml
gcloud container clusters update "$CLUSTER" --location="$REGION" --update-addons=ConfigConnector=DISABLED

# Deployer service account and state bucket (delete state last, only when nothing uses it)
gcloud projects remove-iam-policy-binding "$PROJECT_ID" \
  --member="serviceAccount:infra-manager-deployer@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/config.agent"
gcloud iam service-accounts delete "infra-manager-deployer@$PROJECT_ID.iam.gserviceaccount.com"
gcloud storage rm --recursive "gs://bookshelf-tfstate-$PROJECT_ID"

Delete the state bucket’s versions too (gcloud storage rm --recursive removes all versions of a versioned bucket’s objects and the bucket). Deleting a throwaway project removes everything at once, after a 30-day recovery window. See Shutting down projects.