インフラ

TerraformでGCPを構築する手順|google 7系の認証とGCSバックエンド設計

TerraformでGoogle Cloudを触り始めると、最初の30分はだいたい認証で止まります。日本語の入門記事はサービスアカウントのJSONキーを作ってcredentialsに渡す手順が主流ですが、Google Cloud公式のIAMベストプラクティスは「We recommend that you avoid using service account keys whenever possible.」と書いている。この記事では、google 7系プロバイダの設定とgoogle-betaの併用条件、キーを配らない認証(ADC・Workload Identity Federation・権限借用)の組み立て、tfstateをGCSへ置くbackend "gcs"の引数とロック・分割境界、サポートが終了したDeployment Managerからの移行先までを、2026年8月14日時点の一次情報で実測して整理します。

まとめ:GCPをTerraformで扱うときに先に決める認証・state・境界の3点

結論から書きます。認証はローカルがADC(gcloud auth application-default login)+権限借用、CI/CDはWorkload Identity Federationの2本立てにし、サービスアカウントのJSONキーは組織ポリシーで作成自体を止めるところまで含めて設計してください。キーを配る構成は、監査ログに「誰が使ったか」が残りません。

stateはGCSバケットへ置き、本番と検証はバケットごと別プロジェクトに分けるのが実務上の境界です。同一バケット内のprefix分割はIAMの境界にならないため、環境分離の手段としては弱い。prefixは権限が同じ相手の分割に使います。

プロバイダはhashicorp/googleの7系(2026年8月14日時点の最新は7.44.0・2026年8月11日公開)を使い、バージョンは~> 7.0のようにメジャーで固定する。Deployment Managerは2026年4月1日にサポートが終了しており、新規の選択肢には入りません。

google 7系プロバイダの設定と、google-betaを併用する条件

Google Cloud向けのTerraformは、プロバイダを1つ書けば動きます。ただしその1つに書くprojectregionの扱いを誤解したまま進むと、リソースが意図しないプロジェクトに作られます。

google providerのproject・region既定とリソース側指定の優先順

provider "google"ブロックに書くprojectregionzoneは、あくまで既定値です。各リソースが自分のproject引数を持っている場合はそちらが優先され、プロバイダ側の値は使われません。マルチプロジェクト構成でこの優先順を忘れると、共有VPCのサブネットだけホストプロジェクト、残りはサービスプロジェクト、といった配置が崩れます。

環境変数GOOGLE_PROJECTGOOGLE_REGIONでも既定値を与えられます。CI上ではこちらのほうが扱いやすい一方、手元とCIで異なる値が入っているとterraform planの差分が人によって変わる事故が起きます。プロジェクトIDは変数化してterraform.tfvars-var-fileで明示的に渡し、環境変数は補助にとどめてください。宣言的な構成管理そのものの考え方はIaCとは?Infrastructure as Codeの仕組み・メリットと導入判断で扱っています。

7.0で厳格化されたimport形式と、6系から上げる前に通す確認手順

7.0.0は2025年8月26日公開で、以降は7.44.0(2026年8月11日)まで週次に近い頻度で更新されています。8系は同日時点でプレリリースも含めて存在しない。今から書き始めるなら7系が唯一の現行メジャーです。

7.0.0で全リソースに効いた変更が、importの入力検証でした。公式アップグレードガイドは「All GCP resource IDs supplied to “terraform import” must match the documentation specified import formats exactly.」と記載しています。6系では入力の一部が正しければ通っていた不正な形式が、7系では弾かれる。既存環境を取り込む予定があるなら、この差が移行作業の見積りに直結します。

個別リソースでは、データソースgoogle_service_account_keyprojectが削除され、google_alloydb_clusterにはdeletion_protectionが既定trueで追加されました。後者は「In 7.0.0, existing clusters will have deletion_protection set to true during the next refresh unless otherwise set in configuration.」とあり、次回refreshで既存クラスタにも付きます。公式が示す上げ方は、6系の最終版へ先に上げてガイドの修正を当て、terraform planが想定外の差分と非推奨警告を出さない状態を作ってから7系へ進む順です。バージョン制約の書き方と.terraform.lock.hclの扱いはterraform providerのバージョン制約とalias設計にまとめてあります。

google-betaを足す判断基準と、同一リソースで併用しない理由

Google Cloudのプロバイダは1.19.0以降、googlegoogle-betaの2種類があります。公式の説明は「The google-beta provider is distinct from the google provider in that it supports GCP products and features that are in Preview, while google does not.」。ドキュメントは共有され、beta限定のフィールドとリソースには印が付きます。

