TerraformでCloud Run + Firestore/Cloud SQL環境をコード化する実践ガイド
~実際に検証してみよう!~



はじめに

皆さん、Google Cloudでサービスの構築や利用を開始する時、どのように行っていますか?私はこれまで、コンソール画面から1つずつ設定していました。しかし、開発・検証・本番と環境が増えたり、チームメンバーが増えたりすると、次のような問題が必ず出てきます。

  • 「本番と検証環境で設定が微妙に違う」がいつの間にか発生する
  • 誰がいつどの設定を変えたのか分からない
  • 環境を作り直すたびに手順書を見ながら時間をかけて設定する

実は、これらの課題を解決する手段として、「Terraformで構成をコード化する」方法が存在します!この記事では、実際に検証した経験を元に、以下の内容について説明します。

  • Terraformとはどのようなツールか
  • 実際にTerraformコードで動かすにはどうすれば良いのか

※仕様確認:本記事の技術内容は2026年9月時点のGoogle Cloud/Terraform公式情報を確認しています。検証に使用したTerraform/Providerのバージョンは「実際の検証手順と結果」に記載しています。

前提条件

以下の前提条件の元、検証しています。

  • GCPプロジェクトが作成済みで、課金が有効になっていること
  • 以下のツールがローカルにインストールされていること
  • gcloud auth application-default login でTerraformからGoogle CloudへアクセスするためのApplication Default Credentials(ADC)を用意していること

ディレクトリ構成

環境が増えても破綻しないように、最初から次の構成にしておきます。

.
├── environments
│   ├── dev.tfvars
│   └── prod.tfvars
├── backend.tf
├── main.tf
└── variables.tf

※個人検証ではローカルstateを利用できますが、チーム開発では複数人がTerraformを実行するため、GCSバックエンドなどのリモートstateを利用し、アクセス制御やバージョニングを行うことをおすすめします。本記事では、state保存用のGCSバケットは事前に作成する前提とします。現在、Cloud StorageのCLI操作にはgcloud storageの利用が推奨されています。なお、state保存用バケットを別のTerraform構成(bootstrap用)で管理する方法もあります。

Terraformとは

そもそもTerraformとは何か、皆さんご存知でしょうか?
一言で言うと、「クラウドのサーバーやネットワークなどの構成をテキスト(コード)で定義し、コマンドで作成・変更・管理できるツール」です。
具体的には、HashiCorp社が開発・提供する、クラウドや各種ITインフラの構成をコードとして定義・管理するIaC(Infrastructure as Code)ツールです。
AWS、Google Cloud、Microsoft Azureなどの主要クラウドサービスをはじめ、GitHubやDatadogといったSaaS製品まで、さまざまなリソースを同じTerraformのワークフローで管理できます。

Terraformでできる基本的な操作は以下の通りです。

  • Init:プロバイダーのプラグインやモジュールをダウンロードしてワークスペースを初期化
  • Plan:定義コードと現在のインフラ状態を比較し、変更予定の差分を表示
  • Apply:計画された内容を実行し、クラウド上にリソースを自動プロビジョニング
  • Destroy:検証環境の撤去時など、Terraformで管理しているリソースの削除を実行

Terraformを利用するメリットは、以下が挙げられます。

  • 手作業によるミスを減らせる:同じコードから構築することで、クリックミスや設定漏れを減らしやすくなる。
  • 同じ構成を再現しやすい:変数を切り替えることで、開発・検証・本番などの環境差分を管理しながら構成を再現しやすくなる。
  • 変更履歴を追跡しやすい:TerraformコードをGitなどで管理することで、「誰が・いつ・何を変更したか」を追跡しやすくなる。なお、Git上のコードを戻すだけでデータやリソースの状態まで必ず元に戻るわけではないため、変更時はterraform planで差分を確認します。

皆さん、Terraformとは何か分かりましたでしょうか?
次の項目からは、私が実際に試した、Terraformを使ってGoogle Cloudに環境を構築する方法を説明していきます。Terraformのファイルごとに見ていきましょう!

