💾 terraform bootstrap for a gcp backend
a light-weight first apply for state and locking
tl;dr
I introduce a light-weight bootstrap setup that solves the paradox caused by GCP projects not existing before the initial apply. No hassle in migrating local state to cloud backends down the road.
the problem
You'll want to store the state of your Terraform apply in a secure, shared backend. This is so that team members don't have to ask you to Slack them .tfstate files at 3am when they want a new production database /s.
However, GCP projects don't exist, buckets aren't set up, and the backend isn't configured. This is a problem because Terraform needs to know the backend configuration before it can apply.
GCP provides a project factory module for this, but its unmaintained since 2025, and the setup script straight up fails. I tried opening a PR for this, but it has closed so many times due to staleness and no replies that I gave up.
I may revisit that at a later date, but for now, I've created a light-weight bootstrap setup that solves the immediate problem:
- I need a project.
- I need billing enabled.
- I need a bucket to store the state in.
- I need to enable the necessary bootstrap services.
the solution
A small set of gcloud CLI commands to create the project, link the billing, and create the bucket.
gcloud projects create $PROJECT --folder=$FOLDER
gcloud billing projects link $PROJECT --billing-account=$BILLING
gcloud storage buckets create gs://tf-state-$APP_NAME \
--project=$BUCKET_PROJECT \
--location=europe-west2 \
--uniform-bucket-level-access
gcloud storage buckets update gs://tf-state-$APP_NAME --versioning
gcloud services enable cloudresourcemanager.googleapis.com serviceusage.googleapis.com --project=$PROJECT
A Terraform backend configuration to store the state in the bucket.
terraform {
required_version = ">= 1.8"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 7.44.0"
}
}
backend "gcs" {
bucket = "tf-state-${var.app_name}"
}
}
tofu init -var-file=<environment>.tfvars and you're good to go!
caveat
This setup creates a bucket per app, the state files will be split by environment. This means you need rock solid IAM on your bucket and state files to ensure only the appropriate team members can access the state files.