Claude CodeでTerraformのコードを書く手順 — plan差分の検証まで
Claude CodeにTerraformの構成を書かせるとき、CLAUDE.mdの検証手順・fmtフック・applyを止める権限設定で、plan差分を人が確認する形に整える手順です。
Claude CodeにTerraformのコードを書かせるときの要点は、HCLの書き方より「書いたあとに何を通すか」を先に決めておくことです。terraform fmtとterraform validateで形と整合を機械的に確かめ、terraform planの差分を人が読み、applyはClaudeに実行させない。この3段をCLAUDE.md・フック・権限設定に分けて置くと、コードの生成が速くなっても本番への反映は人の手元に残ります。
先に決める検証ループ
Terraformは書いた構成がそのまま実インフラを変える道具です。そのためClaudeに任せる範囲は、次の3段に分けると扱いやすくなります。
| 段 | コマンド | 何を確かめるか | 誰が実行するか |
|---|---|---|---|
| 整形 | コマンドterraform fmt | 何を確かめるか標準の書式に揃っているか | 誰が実行するかフックが自動で実行 |
| 検証 | コマンドterraform validate | 何を確かめるか構文と内部の整合(属性名・型) | 誰が実行するかClaudeが編集のたびに実行 |
| 差分 | コマンドterraform plan -out=tfplan | 何を確かめるか何が作られ、変わり、消えるか | 誰が実行するかClaudeが実行し、人が読む |
| 反映 | コマンドterraform apply | 何を確かめるか実インフラの変更 | 誰が実行するか人だけが実行 |
terraform validateは、構成が構文として正しく内部で矛盾していないかを、変数や既存のstateと無関係に確かめるコマンドです。リモートのstateやプロバイダーAPIは検証しません。HashiCorpは、エディタの保存後チェックやCIのテスト手順のように自動で回しても安全だと説明しています。Claudeが編集を重ねる作業と相性がよい理由はここにあります。
一方で、validateが通っても「意図した変更になっているか」は分かりません。属性名が正しくても、消したくないリソースが削除対象に入っていることはあります。それを見つけられるのはplanだけです。
CLAUDE.mdにTerraformの作業手順を書く
CLAUDE.mdはセッション開始時に毎回コンテキストへ読み込まれる指示ファイルで、公式は1ファイル200行未満を目安にしています。また、指示は「検証できるほど具体的」に書くほうが守られやすいとされています。「Terraformを正しく書く」ではなく、コマンド名まで書きます。
# Terraform 作業ルール
## 構成
- ルートモジュールは `infra/` 配下。環境ごとの値は `envs/<env>.tfvars`
- 共有モジュールは `modules/` に置き、ルートから `source = "../modules/<name>"` で呼ぶ
## 編集のたびに実行する
1. `terraform fmt -recursive`
2. `terraform init -backend=false` のあと `terraform validate`
3. エラーが出たら原因を読んで直し、通るまで繰り返す
## 変更を提案するとき
- `terraform plan -out=tfplan` を実行し、add / change / destroy の件数を報告する
- destroy と replace を含む場合は、対象リソースのアドレスを一覧で示す
## 禁止
- `terraform apply` と `terraform destroy` は実行しない
- `*.tfstate` と `*.tfvars` の中身を出力しない・コミットしない
- `-target` は復旧作業のときだけ使い、日常の変更には使わないterraform init -backend=falseを挟むのは、validateには初期化済みの作業ディレクトリが要るためです。この指定なら、設定済みのbackendに触れずにプロバイダーとモジュールだけを取得できます。手元でstateのバックエンドへ接続していなくても、検証のループを回せます。
-targetを禁止側に置くのは、HashiCorpが「復旧やTerraformの制約の回避といった例外的な場面でだけ使う」もので、日常の操作には推奨しないと明記しているためです。「このリソースだけ確認したい」という場面で-targetが使われやすいため、ルールとして先に封じておきます。
構成が大きく、環境ごとに指示を変えたい場合は、.claude/rules/にファイルを分ける方法もあります。pathsフロントマターで対象を絞ると、該当ファイルを読んだときだけ読み込まれます。
---
paths:
- "**/*.tf"
- "**/*.tfvars"
---
# HCL の書き方
- 変数には `description` と `type` を必ず付ける
- 認証情報などの値は `sensitive = true` を付けた変数で受ける編集のたびにfmtを走らせるフック
整形はClaudeに頼むより、フックで機械的に走らせたほうが確実です。公式ガイドにはPrettierを対象にしたPostToolUseフックの例があり、Edit|Writeをmatcherにして編集後のファイルへコマンドを流す形です。同じ骨格をTerraform向けに置き換えると次のようになります(公式のPrettier例をもとにした形で、公式がTerraform用に示しているものではありません)。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "f=$(jq -r '.tool_input.file_path'); case \"$f\" in *.tf|*.tfvars) terraform fmt \"$f\" ;; esac"
}
]
}
]
}
}terraform fmtは、対象に特定のファイルを渡せます。.tfと.tfvarsだけを通すcase文を入れたのは、READMEや設定ファイルを編集したときにfmtが空振りしないようにするためです。-checkを付けると、ファイルを書き換えずに整形が必要なファイルの一覧と終了コードだけを返すので、フックではなくCIの確認に向いています。
なおfmtは、HashiCorpが「意図的に意見の強いコマンド」としており、カスタマイズの選択肢がありません。書式の議論をClaudeとの間で始めなくて済む、という点でもフック向きです。
plan差分を読むところまでをClaudeに任せる
構成を書いたら、planを保存形式で実行させます。
terraform plan -out=tfplan
terraform show tfplan-outで保存したplanは、あとからterraform apply tfplanに渡して、そのまま実行できます。この2段の流れは、HashiCorpが主にCIなどの自動化での利用を想定しているものです。-outを付けないplanは「投機的プラン」で、適用する意図のない差分の確認用と位置づけられています。
保存ファイル名に.tfの拡張子は付けません。Terraformが設定ファイルとして解釈し、以降のコマンドが構文エラーになるためで、慣例はtfplanです。
Claudeへの依頼は、次のように「変更内容の報告」を求める形にします。
infra/network の変更を plan して。
add / change / destroy の件数と、destroy と replace の対象アドレスを表にして報告して。
apply はしないで、差分の読み方に迷う箇所があればそのまま質問して。差分が空かどうかだけを機械的に知りたいときは、-detailed-exitcodeが使えます。終了コードが0なら差分なし、1ならエラー、2なら差分ありです。Claudeに「2が返ったら変更内容を要約する」と指示しておくと、スクリプトや後述の権限設定とも組み合わせられます。
applyを実行させない権限設定
CLAUDE.mdの禁止事項は指示であって、強制力はありません。強制したいなら、権限設定のdenyルールを使います。公式のルール評価は「deny、ask、allowの順で最初に一致したものが結果を決める」もので、denyに書いた内容は、より狭いallowが後ろにあっても覆りません。
{
"permissions": {
"allow": [
"Bash(terraform fmt *)",
"Bash(terraform validate *)",
"Bash(terraform init -backend=false *)",
"Bash(terraform plan *)",
"Bash(terraform show *)"
],
"deny": [
"Bash(terraform apply *)",
"Bash(terraform destroy *)",
"Bash(terraform state *)"
]
}
}書き方の要点は、公式の説明どおり*をサブコマンドの後ろに置くことです。Bash(terraform plan *)はterraform planで始まるコマンドだけを許可し、Bash(terraform *)にするとapplyまで通ってしまいます。ただしBashのルールは文字列の前方一致で照合されるため、terraform -chdir=infra applyのように書き方を変えた呼び出しには一致しません。この限界は公式にも記載があるので、denyだけに頼りきらず、次に述べる資格情報の分離を併用します。
実行環境そのものにapplyできる資格情報を渡さない、という守り方が最も確実です。Claudeを動かすシェルにはplanに必要な読み取り権限だけを持たせ、変更権限を持つ認証はCIのパイプラインに置きます。
terraformがstateやキャッシュを作業ディレクトリの外へ書く構成では、サンドボックスの書き込み範囲の調整が必要になることがあります。手順はallowWrite/denyWriteでkubectl・terraformの書き込み範囲を調整するにまとまっています。
stateとplanファイルの取り扱い
Terraformのstateとplanファイルには、リソースの属性やメタデータが詳しく入ります。初期パスワードやAPIトークンのような機密値が入ることもあります。ローカルで開発するとstateは平文のファイルとして置かれるため、HashiCorpはGitの管理から外すよう案内しています。
sensitive = trueは、CLIの出力とHCP Terraformの画面から値を隠すだけです。stateとplanファイルの中には値が保存され、terraform outputに-jsonや-rawを付けると平文で表示されます。この点は、Claudeに出力を読ませる運用で見落としやすい部分です。
-outで保存したplanファイルも同じ扱いです。画面上で値が伏せられていても、ファイルには平文で入ります。planファイルは潜在的に機密を含む成果物として扱い、コミットしない、チャットに貼らない、といったルールをCLAUDE.mdの禁止項目に含めておきます。
秘匿したい値そのものをstateに残したくない場合は、Terraformの一時的な値(ephemeral)や書き込み専用の引数を使う方法があります。これらは値をplanにもstateにも保存しない仕組みで、機密値をHCLに渡す設計を、Claudeに書かせる前の段階で決めておくと手戻りが減ります。
生成物の検査を増やす
validateとplanの先に、terraform testがあります。テストファイルは.tftest.hclまたは.tftest.jsonという拡張子で、モジュールの更新が既存の挙動を壊さないかを、短命なリソースに対して確かめます。v1.7.0以降はプロバイダーの返す値をモックにでき、実インフラを作らないテストも書けます。
Claudeにモジュールを書かせるときは、「変数の検証ルールを1つ足すたびにテストも1本足す」といった依頼にしておくと、生成コードの意図が実行可能な形で残ります。テストだけ通すハードコードを防ぐプロンプトの考え方は、.tftest.hclでも同じように当てはまります。
よくあるつまずき
validateが通ったのに、planで初めて失敗する
validateは構文と内部の整合だけを見ます。存在しないAMIやリージョンの制約のような、プロバイダーAPIを介して分かる誤りは、planまで実行しないと出ません。「validateが通った」を完了の合図にしない、とCLAUDE.mdに書いておきます。
保存したplanファイルを.tfで終わる名前にした
先ほどのとおり、構成ファイルとして読み込まれて次のコマンドが失敗します。名前はtfplanか.tfplanのような別の拡張子にします。
一部のリソースだけをplanしたくて-targetが混ざる
-targetで一部だけを対象にすると、依存先の変更を見逃したplanになります。Claudeが「確認のため」に使い始めたら、ルールの読み込みを疑い、/contextでCLAUDE.mdが読まれているかを確かめます。
まとめ
TerraformでClaude Codeを使うときの土台は、fmtとvalidateを機械的に回し、planを人が読み、applyは実行させない、という3段の分担です。CLAUDE.mdに手順を書き、フックで整形を自動化し、権限のdenyでapplyを止め、資格情報の側でも変更権限を渡さない。指示・自動化・権限・認証の4層を重ねるほど、生成の速さと本番の安全が両立します。
同じ考え方は、KubernetesマニフェストやAnsibleのPlaybookにも通用します。Terraform側のツール連携としては、Terraform MCPサーバーを経由してClaudeにIaCコードを書かせる方法もあります。