Step 1:backend.tf - 基盤設定

本記事では、Terraform本体やGoogle Cloud Providerのバージョン要件、stateの保存先、Provider設定をbackend.tfにまとめます。Terraformでは同じディレクトリ内の.tfファイルをまとめて1つの構成として評価するため、backend.tfというファイル名自体に特別な意味があるわけではありません。

terraform {
  required_version = ">= 1.7.0"  # Terraformのバージョン指定
  required_providers {  # 使うプロバイダー(GCP、AWS等)
    google = {
      source = "hashicorp/google"
      version = "~> 5.30"
	      }
  }
# tfstateの保存先
  # 検証用にローカルバックエンドを使用(本番ではGCSを使用)
  # backend "gcs"{  
  #   bucket = "YOUR_PROJECT_ID-terraform-state"
  #   prefix = "app"
  # }
}
# GCPへの接続設定
provider "google" {
  project = var.project_id
  region = var.region
}

本記事では、次の内容をこのファイルにまとめています。

  • Terraformのバージョン
  • プロバイダー(Google Cloud、AWS等)の指定
  • tfstateの保存場所(GCS、S3等)
  • プロバイダーへの接続設定

Step 2:variables.tf - 変数の定義

ここでは、コード内で使う変数の「型」「説明」「デフォルト値」などを定義します。
「テンプレートの入力項目を定義する」イメージで、環境ごとに変わる値は別ファイル(tfvars)などから渡します。

<検証で使用した中身>

variable "project_id" {
  type        = string # 文字列型
  description = "GCP Project ID" # 説明
}
variable "region" {
  type        = string
  default     = "asia-northeast1"  # デフォルト値(省略可能)
  description = "GCP region for resources"
}
variable "environment" {
  type        = string
  default     = "dev"
  description = "Environment name (dev/prod)"
}
variable "container_image" {
  type        = string
  description = "Container image for Cloud Run"
}
variable "db_password" {
  type        = string
  sensitive   = true
  description = "Database password (store in Secret Manager)"
}

ここの内容に含めること

  • 変数名
  • データ型(string、number、bool等)
  • 説明文
  • デフォルト値(あれば)

Step 3:main.tf - リソース定義

本記事では、実際に作成するGoogle Cloudリソース(VPC、Cloud Run等)をmain.tfにまとめます。
言うなれば「設計図の本体」のようなもので、「このVPCを作って、このCloud Runを作って...」という実際の構成を定義します。なお、main.tfというファイル名自体に特別な意味があるわけではありません。

resource "google_compute_network" "vpc" {
  name = "app-vpc"               # VPCネットワークを作る
}
resource "google_cloud_run_v2_service" "app" {
  name     = "app"               # Cloud Runサービスを作る
  location = var.region          # 変数を参照
  template {
    containers {
      image = var.container_image
    }
  }
}

ここの内容に含めること

  • 作成したいリソース(VPC、DB、サービス等)
  • リソース間の依存関係(depends_on)
  • 設定値(変数参照も含む)

main.tfの中身は長いため、項目を分けて、検証結果と共に説明します。

Step 3-1:必要なAPIの有効化

Cloud Run・Firestore・Cloud SQLを使うには、まず事前にAPIを有効化する必要があります。
tosetのリスト型引数の中に、有効化したいAPIを記述します。

resource "google_project_service" "project" {
  for_each = toset([
    "run.googleapis.com",
    "vpcaccess.googleapis.com",
    "servicenetworking.googleapis.com",
    "sqladmin.googleapis.com",
    "firestore.googleapis.com",
    "secretmanager.googleapis.com"
  ])
  project = var.project_id
  service = each.key
  disable_on_destroy = false
}

Tip:disable_on_destroy = falseにしているのは、terraform destroyした際にAPI自体を無効化してしまうと、他のリソースが依存している場合に思わぬ影響が出るためです。