注意すべきは送信先です。公式は「The google-beta provider sends all requests to the beta endpoint for GCP if one exists for that product, regardless of whether the request contains any beta features.」と明記している。beta機能を1つも使っていなくても、そのリソースの通信はbetaエンドポイントへ流れます。だからリポジトリ全体を切り替えるのではなく、必要なリソースにだけprovider = google-betaを書くのが正しい足し方です。

同一のクラウドリソースを両方のプロバイダで宣言するのは避けてください。Terraformから見れば別アドレスの2リソースであり、applyのたびに相手の変更を打ち消し合います。GAへ昇格したらbeta指定を外し、terraform planが無差分になることを確認してから宣言を戻す。なおTerraform側のリソース名と実サービス名がずれる例もあり、Google Cloud FunctionsとCloud Run functionsへの改称はその一例です。

default_labelsで統一するタグ設計と属性ラベルの既定挙動

コスト配賦のラベルを全リソースへ手で書く運用は続きません。プロバイダのdefault_labelsはトップレベルにlabelsを持つリソースへ一括適用され、リソース側で同じキーを指定するとそちらが勝ちます。共通ラベルを既定に置き、例外だけ上書きする形にしてください。適用結果はterraform_labelseffective_labelsに記録され、planの差分でも追えます。あわせてadd_terraform_attribution_labelの既定値がtrueである点も押さえておくとよい。Terraformが作ったリソースにはgoog-terraform-provisioned = trueが自動で付き、コンソールから「コード管理下か、手作業か」を見分けられます。

サービスアカウントキーを配らない認証設計とWIF・権限借用の使い分け

ここが日本語圏の入門記事と最も食い違う部分です。JSONキーを作る手順は動きますが、Google公式は別の方法を先に検討するよう書いています。

ローカルはADC、CIはWIF|キーのJSONを配布しない前提の組み立て

Google Cloud公式のIAMベストプラクティスは「We recommend that you avoid using service account keys whenever possible.」と述べ、理由を「Cloud Audit Logs creates a log when a service account modifies a resource, but if the service account is authenticated with a service account key, there is no reliable way to tell who used the key.」と説明しています。キーが漏れたかどうか以前に、監査ログから実行者を特定できない点が問題です。

代替として公式が挙げるのは、Application Default Credentials、外部ワークロード向けのWorkload Identity Federation、ユーザー資格情報からのサービスアカウント権限借用、Google Cloud内リソースへのサービスアカウント添付の4つ。Terraformの文脈では、手元がgcloud auth application-default loginで得たADC、GitHub ActionsなどのCIはOIDCトークンをWorkload Identity Federationで交換、GCEやCloud Build上ならインスタンスに添付したサービスアカウント、と整理できます。公式はさらに「We recommend preventing the creation of service account keys by applying the Disable service account key creation organization policy constraint.」として組織ポリシーでキー作成自体を塞ぐことまで勧めている。運用開始後にこれを入れると既存の自動化が一斉に止まるため、プロジェクト立ち上げの初日に決める項目です。

impersonate_service_accountで権限を借りる構成と必要ロール

権限借用は、ユーザー自身に強い権限を持たせずにTerraformを動かす方法です。プロバイダのimpersonate_service_accountに借用先のサービスアカウントを指定すると、以降のGoogle API呼び出しがそのサービスアカウントとして実行される。公式の記述は「You must have roles/iam.serviceAccountTokenCreator role on that account for the impersonation to succeed.」です。

このロールは付け先を間違える例が多く見られます。付与するのは借用先サービスアカウントというリソースに対して、借用する側のユーザーへ。プロジェクトレベルで全員に付けると、そのプロジェクト内の全サービスアカウントを誰でも借りられる状態になります。組織単位の委譲が必要なときはimpersonate_service_account_delegatesでチェーンを明示し、CIでは環境変数GOOGLE_IMPERSONATE_SERVICE_ACCOUNTを使うと扱いやすい。

見落としやすいのがbackend側です。backend "gcs"はプロバイダの設定を読まないため、stateの読み書きも借用したいならbackendブロック側にも同じ引数が要ります。プロバイダだけ設定してbackendを忘れると、リソース操作は通るのにstateの取得で403になる。

user_project_overrideとbilling_projectを設定する場面

ADCで進めると、あるタイミングから「quota project」に起因する403が出ます。プロバイダのuser_project_overrideは既定falseで、公式は「Controls the quota project used in requests to GCP APIs for the purpose of preconditions, quota, and billing.」という説明です。falseのときのquotaプロジェクトはAPI側の判定に委ねられ、資格情報に紐づくプロジェクトになることがあります。

