🎃

terraform-mcp-server × Claude Code 活用で既存 GoogleCloud 環境を IaC 化する

に公開

はじめに

Google Cloudプロジェクトを運用していると、コンソールやgcloudで作成したリソースをTerraformで管理したいというニーズがよくあります。この記事では、terraform-mcp-serverと Claude Code を活用して、既存プロジェクトのインフラをコード化(IaC)した方法を紹介します。

この記事は以下を利用しています。

  • Claude Code v2.0.27
  • Model: Sonnet 4.5

terraform-mcp-server とは

hashicorp/terraform-mcp-server

The Terraform MCP (Model Context Protocol) server is a specialized tool that enables LLMs to generate high-quality Terraform code and manage Terraform workflows through API integrations with HCP Terraform and Terraform Enterprise.
see: instructions/example-mcp-instructions.md

というものです。このMCP サーバがある場合は実際のドキュメントを参照してコードを生成するので、初めから精度の高いコードを生成できます。

https://developer.hashicorp.com/terraform/mcp-server/deploy#run-in-docker

GitHub のほかに公式サイトにインストール方法が書いてあります。MCPサーバは docker の他にコンパイル済みバイナリファイル による提供もあります。ちなみに Homebrew Formulae にもありました。

https://formulae.brew.sh/formula/terraform-mcp-server

プロジェクトセットアップ

早速 terraform 化をすすめていきます。ディレクトリ構造は最終的にこんな感じになるのをイメージしてます。Google Cloud のドキュメント[1]では、environments ディレクトリを配置することを推奨してますが、各環境のルートへのネストが深くなるので今回は避けました。

  • app - アプリケーションインフラ、基本的にここにいれておきます
  • db - データベース & ストレージ、BQ / Redis / RDB などはここにいれます
  • tool - 開発ツール & CI/CD, Slack通知の関数やCloud Build などはここにいれます
terraform
├── README.md
├── app
│   ├── <environments>
│   │   ├── <resource>.tf
│   │   ├── backend.tf
│   └── modules
│       └── <module name>
├── db
├── tool
├── backend.template.tf
└── generate-backend.sh

はじめにディレクトリを作成します

mkdir -p terraform/{app,db,tool}/{modules,production,develop,local}

Terraform ブロックと provider ブロックは共通化したいので、それらを生成するコマンドを作成します。Claude Code をつかって、テンプレートファイル backend.template.tf と シェル generate-backend.sh を作成します。 初期作成では terraform のバージョンが古いので、追加で依頼し修正します。
ちょっとしたスクリプトをすぐつくれるのは本当にいい時代になりましたね

プロンプト
以下を満たす template file と shell script を terraform-mcp-server をつかって作成してください。

1. template file
- Terraform は最新をバージョン指定
- terraform ブロックの backend "gcs" ブロックを持ち、bucket と prefix は置換可能な形式にする
- terraform ブロックの required_providers ブロックは google, google-beta を持つ
- provider google ブロックの default_labels.environment と project は置換可能な形式にする、region / zone は固定値を入れる

2. shell script - backend.template.tf
- template file の変数を引数で受け取れるようにする e.g.) --prefix=<your prefix>
- 受け取った変数から envsubst を使って terraform file を生成する
- 生成する terraform file は標準出力する

それぞれ terraform/ 配下に保存する

生成できたのでチェックします。

プロンプト
@terraform/backend.template.tf のバージョンが最新か確認して
出力
⏺ バージョンを確認して更新しました:

  確認結果

  - Terraform: 1.10.5 → 1.13.4 (最新安定版) に更新しました
  - Google Provider: 7.8.0 は最新版です ✓
  - Google-Beta Provider: 7.8.0 は最新版です ✓

  現在の最新安定版は Terraform 1.13.4 で、1.14.0
  はまだリリース候補(RC)段階です。本番環境で使用する場合は安定版の 1.13.4 を使用することをお勧めします。

こんな感じのものができました。

https://github.com/sikeda107/tech-blog/blob/main/iac-with-mcp/backend.template.tf

https://github.com/sikeda107/tech-blog/blob/main/iac-with-mcp/generate-backend.sh

早速作成したシェルスクリプトを利用します

プロンプト
@terraform/generate-backend.sh をつかって backend.tf を生成します
それぞれの GCP_PROJECT は

1. production は <production-project-id>
2. develop は <develop-project-id>
3. local は <local-project-id>