検証結果:実際に6つのAPIが正しく有効化されました。ただし、本検証プロジェクトではCompute Engine APIやIAM APIなど、一部の基盤APIが既に有効になっていました。新規プロジェクトから再現する場合は、compute.googleapis.comやiam.googleapis.comを含め、構成で利用するAPIが有効か事前に確認してください。また、API有効化直後は反映まで時間がかかることがあり、初回applyでエラーになる場合があります。その場合は少し待ってから再度terraform applyを実行します。

Step 3-2:Firestore構成

次に、Firestoreの構成をコード化します。本記事では、Cloud Runから利用するFirestore Nativeモードの(default)データベースを1つ作成します。

resource "google_firestore_database" "database" {
  project     = var.project_id
  name        = "(default)"
  location_id = var.region
  type        = "FIRESTORE_NATIVE"
  
  depends_on = [
    google_project_service.project
  ]
}

注意:Firestoreは1つのGoogle Cloudプロジェクトに複数のデータベースを作成でき、Standard/Enterpriseともプロジェクトあたり最大100データベースが基本上限です。本記事では、クライアントライブラリなどで既定の接続先となる(default)データベースを利用します。既にコンソールなどから(default)データベースを作成済みで、Terraform管理へ切り替える場合はterraform importで既存リソースを取り込みます。

Step 3-3:Cloud SQL構成(プライベートIP接続)

Cloud SQLにはパブリックIPを利用する接続方法もありますが、本記事ではインターネットを経由せずVPC内から接続できるプライベートIP構成を採用します。Private Services Accessを利用するため、VPCとService Networking接続を先に用意する必要があり、Firestoreと比べて構成要素が増えます。

# VPC
resource "google_compute_network" "vpc" { ... }
# プライベートIP範囲の予約
resource "google_compute_global_address" "private_ip_address" { ... }
# VPCとGoogleサービスのピアリング
resource "google_service_networking_connection" "private_vpc_connection" { ... }
# Cloud SQL インスタンス
resource "google_sql_database_instance" "instance" {
  ...
  settings {
    ip_configuration {
      ipv4_enabled    = false
      private_network = google_compute_network.vpc.id
    }
  }
  deletion_protection = var.environment == "prod"
  depends_on = [
    google_service_networking_connection.private_vpc_connection
  ]
}

Tip:deletion_protection = var.environment == "prod" のように環境変数で分岐させると、Terraformによる本番インスタンスの誤削除を防ぎやすくなります。ただし、このdeletion_protectionはTerraformからの削除を保護する設定です。Cloud Console、gcloud CLI、APIを含む削除保護が必要な場合は、Cloud SQL側のsettings.deletion_protection_enabledも検討します。

注意:検証時、servicenetworking.services.addPeering権限が不足してエラーになるケースがありました。Private Services Accessの作成にはこの権限を含むネットワーク権限が必要で、Google Cloudの公式ドキュメントではroles/compute.networkAdmin(Compute Network Admin)に必要な権限が含まれると案内されています。Ownerは非常に広い権限を持つため、実運用では必要な権限に絞って付与してください。また、Organization Policyによる制約はOwner権限でも回避できないため、IAM不足と組織ポリシー制約は分けて確認します。

また、データベースパスワードはソースコードや.tfvarsへ直接書き込まず、Secret Managerで管理します。ただし、Terraformのsensitive = trueはCLI出力をマスクする機能であり、通常のsecret_dataに渡した値はTerraform stateに保存されます。本検証では下記の方法を使いましたが、本番ではstateのアクセス制御・暗号化を行うほか、対応する新しいTerraform/Google Providerを利用できる場合はsecret_data_woなどのwrite-only引数、またはシークレット値だけをTerraform外から登録する方法も検討してください。

# Secret Managerにシークレットを作成
# ※検証時のコード。secret_dataに渡した値はTerraform stateにも保存されます。
resource "google_secret_manager_secret" "db_password" { ... }
# シークレットにバージョン(値)を追加
resource "google_secret_manager_secret_version" "db_password_version" {
  secret      = google_secret_manager_secret.db_password.id
  secret_data = var.db_password
}