ここで問題になるのがgcloud由来の資格情報です。公式には「Credentials that come from the gcloud tool are associated with a project owned by Google. In order to properly use credentials that come from gcloud with Terraform, it is recommended to set this property to true.」との記載があります。つまりローカルのADCで開発するならuser_project_override = trueが既定の選択で、あわせてbilling_projectに自分のプロジェクトIDを指定する構成です。trueのとき、リクエストにはX-Goog-User-Projectシステムパラメータが付きます。環境変数はそれぞれUSER_PROJECT_OVERRIDEGOOGLE_BILLING_PROJECTです。

tfstateをGCSへ置くbackend gcsの引数とロック・プロジェクト分割

Google公式のTerraformベストプラクティスは、デプロイの推奨として「Store Terraform state in a Cloud Storage bucket」と書いています。ローカルのtfstateは1人で試す段階までです。

backend “gcs”のbucket・prefixと環境ごとに分ける置き方

必須の引数はbucketだけで、グローバルに一意なGCSバケット名を渡します。実務で併せて使う引数は次のとおりです。

  • prefix:バケット内でstateファイルを置くパス。環境やコンポーネント単位の仕切りに使う
  • impersonate_service_account:state操作を借用したサービスアカウントで行う
  • credentials(環境変数GOOGLE_BACKEND_CREDENTIALS):backend専用に資格情報を分けたい場合
  • access_token:短命のOAuth 2.0トークンを直接渡す。CIでWIFと組み合わせる構成で使う
  • storage_custom_endpoint:Private Service Connect経由でGCSへ到達させる場合

backendブロックには変数を書けません。環境ごとにバケットを変えるなら、値はterraform init -backend-config=env/prod.gcs.tfbackendのように外から渡します。tfstateそのものの構造や、バックエンドを移すときの手順はTerraform stateとは?tfstateの構造とバックエンド・移動削除の安全手順で扱っているので、GCS固有の部分だけこちらで押さえてください。

stateロックとGCS側の暗号化オプションを決めるときの判断軸

ロックについて、HashiCorpの公式ドキュメントはgcsバックエンドに対して「This backend supports state locking.」と明記しています。DynamoDBのような別サービスを用意する必要はなく、バケットさえあれば同時applyの衝突を防げる。ここはS3バックエンドと運用感が違う点です。

暗号化は3択です。何も指定しなければGoogle管理の鍵で保存され、encryption_key(環境変数GOOGLE_ENCRYPTION_KEY)にbase64エンコードした顧客提供の鍵を渡せばCSEK、kms_encryption_keyを指定すればCloud KMSの鍵を使います。判断軸は単純で、鍵のローテーションとアクセス監査を担当できる人がいるならKMS、いないならGoogle管理鍵のままにしてください。CSEKは鍵を失うとstateを復号できず、鍵の配布経路がキー配布と同じ問題を抱えます。

暗号化より先に効くのはバケットのバージョニングです。stateは書き込み時に上書きされるため、誤ったterraform state rmや中断したapplyから戻る手段はバージョニングしかない。バケット作成時に有効化し、古い世代はライフサイクルルールで削除します。

プロジェクトを分けるか、prefixで分けるか|state分割の境界

本番と検証を分ける手段として、同一バケットのprefix違いで済ませる構成をよく見ます。これは勧めません。Google CloudではIAMと課金の境界がプロジェクトであり、バケット内のprefixには権限境界がないためです。検証環境の権限しか持たないはずのメンバーが、バケットの読み取り権限経由で本番のtfstateを読めてしまいます。tfstateには接続文字列や生成された認証情報が平文で入りうる。

実務の境界はこうです。本番と検証は別プロジェクト・別バケットに分ける。同一環境内でネットワーク層とアプリケーション層を分けたい、といった権限が同じ相手の分割はprefixで足ります。分割単位を細かくするほどapplyの所要時間は短くなりますが、terraform_remote_stateでの参照が増えて依存が絡みます。環境をどの方式で分けるかの比較はterraform workspaceの使い方と環境分離の判断基準に整理しました。workspaceは同一バックエンド内で状態を切り替える仕組みであり、プロジェクトごと分ける本番/検証の分離とは目的が異なります。

Deployment Manager終了で決まる移行先とInfra Managerの採否

Google Cloudのネイティブなデプロイ機構だったDeployment Managerは、すでにサポート期間の外にあります。既存デプロイを抱えている組織にとっては、移行の期限が明確な日付で決まっています。

サポート終了2026年4月1日・新規遮断6月30日・停止2027年6月30日

