fmt・validate・plan を回すことで、変更を先にレビューできるこの記事は、GCP上のTerraform運用を「ひとりでローカルから叩く形」から、「GitHub Actionsで安全に回す形」に進化させる実践例です。
元記事の著者は、以前のラボではローカル端末でこんな流れだったと書いています。
terraform planterraform apply学習用途ならこれで十分です。
でも、少し実務っぽくなると話は別です。インフラ変更は、アプリコード以上に事故が怖い。うっかりVPCを消したり、意図しない設定を入れたりすると、影響範囲が大きいからです。
そこでこの記事では、Terraformの実行をGitHub Actionsに移し、次の流れを作っています。
fmt → validate → planapplyこれ、かなり“ちゃんとした運用”に近いです。個人的には、Terraformを「コードとしてレビューする対象」にする発想がとても大事だと思います。インフラもソフトウェアなので、レビューなしで本番反映するのはやっぱり危ないですよね。
この記事の核は、実はCI/CDそのものよりも認証方法です。
普通にGCPへアクセスしようとすると、サービスアカウントのJSONキーをGitHub Secretsに入れたくなります。
でもそれだと、そのキーは長期間使える“強い鍵”になります。漏れたら面倒ですし、ローテーションも地味に大変です。
そこで使っているのが Workload Identity Federation です。
ざっくり言うと、
という仕組みです。
つまり、JSONキーをダウンロードしない。
この「鍵を置かない」設計がかなり良いです。運用の安心感が全然違います。静的キーを消せるだけで、セキュリティの話が一段軽くなるんですよね。
このラボが作るインフラは、あえてシンプルです。
凝ったネットワーク構成や複雑なサービスは作りません。
理由ははっきりしていて、CI/CDの流れをまずデバッグしやすくしたいからです。
これ、すごく実践的だと思います。
最初から大きな構成でやると、問題が出たときに「Terraformが悪いのか」「認証が悪いのか」「GitHub Actionsが悪いのか」が分かりにくいです。だから小さく始めるのは正解です。
記事のフォルダ構成はこうです。
.github/workflows/
lab-09-terraform-plan.ymllab-09-terraform-apply.yml09-terraform-cicd-github-actions/
backend.tfmain.tfvariables.tfoutputs.tfterraform.tfvars.exampleREADME.mdTerraform本体は 09-terraform-cicd-github-actions/ に置き、GitHub Actionsのワークフローは .github/workflows/ に分けています。
この分離は見た目もわかりやすいし、管理もしやすいです。地味だけど大事。
Terraformは「今どんなインフラがあるか」を覚えておくために、state という状態ファイルを使います。
この記事では、そのstateをGCSに置いています。
terraform {
backend "gcs" {
bucket = "terraform-gcp-learning-lab-terraform-state"
prefix = "terraform-gcp-learning-lab/09-terraform-cicd-github-actions"
}
}
この設計のポイントは、前のラボとstateを分けていることです。
stateが混ざると、Terraformは「何がこの構成に属するのか」を見失いやすくなります。実務でも、プロジェクトごと・環境ごとにstateを分けるのはかなり基本です。
保存先のパスはこんな形になります。
gs://terraform-gcp-learning-lab-terraform-state/terraform-gcp-learning-lab/09-terraform-cicd-github-actions/default.tfstateTerraformの中身は、VPCとサブネットを作るだけです。
resource "google_compute_network" "vpc_network" {
name = "${var.environment}-${var.network_name}"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "subnet" {
name = "${var.environment}-${var.subnet_name}"
region = var.region
network = google_compute_network.vpc_network.id
ip_cidr_range = var.subnet_cidr_range
}
ここでの要点は以下です。
auto_create_subnetworks = false
network = google_compute_network.vpc_network.id
ip_cidr_range = "10.90.1.0/24"
ネットワークの話はとっつきにくいですが、要するに建物の敷地と、その中の区画を作っているイメージです。
VPCが敷地、サブネットが区画、という感じで見ると少し分かりやすいです。
variables.tf では、プロジェクトIDやリージョンなどを変数化しています。
特に重要なのが project にデフォルト値がないことです。
variable "project" {
type = string
validation {
condition = length(var.project) > 0
error_message = "The project variable must not be empty."
}
}
これは良い設計です。
プロジェクトIDをコードに埋め込むと、再利用しづらいですし、うっかり別環境へ流用したときに事故の元になります。
GitHub Actionsでは、リポジトリ変数から渡しています。
-var="project=${{ vars.GCP_PROJECT_ID }}"
このやり方は、設定をコードから追い出しつつ、CI/CD側で安全に注入するやり方です。
「コードに環境依存情報をべったり書かない」というのは、地味だけど本当に大切です。
著者は GitHub の以下に変数を置いています。
Settings -> Secrets and variables -> Actions -> Variables入っているのはこんなものです。
GCP_PROJECT_IDGCP_REGIONGCP_WORKLOAD_IDENTITY_PROVIDERGCP_SERVICE_ACCOUNTTF_WORKING_DIRTF_VERSION
ここで大事なのは、これらはサービスアカウントキーではないという点です。
つまり、Secretsに長期鍵を抱え込まなくて済む。ここはかなり気持ちがいいです。
Plan側のワークフローは、Pull Requestをトリガーにしています。
やっていることはシンプルです。
terraform fmt -check -recursiveterraform initterraform validateterraform planこの順番がすごく良いです。
fmt はコードの見た目を揃える、validate は構文や設定の妥当性を見る、plan は実際に何が変わるかを出す。
つまり、Pull Requestの時点で「変なTerraform」をかなり弾けるわけです。
特に id-token: write が重要だと書かれていました。
これは GitHub Actions が OIDC トークンを要求するために必要です。WIFを使うなら、ここを忘れると認証で詰みます。

Apply側は workflow_dispatch で、つまり手動実行です。
やっていることはPlanとほぼ同じですが、最後が違います。
fmtinitvalidateterraform plan -out=tfplanterraform apply tfplanここでの重要ポイントは、planの結果をファイルに保存して、そのままapplyすることです。
これにより、Planで見た内容とApplyで実際に適用される内容のズレを減らせます。
しかもワークフローに environment: terraform-apply が指定されています。
GitHub ActionsのEnvironmentを使うと、承認フローや保護ルールを噛ませやすいので、実務的にもかなり良い選択です。
個人的には、ここがこの構成のいちばん“ちゃんとしてる”部分だと思います。
「自動化したいけど、勝手に変更されるのは困る」という現実的なバランスが取れています。

このラボの良さは、自動化と統制の両方をちゃんと考えているところです。
CI/CDというと「全部自動で流す」方向に寄りがちですが、インフラはそう単純ではありません。
むしろ大事なのは、いつ・誰が・何を変えるかを見える化することです。
この記事の構成は、
という、かなり筋のいい設計です。
派手さはないけれど、実務ではこういう地味な堅牢さが一番効くと思います。

この記事は、Terraform運用を「便利な自動化」ではなく、安全に変更を管理する仕組みとして組み立てています。
特に良いのは以下です。
fmt / validate / planTerraformを使っていると、つい「動けばOK」になりがちです。
でも本当に大事なのは、あとから見返したときに安心できる運用だと思います。この記事は、その感覚をかなりわかりやすく形にしていて、読んでいて素直に参考になる内容でした。
参考: CI/CD for Terraform on GCP: Plan on Pull Request, Apply with Approval, No Static Keys