検証結果:Secret Managerのリソースはスムーズに作成でき、IAM連携も正しく動作しました。

Step 3-4:Cloud RunからCloud SQL/Firestoreにアクセスする

Cloud Runサービス自体の定義と、それに紐づくIAM権限をまとめて作成します。本検証ではServerless VPC Access Connectorを利用しました。現在、Google CloudではCloud RunからVPCへ接続する方法としてDirect VPC egressが推奨されているため、新規設計ではまずDirect VPC egressを検討し、要件に応じてConnector方式を選択してください。

# VPCアクセスコネクタ
resource "google_vpc_access_connector" "connector" { ... }
# Cloud Run サービス
resource "google_cloud_run_v2_service" "default" {
  ...
  template {
    ...
    vpc_access {
      connector = google_vpc_access_connector.connector.id
      egress    = "PRIVATE_RANGES_ONLY"
    }
  }
}

注意:本検証のConnector方式ではvpc_accessの設定がないと、Cloud RunからプライベートIPのCloud SQLへ到達できません。Direct VPC egressを利用する場合は、Cloud RunサービスにVPCネットワーク/サブネットを設定します。一方、Firestoreは通常GoogleのマネージドAPI経由でアクセスするため、Cloud SQLのようなプライベートIP接続のためのVPC設定は通常不要です。利用するデータサービスによって、Cloud Run側のネットワーク設計が変わる点は押さえておきましょう。

検証結果:VPC Access Connectorは問題なく作成できました。作成に約3〜4分かかるため、terraform apply実行時はしばらく待つ必要があります。また、削除時も同様に3〜4分かかりました。

Step 4:*.tfvars - 変数の値

ここからは、variables.tfで定義した変数に、実際の値を入れる、* tfvarsファイルについて説明していきます。environments/dev.tfvarsとenvironments/prod.tfvarsを用意し、環境差分だけを変数化します。


# environments/dev.tfvars
project_id  = "my-gcp-project-dev"
environment = "dev"
...
# environments/prod.tfvars
project_id  = "my-gcp-project-prod"
environment = "prod"
...

適用時は以下のように指定します。

terraform init
terraform plan -var-file="environments/dev.tfvars"
terraform apply -var-file="environments/dev.tfvars"

注意:db_passwordのような機密値は.tfvarsへ直接書かず、環境変数やCI/CDのSecret機能などから渡します。ただし、入力経路をSecretにしても、通常のTerraformリソース引数として利用した値はstateへ保存される場合があります。GCSバックエンドのアクセス制御や暗号化を行い、対応バージョンを利用できる場合はwrite-only引数も検討してください。

実際の検証手順と結果

この記事のTerraformコードを実際に検証した手順と結果は以下の通りです。

検証環境

  • Terraform v1.15.8
  • Google Provider v5.45.2
  • 組織管理下のプロジェクトで実施

検証ステップ

コマンドプロンプトで以下のコマンドを打って検証を行います。

1. 文法チェック (terraform validate)

terraform init -backend=false
terraform validate

結果:Success! The configuration is valid.

2. 差分確認(terraform plan)
結果:Plan:22 to add, 0 to change, 0 to destroy.

terraform plan -var-file=verify.tfvars

以下の22リソースが正しく認識されました:

  • API有効化 × 6
  • VPC、プライベートIP範囲、Service Networking接続
  • Cloud SQLインスタンス、データベース、ユーザー
  • Firestore Nativeデータベース
  • Secret Manager(シークレット + バージョン)
  • サービスアカウント、IAM権限 × 4
  • VPC Access Connector
  • Cloud Runサービス、パブリックアクセス権限

3. 実リソース作成(terraform apply)

terraform apply -var-file=verify.tfvars -auto-approve

結果:15リソースの作成成功、7リソースで権限エラー

作成できたリソース(15個):

  • API有効化 × 6
  • VPCネットワーク
  • プライベートIP範囲予約
  • Secret Manager(シークレット + バージョン + IAM)
  • サービスアカウント
  • IAM権限 × 3
  • VPC Access Connector(作成に約3分30秒)