Google Cloud公式の廃止ドキュメントに記載されている日程を整理します。

時期 状態 実務上の意味
2026年4月1日 サポート終了 標準サポート無回答。新機能・重大修正以外停止
2026年6月30日 新規利用の遮断 新規はV2 API有効化・初回デプロイ作成不可
2027年3月31日 最大延長サポートの終了 この期間はブロッカーと移行関連のチケットのみ受付
2027年6月30日 サービス停止 APIと機能が停止する

既存ユーザーは自己責任で管理・移行・削除を続けられますが、事実上の作業期限は2027年6月30日です。新規プロジェクトでDeployment Managerを検討する余地はもうありません。逆算すると、既存デプロイの棚卸しと移行計画は2026年度内に着手しておく規模感になります。

Infra Managerと素のTerraform|state管理の担当をどちらに置くか

Google公式が移行先として挙げるのはInfrastructure Manager(Infra Manager)です。Deployment ManagerがYAMLとJinja/Pythonテンプレートで構成を書いたのに対し、Infra ManagerはTerraformでインフラのデプロイを作ります。どちらの道を選んでも、書く言語はHCLになる。

分岐点はstateの管理責任です。Infra Managerはstateの保管とTerraformの実行をGoogle Cloud側が持つマネージドサービスで、バケットの設計もCIランナーの用意も要りません。素のTerraformにbackend "gcs"を組み合わせる構成は、バージョン固定・ロック・実行環境をすべて自前で持つかわりに、他クラウドやSaaSのプロバイダを同じパイプラインへ混ぜられます。判断基準は明確に置けます。Google Cloudだけで完結してstateの運用担当を置けないならInfra Manager、CI/CDにIaCを載せて他クラウドやDatadog・GitHubも同じ仕組みで管理するなら素のTerraformです。中間の「とりあえず両方」は実行経路が二重になってstateの真実が分かれるため取りません。移行の設計や既存環境の棚卸しから相談したい場合は、AWS・Google Cloud・Azureのインフラ構築で受託の実績があります。

DM Convertで変換したコードをそのまま運用に載せない理由

Googleは移行支援としてDM Convertを提供しており、Deployment Managerの構成をTerraform形式へ自動変換できます。ただし変換の出力は、既存デプロイに含まれるリソース定義を平坦に書き写したものです。モジュール分割も変数化も命名規約も入っていません。Google公式のTerraformベストプラクティスが求める標準モジュール構造(main.tfvariables.tfoutputs.tf)とはかけ離れた形になります。

安全な進め方は次の順です。

  1. DM Convertで変換し、生成されたHCLをterraform fmtにかけて読める形にする
  2. 既存リソースをterraform importでstateへ取り込む(7系のimport形式は厳格なので、公式ドキュメントの形式と完全一致させる)
  3. terraform planが差分ゼロになるまで、変換されたコードを実リソースへ寄せる
  4. 差分ゼロを確認したうえで、モジュール化と変数化のリファクタリングに入る

3と4を入れ替えると、コードを整理した結果の差分なのか、変換漏れによる差分なのかを切り分けられなくなります。差分ゼロという中間ゴールを必ず1回作ってください。

GCPでTerraformを見送る条件と、権限設計で詰まる典型パターン

ここまで設計の話をしてきましたが、そもそもTerraformを入れないほうがよい場面もあります。判断を先に書きます。

Terraformを見送る条件|単発検証とマネージド前提の小規模構成

次の2つに当てはまるなら、Terraformは入れないでください。1つは数日で捨てる検証環境で、gcloudのコマンドを手順書に残すほうが速く、tfstateの管理コストが丸ごと無駄になります。もう1つは、Cloud Run 1サービスとFirestore程度のマネージド構成で、構成変更が月1回未満、触る人が1名という規模。この条件下ではコードの陳腐化のほうが早く、次に触るときには実環境とコードがずれていて、結局コンソールで直すことになります。

採用条件も条件付きで言い切れます。環境が2つ以上ある、構成変更が週次以上で発生する、触る人が2名以上いる。このうち2つを満たしたらTerraformを入れる側に倒してください。分かれ目は構成の複雑さではなく変更の頻度と関与人数で、1人が月1回触る複雑な構成より、3人が毎週触る単純な構成のほうがIaCの効果は出ます。

権限設計で詰まる入口|Token Creatorの付け先とquotaプロジェクト

止まる箇所は2種類に収れんします。1つはquotaプロジェクトの未設定で、ADCのままuser_project_overridefalseで進めると一部のAPIが403を返す。エラー文面がIAM不足のように見えるためロールを足す方向へ迷い込みがちですが、原因は課金プロジェクトの解決です。