出力先は app / db / tool 配下の対応する環境です

各環境へ backend.tf が配置できたらOKです。
そして、terraform state を保存する backend に Cloud Storage を使いたいので、gcloud で作成します

https://cloud.google.com/storage/docs/creating-buckets?hl=ja

PROJECT_ID=<your project id>

gcloud storage buckets create gs://terraform-remote-backend-mybucket \
--project=$PROJECT_ID \
--default-storage-class=STANDARD \
--location=asia-northeast1 \
--uniform-bucket-level-access \
--soft-delete-duration=7d

Terraform 化していく

本題である Terraform コードを実装していきます。流れは以下のとおりです。

  1. MCP × gcloud コマンドで import ブロックの定義
  2. terraform plan で HCL を自動生成
  3. 生成した HCL を MCP で整理

1. MCP × gcloud コマンドで import ブロックの定義

今回は既存のリソースを取り込むので import ブロックを定義します。既存リソースの取り込み方法については公式ドキュメントをご参照ください。

https://developer.hashicorp.com/terraform/language/import

基本的に以下のプロンプトをリソースごとに実行していきます。 Terraform化は何度もやることじゃないので、今回はスラッシュコマンドやサブエージェントはつくらずコピペと少しの調整で進めていきました。

プロンプト
@terraform/app/develop/backend.tf
@terraform/app/develop/import.tf

<Google Cloud サービス名> を terraform で管理します。

1. `gcloud asset search-all-resources` または `gcloud asset search-all-iam-policies` で scope に project id を設定してリストアップする
2. terraform-mcp-server をつかって、import block を import.tf へ定義する

以下の指示を守ること

- import 以外は定義しないこと。
- ユーザ作成ではないリソースは import しないこと。

terraform-mcp-server があるため import ブロックがほぼ正解な状態で生成できます。MCPがないときは、毎回以下をやっていたのでこのためだけでも使う価値を感じてます。

  1. document を開いて import の節を確認する
  2. サンプルをコピペする
  3. 実際のリソースIDを確認する
  4. 書き直す

例えば Cloud Build トリガーの場合は以下のページを開き、import の節を確認します。毎回これを開いて確認しているのでとても時間がかかります。

google_cloudbuild_trigger | Resources | hashicorp/google | Terraform | Terraform Registry

もしも asset に対応していないリソースだとしても、個別のリソースのコマンドを自分で探してきて実行してくれます。


CloudSchedulerを代替コマンドで探索してるところ

また、ロードバランサーなどで関連するリソースが多く、確実に定義したいものがあればその定義もいれてます。特に証明書は似たリソースが多いので、入れたほうが確実でした。

たとえば以下をプロンプトに追記します

プロンプト
対象のリソースは以下のとおりです
1. google_compute_managed_ssl_certificate
2. google_compute_ssl_policy
3. google_compute_global_address
4. google_compute_target_https_proxy
5. google_compute_target_http_proxy
6. google_compute_global_forwarding_rule
7. google_compute_url_map

2. terraform plan で HCL を自動生成

import ブロックが定義できたので、次に resource を定義していきます。このまま Claude Code に resource 作成を依頼してもいい感じに実装してくれそうですが、わたしは plan コマンドをつかって先にベースコードを生成します。

plan で自動生成したものを整理することで、設定値の漏れがないか、実際のリソースと差分がないかといった不安がなくなります。

https://developer.hashicorp.com/terraform/language/import/generating-configuration

terraform init
terraform plan -generate-config-out=generated.tf
generated.tf
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.

# __generated__ by Terraform from "projects/xxxxx/locations/asia-northeast1/jobs/xxxxx"
resource "google_cloud_scheduler_job" "xxxxx_xxxxx" {
  attempt_deadline = null
  description      = null
  name             = "xxxxx_xxxxx"
  paused           = false
  project          = "xxxxx_xxxxx"
  region           = "asia-northeast1"
  schedule         = "00 1 * * *"
  time_zone        = "Asia/Tokyo"
  pubsub_target {
    attributes = {
      foo = "bar"
    }
    data       = null
    topic_name = "projects/xxxxx_xxxxx/topics/xxxxx_xxxxx"
  }
  retry_config {
    max_backoff_duration = "3600s"
    max_doublings        = 5
    max_retry_duration   = "0s"
    min_backoff_duration = "5s"
    retry_count          = 0
  }
}