権限不足で作成できなかったリソース(7個):

  • Firestoreデータベース(Error 403:The caller does not have permission)
  • Service Networking Connection(servicenetworking.services.addPeering権限不足)
  • 上記に依存するCloud SQL関連リソース

重要:今回の検証では、IAM権限や組織側の制約が原因で一部リソースを作成できませんでした。IAM権限の不足であれば必要なロール/権限の付与で解消できますが、Organization Policyによる制約はOwnerロールでも回避できません。エラー時は、IAMとOrganization Policyを分けて確認してください。

4. クリーンアップ(terraform destroy)

terraform destroy -var-file=verify.tfvars -auto-approve

結果:Destroy complete! Resources:15 destroyed.

VPC Access Connectorの削除に約3分30秒、全体で約4分で完全にクリーンアップできました。

検証で分かった実務的なポイント

  • API有効化の反映時間:
    API有効化直後は反映に1〜2分かかり、すぐに依存リソースを作ろうとするとエラーになることがある
  • VPC Access Connectorの作成/削除時間:
    それぞれ約3〜4分かかるため、terraform applyやdestroyは余裕を持って実行する
  • IAM/Organization Policyの影響:
    企業プロジェクトでは権限や組織ポリシーの制約を受けるため、事前に必要権限を確認する。検証用プロジェクトを用意する場合も、本番と同じガードレールを意識して設計する
  • terraform destroyによる撤去:
    今回の検証では、Terraformで作成できた15リソースを約4分で削除でき、検証環境の作り直しを効率化できた

トラブルシューティング

検証を行っている最中に、私が経験したエラーとそのトラブルシューティング方法を以下に記載します。

API有効化エラーが出る

Error:Error creating Connector:googleapi:Error 403:
Serverless VPC Access API has not been used...

対処法:API有効化直後は反映に1〜2分かかります。google_project_serviceにdepends_onを設定していても、初回applyでこのエラーが出ることがあります。2〜3分待ってから再度terraform applyを実行してみてください!

Service Networking Connection の権限エラー

Error:googleapi:Error 403:Permission denied to add peering
for service 'servicenetworking.googleapis.com'

対処法:servicenetworking.services.addPeeringを含む必要権限を確認します。

  • Private Services Accessの作成権限を含むroles/compute.networkAdmin、または必要権限を含むカスタムロールを検討
  • 過剰なOwner付与を前提にせず、必要最小限の権限で対応
  • 組織ポリシーでconstraints/compute.restrictVpcPeeringなどの制約がないか確認

Firestoreデータベース作成エラー

Error:Error creating Database:googleapi:Error 403:
The caller does not have permission

対処法:

  • roles/datastore.ownerロールが必要
  • 既に(default)データベースが存在する場合はterraform importで取り込む

まとめ

今回の検証を経て、以下のようなことが分かりました。

  • FirestoreはCloud RunからマネージドAPI経由で利用できるので、Cloud SQLのプライベートIP接続と比べるとネットワーク構成をシンプルにしやすい
  • Cloud SQLをプライベートIPで利用する場合はVPCやPrivate Services Accessなどの構成が必要になる。Cloud RunからVPCへ接続する方法は、現在推奨されているDirect VPC egressをまず検討する
  • 環境ごとの.tfvars分離と、tfstateのGCS管理は最初から入れておくと後から苦労しない
  • Terraformで作成・削除を自動化すると、Cloud SQLのような継続課金リソースを含む検証環境も管理しやすくなる

初めてTerraformを触ってみたのですが、Google Cloudの環境構築をコードとしてまとめることで、作成・変更・削除の流れを整理しやすいと感じました!基本的な書き方とstateの扱い、権限やネットワークの考え方を押さえることで、より安全に活用できます。本記事が皆さんのTerraform活用のきっかけになれば嬉しいです。
Google Cloud の導入や活用でお困りのことがあれば、お気軽にお問い合わせください!