もう1つは、backendとproviderで必要な権限が別物である点です。providerが要求するのは対象リソースの操作権限、backendが要求するのはstateバケットに対するオブジェクトの読み書き権限(roles/storage.objectAdmin相当)。CIのサービスアカウントへ前者だけ与えてterraform initで止まる、という詰まり方をします。前述のTokenCreatorの付け先違いと合わせ、この3点を先に潰しておけば初期構築で丸一日を失うことはありません。

マルチクラウドを1リポジトリで扱う条件と、分けたほうがよい場面

Google CloudとAWS、あるいはAzureを併用している場合、リポジトリを分けるか統合するかで迷います。判断はシンプルです。デプロイの依存関係がクラウドを跨ぐ場合だけ、同一のTerraform構成に置いてください。AWS側のRoute 53レコードがGoogle Cloud側のロードバランサIPを参照する、といった関係がこれにあたります。1つのstateでgoogleawsのプロバイダを併記したほうが順序を保証できる。

跨がないなら分けてください。プロバイダを1つのstateに混ぜると、片方のメジャーアップグレードのたびに無関係なもう一方のリソースまでrefreshの対象になり、terraform planの所要時間も認証失敗の影響範囲も広がります。Azure側の実装はTerraformでAzureを構築する手順とazurerm 5系の認証・backend設計にまとめてあり、認証方式もbackendの引数もGoogle Cloudとは別物。共通化できるのはディレクトリ構成とCIの型までで、認証とstateの設計は各クラウドで作り分けます。

よくある質問

Google CloudをTerraformで扱い始めた段階で寄せられる質問を、判断の理由まで含めて答えます。

terraform gcpの学習で最初に作るリソースは何がよいですか?

GCSバケット(google_storage_bucket)を勧めます。作成が数秒で終わり、費用がほぼ発生せず、削除も容易で、しかもstateを置く先そのものだからです。バケットを1つ作ってからbackendをローカルからGCSへ移すと、terraform init -migrate-stateの挙動まで一度に体験できます。逆に最初にGKEクラスタやCloud SQLを選ぶと、作成に10分以上かかり、失敗時のリトライで学習のテンポが落ちます。

サービスアカウントキーのJSONを使う構成は今も使えますか?

技術的には動きます。プロバイダのcredentials引数やGOOGLE_CREDENTIALS環境変数は現行の7系でも有効です。ただしGoogle Cloud公式のIAMベストプラクティスは「We recommend that you avoid using service account keys whenever possible.」とし、キー認証では監査ログから実行者を特定できない点を理由に挙げています。新規に組むなら、ローカルはADC、CIはWorkload Identity Federationにしてください。既存でキーを使っている場合は、まず組織ポリシーで新規キーの作成を止め、既存キーの棚卸しから始めるのが現実的な移行順です。

google providerのバージョンはどこまで固定すべきですか?

required_providersversion = "~> 7.0"のようにメジャーを固定し、パッチとマイナーは.terraform.lock.hclで固定する組み合わせを勧めます。google providerは週次に近い頻度で更新され、2026年8月11日時点の最新は7.44.0です。マイナーまで固定すると新リソースが使えず、無指定だとメジャー更新を意図せず踏みます。lockファイルはリポジトリへコミットし、更新はterraform init -upgradeを明示的に実行したときだけ起こす運用にしてください。

GCSバックエンドのバケットはTerraformで作ってよいですか?

作って構いませんが、そのバケットを管理するstate自体は同じバケットに置かないでください。鶏と卵になり、バケットを消すとstateも消えます。実務では、stateバケットとサービスアカウントだけを扱う小さな構成を1つ用意し、そこはstateをローカル管理かつ変更頻度をほぼゼロにする、という分け方が現実的です。作成時はバージョニングを有効化し、lifecycle { prevent_destroy = true }を付けて誤削除を止めておきます。

Deployment Managerの既存デプロイはいつまでに移せばよいですか?

サービス停止は2027年6月30日で、これが最終期限です。ただしサポートはすでに2026年4月1日に終了しており、標準の問い合わせには回答がありません。2027年3月31日までの最大延長期間も、受け付けられるのはブロッカーと移行関連のチケットに限られます。移行中に問題が起きたときに支援を受けられる期間から逆算すると、2027年3月末までに移行を完了させる計画が現実的です。変換はDM Convertで始め、importでstateへ取り込み、planが差分ゼロになる状態を作ってからリファクタリングへ進んでください。

関連記事

資料請求

RELATED POSTS 関連記事