そのあと、生成したファイルに対して設定値などを整理します

プロンプト
@terraform/app/develop/generated.tf

以下をおこなうこと

1. 自動生成のコメントを削除する
2. 直接 resource url, ip_address を記載しているものは resource id から参照するようにする
3. null / [] など terraform default 値は削除してください。 値のチェックには terraform-mcp-server をつかってください
4. project プロパティは削除する
5. generated.tf は適切な粒度でリソースをファイルに分割すること

最後に fmt / validate でチェックする


自動生成のコードを修正しているところ

generated.tf
resource "google_cloud_scheduler_job" "xxxxx_xxxxx" {
  name      = "xxxxx_xxxxx"
  region    = "asia-northeast1"
  schedule  = "00 1 * * *"
  time_zone = "Asia/Tokyo"

  pubsub_target {
    attributes = {
      foo = "bar"
    }
    topic_name = "projects/xxxxx_xxxxx/topics/xxxxx_xxxxx"
  }

  retry_config {
    max_backoff_duration = "3600s"
    max_doublings        = 5
    max_retry_duration   = "0s"
    min_backoff_duration = "5s"
  }
}

ここまででほとんど問題ないレベルのものが作成できてますが、 topic_name といった他のリソースから取得できる値が直書きされています。そういったものは次で直していきます。

3. 生成した HCL を MCP で整理

最後に、Claude Code が振り分けたリソースを整理したり、app ではなく db や tool のほうに振り分けたり、他のリソースIDを参照したり最終調整をおこないます。

共通のものとしては

  • ユーザ作成ではないリソースは import しないでください
  • アルファベット順で大文字小文字も意識してソートして
  • サービスアカウントの email は resource から取得する
  • compute engine default service account を使ってるところは data を定義して利用してください

など。

例えば、Cloud Run では

プロンプト
1. client, client_version, labels, container image, traffic を lifecycle から ignore してください。 また、image 以外の定義は削除してください
2. project id は data を main.tf に定義して利用してください
3. container の env は locals ブロックに括り出してください。 "KEY" = "VALUE" の形式がいいです

プロンプト
1. Cloud Run Service IAM Members は各 Cloud Run と同じファイルに定義して
2. iam member と service account は近くに定義して 

Cloud Armor では

プロンプト
@terraform/app/develop/security_policy.tf の google_compute_security_policy の rule を google_compute_security_policy_rule で定義しなおしてください
なお、terraform-mcp-server をつかってください 

※ google_compute_security_policy_rule として rule をくくりだすことで plan 時の差分がでにくいようにしています。

といったことをやっていきます。細かい中身の意図などはあえて書きませんが、よさそうなものがあれば参考にしてみてください。今回はフラットな構造を意識したので、modules をあまり定義しませんでしたが、必要に応じてやっていってもいいかと思います。

簡単ですが以上で大体のコードは生成できました🚀

おわりに

terraform-mcp-server と Claude Code 活用で既存 GoogleCloud 環境を Terraform で IaC 化した経験を紹介しました。terraform-mcp-server のおかげで試行錯誤しながらすすめても、1日もかからずに Terraform 化を終えられました。
gcloud やコンソールから Cloud Run などのリソースをとりあえず 作ってしまい、Terraform 化がめんどうになることは多いと思います。面倒だなと思ってるその一歩目として、そういった方はぜひ参考にしてみてもらえるとうれしいです😃

おまけ resource-config の bulk-export の感想

https://cloud.google.com/docs/terraform/resource-management/import?hl=ja

Google Cloud では公式で、terraform のコードを生成する機能があります。これもためしてみましたが以下の点から利用するのをやめました。

  • すべてのリソースを出力できるわけではなく、対応できていないリソースも多い。Cloud Tasks キューや Serverless NEG などがなくこれは残念でした
  • terraform import の shell を生成することはできるが、import ブロックの生成には対応していない。import する前に plan で差分を確認したいのでこれも残念でした

コードの参考にはなるので、ないよりはあるほうが精度があがるのは間違いないです。しかし、provider のドキュメントを参照しつつ resource id さえわかれば terraform コマンドでコード生成はできるので結果的に利用を見送りました。

脚注
  1. 環境固有のサブディレクトリにアプリケーションを分割する - ルート モジュールのベスト プラクティス  |  Terraform  |  Google Cloud Documentation ↩︎

コミューン株式会社

Discussion