terraform variableとlocals・tfvarsの使い分け|型制約と機密値
Terraformの変数まわりでつまずく箇所は、文法ではなく割り当ての判断にあります。外から差し替えるものは variable、コード内部で名前を付け直すだけのものは locals、環境ごとの実値は tfvars ファイル。この境界が決まっていないと、どこを直せば挙動が変わるのかが追えません。この記事では3者の割り当て基準、型制約と validation で入力ミスをplan生成前に止める書き方、-var と TF_VAR_ と terraform.tfvars の優先順位、APIキーを state に残さない3層の手段を、HashiCorp公式ドキュメントの記述に沿って整理します。
まとめ:variableは境界・localsは内部式・tfvarsは環境値
判断の起点は「その値を誰が差し替えるか」の一点です。呼び出し側や実行者が差し替える値だけを variable にし、同じ式を何度も書きたいだけなら locals に寄せます。tfvars ファイルは variable に実値を流し込む入れ物であって、変数の定義場所ではありません。
型制約と validation は、書けるところにはすべて書きます。Terraformは変数の検証をプラン生成の前に実行するため、ここで弾いた誤りは差分の読み合いにすら到達しません。逆に any で型を緩めると、誤りはプロバイダのAPI呼び出しまで届きます。
機密値は3層で考えます。sensitive は出力の表示を止めるだけで state には平文で残り、v1.10以降の ephemeral 変数が state とプランファイルから値を外し、v1.11の write-only 引数がリソース側まで保存対象から外します。tfvars に秘密を書く運用は、この3層を使えないときの次善策です。
variableとlocalsとoutputの役割分担と、書き換えられる値の境界
3つを混ぜて使うと、値の出所を探す時間がコードを書く時間を上回ります。先に境界を固定します。
variableが外部入力・localsが内部式という公式の線引き
variable はモジュールの入力口です。呼び出し側の module ブロック、CLIのオプション、環境変数、tfvars ファイルのいずれからでも値を差し込めます。locals は異なる性質です。公式ドキュメントは、ローカル値は定義したモジュール内でのみ参照でき、他のモジュールからは参照できないと明記しています。
この非対称が、そのまま割り当ての基準になります。dev と prod で違う値を入れたいなら variable、命名規則から組み立てたタグ一式のように入力から機械的に決まる値なら locals。宣言的な構成管理の考え方はIaCとは?Infrastructure as Codeの仕組み・メリットと導入判断で整理しています。
localsをmoduleの外から上書きできない制約と使いどころ
locals は上書きできないことが弱点ではなく、選ぶ理由です。外から触れないため、値が変わる経路が定義行の1箇所に限定されます。命名の組み立て、共通タグのマージ、条件分岐の結果への命名。この3系統に収めておくと、読む人が探す場所を間違えません。
下の表は、3者の性格を並べたものです。
| 種類 | 値の出所 | 外部からの上書き | 主な用途 |
|---|---|---|---|
| variable | 呼び出し側・CLI・環境変数 | できる | 環境差・秘密・切り替え |
| locals | 同じモジュール内の式 | できない | 命名の組み立て・共通タグ |
| output | モジュール内の計算結果 | できない | 親や他構成への値の受け渡し |
output は入力ではなく戻り値です。モジュール境界をどこに引き、どの値を output で出すかという設計はTerraform moduleの分割粒度とoutput設計で扱っています。
値の出所が追えなくなるlocals多用の弊害と、採用してよい条件
公式ドキュメントは locals について、繰り返しを避けて式に意味のある名前を与える一方で「they can make configuration harder to read because they obscure where values originate」と注意し、多くの箇所で再利用する値や複雑なロジックを表す場合に用途を限る指針を示しています。
採用してよい条件は2つに絞れます。同じ式が3箇所以上に現れるとき。三項演算子や for 式が入り込み、リソース定義の中に置くと1行が読めなくなるとき。1箇所でしか使わない単純な文字列に locals で名前を付ける行為は、参照を1段深くするだけで得がありません。
型制約に使えるprimitiveと複合型、anyを使ってよい場面の線引き
型制約は入力の契約書です。書式そのものは短いのに、誤りを止める効果が最も大きい部分でもあります。
string・number・boolの自動変換とnullableの既定値true
プリミティブ型は string、number、bool の3種です。公式は、有効な表現を含む場合はこの3種が相互に自動変換されると説明しています。"3" を number 型の変数に渡しても通るのはこのためで、逆に型を書いておけば "three" のような値はその場で落ちます。
見落としやすいのが nullable です。既定値は true で、明示的に null を渡す経路が開いたままになります。null を受け取ると壊れる変数には nullable = false を付けてください。
object型のoptional指定と、省略時にnullが入る挙動の押さえ方
複合型は、コレクションが list・map・set、構造型が object・tuple です。設定の塊を1つの変数で受け取るなら object 型を選び、属性ごとに型を宣言します。
省略可能な属性には2つの書式があります。optional(型) だけを書いた属性は、省略されるとその型の null です。optional(型, 既定値) と既定値まで書いた場合は、省略されても明示的に null を渡されても既定値が入り、公式は非nullの既定値を持つ属性は受け取り側モジュール内で null にならないと保証しています。null 判定の分岐をモジュール内に書きたくないなら、後者を選ぶ理由がここにあります。
anyを避ける公式の指示と、型を緩めたときに出る実行時エラー
公式ドキュメントは any について「Do not use any just to avoid specifying a type constraint.」と直接的に禁じ、内容を検査せず他システムへ不透明に受け渡す場合に限ると述べています。
型を書かないコストは、エラーの発生位置に現れるものです。型があれば変数の検証段階で止まる誤りが、any では式の評価やプロバイダのスキーマ検証まで進んでから落ち、メッセージもリソース属性の型不一致という遠い場所の文言に変わります。型と description を必須にする記述レベルの統一ルールはTerraformコーディング規約(スタイルガイド)にまとめてあります。
validationの条件式とerror_message、v1.9で広がる参照範囲
型制約が「形」を縛るのに対し、validation は「値の中身」を縛ります。両方を書いて初めて入力の契約が成立します。
conditionが自変数のみだった制約とv1.9以降の他変数参照
v1.9より前の validation は、condition の中で当該変数そのものしか参照できませんでした。環境名とクラスタ設定のように複数の変数が絡む検証は、データソースやリソースの precondition へ逃がす必要がありました。
v1.9でこの制約が外れ、condition から他の入力変数・データソース・ローカル値を参照できるようになっています。「クラスタを作らない設定なら接続先エンドポイントの指定が必須」といった相互依存を、入力の段階で落とせます。バージョン固定の運用に迷う場合は先に required_version の方針を決めてください。
plan生成前に評価されるvalidationと後段のprecondition
評価の順序が使い分けの根拠になります。公式は validation について「Terraform executes input variable validations immediately, before generating a plan」と明記しています。precondition は plan 生成後・リソース作成前、postcondition はリソース作成やデータ読み込みの後です。
入力値だけで判定できる誤りを precondition に書くと、プラン生成の計算を丸ごと無駄にします。逆に、データソースの実測結果に依存する検証は validation へ持ち込めません。判定材料が入力値だけなら variable 側、実在するリソースの状態が必要なら precondition 側という切り分けで足ります。
error_messageに条件と直し方まで書く複数行の文面設計
error_message は文字列に評価される任意の式を受け取り、複数行に対応します。公式によれば、先頭に空白がある行では折り返しが行われません。テンプレート式を混ぜて、渡された値そのものを差し込むこともできます。
文面には、許容される値の範囲・いま渡された値・どのファイルを直せばよいかを入れます。「invalid value」の一行だけでは、tfvars を書いた本人以外に修正できません。
checkブロックが警告で済む理由と、止めたい検証の置き場所
check ブロックは通常のリソースライフサイクルの外で走り、失敗しても警告を出すだけで操作は続きます。デプロイ後にヘルスチェック用のURLを叩いて健全性を見る、といった継続的な確認向けです。
ここを取り違えると事故になります。適用を止めたい条件を check に書くと、警告が出たまま apply が完走します。止めたい検証は validation か precondition へ置いてください。プランに現れた差分をどこまで通すかの判断基準はterraform planの差分の読み方で別に扱っています。
tfvarsとTF_VAR_と-varの優先順位、環境別に値を渡す方式の比較
「値を変えたはずなのに反映されない」の大半は、優先順位の取り違えです。読み込み規則を先に固定します。
terraform.tfvarsと.auto.tfvarsが自動で読まれる条件
カレントディレクトリに terraform.tfvars または terraform.tfvars.json があれば、Terraformは自動で読み込みます。.auto.tfvars または .auto.tfvars.json で終わるファイルも同様に自動読み込みの対象で、こちらは複数置けます。
環境ごとに分けたファイルなど、それ以外の名前は自動では読まれません。-var-file で明示的に渡す必要があり、この指定漏れが環境取り違えの入口になります。
-varとTF_VAR_とdefaultの優先順位を6段で押さえる整理
公式が示す優先順位は高い順に6段です。第1にコマンドラインの -var と -var-file(指定した順序で後勝ち)およびHCP Terraformの変数、第2に *.auto.tfvars と *.auto.tfvars.json(ファイル名の辞書順)、第3に terraform.tfvars.json、第4に terraform.tfvars、第5に TF_VAR_ 接頭辞の環境変数、最下位が variable ブロックの default です。
環境変数が tfvars ファイルより弱い点は、CIの設計に直接効きます。パイプラインで環境変数を設定していても、リポジトリに terraform.tfvars が残っていればそちらが勝つ。CI側で値を確実に効かせたいなら -var を使ってください。
環境別の値をtfvarsで分ける方式とディレクトリ分割の比較
環境差の持たせ方は、大きく3方式に分かれます。
| 方式 | 値の置き場所 | 取り違えの起点 | 採用条件 |
|---|---|---|---|
| tfvarsファイル分割 | 環境ごとの.tfvars | -var-fileの指定漏れ | 環境2〜3・構成が同一 |
| ディレクトリ分割 | 環境ごとのルート | 共通部の二重管理 | 環境ごとに構成が違う |
| workspace | 変数の条件分岐 | selectの切り替え忘れ | 差分が数個・同一権限 |
実務での既定はtfvarsファイル分割です。設定が1ファイルに集まり、差分がテキストで読めます。ディレクトリ分割は、prodだけ冗長構成でリソース種別から違うといった場合に切り替えます。workspace は state が同じバックエンドに並ぶため、権限分離が要る本番には向きません。方式ごとの詳細はterraform workspaceの使い方と環境分離の判断基準で扱っています。
機密値をstateに残さない扱い、sensitiveとephemeralの守備範囲
ここは誤解が実害に直結する領域です。3つの機能の守備範囲が階段状に違います。
sensitiveが隠すのは出力だけでstateには平文で残る事実
variable に sensitive = true を付けると、planやapplyのCLI出力から値が伏せられます。ただし公式ドキュメントは「Terraform still stores the values of sensitive variables in your state.」と明記しています。tfstate は平文のJSONです。
sensitive は画面共有やCIログでの露出を防ぐ機能であり、秘密の保管方法ではありません。state を保護する側の対策と組み合わせる前提になります。tfstate の中身とバックエンドの保護はTerraform stateとは?tfstateの構造とS3バックエンド・移動削除の安全手順で扱っています。
v1.10のephemeral変数がstateとplanから値を外す仕組み
v1.10以降で使える ephemeral 引数は、実行中だけ値を保持し、stateファイルとプランファイルからその値を除外します。短命なトークンやセッション識別子のように、保存する意味がなく保存すると危険な値が対象です。
代償として、値を割り当てられる場所に制限が付きます。ephemeral な値は同じく一時的に扱える場所へしか渡せず、通常のリソース属性へは素通しできません。後付けする場合は、その変数がどのリソースへ流れているかを先に洗い出してください。
v1.11のwrite-only引数でDBパスワードを残さない構成
v1.11では、マネージドリソース側に write-only 引数が入りました。書き込み専用で読み出されない引数で、ephemeral な値を受け取り、プランにも state にも保存されません。
RDSの初期パスワードのように「リソースへ渡す必要はあるが保存したくない」値が、ここで初めて素直に扱えます。プロバイダ側が対応した引数を用意している必要があるため、使う前に write-only 版の引数名を確認してください。
tfvarsをリポジトリに置かない運用と外部管理への逃がし方
上の3層を使えない事情がある場合の次善策が、秘密を含む tfvars をバージョン管理から外す運用です。secrets.auto.tfvars のようなファイルを除外設定に加え、実行環境側にだけ配置します。
この方式には運用上の穴があります。ファイルが配布物になるため、更新の取り違えと配布漏れが起きる。秘密の本数が5本を超えたら、シークレット管理サービスからデータソースで取得する構成へ切り替えてください。クラウド基盤の構築とCI設計をまとめて任せたい場合は、インフラ構築(AWS・Google Cloud・Azure)の相談窓口で個別の構成を前提に検討できます。
変数化を見送る条件と、なんでもvariableにする設計が壊す可読性
変数は多いほど良いものではありません。ここでは見送る側の条件を言い切ります。
1環境しか使わない構成で変数化を見送る判断と直書きの許容範囲
環境が1つしかなく、当面増える計画もない構成では、リージョン名やインスタンスタイプを variable にする必要はありません。直書きで十分です。変数化は「差し替える予定がある」ことへの投資であり、予定がなければ回収されません。
線引きは明確です。値の変更にコードの変更が伴っても構わないもの、つまりレビューを通す前提の値は直書きしてよい。実行時に人や環境が差し替える値だけを variable にします。この基準で初期構成の変数はおおむね半分以下に減ります。
defaultを付けた変数が事故を招く場面と、必須入力にする基準
default は便利ですが、危険な既定を静かに通す仕掛けにもなります。環境名に default = "dev" を置いた構成で、CIが変数の受け渡しに失敗すると、エラーにならず dev 向けの構成が本番のstateへ適用されます。
既定値を付けてよいのは、どの環境で使っても被害が出ない値だけです。環境名、アカウントID、VPCのCIDR、削除保護の可否には default を付けず、未指定なら実行が止まる状態にしてください。優先順位の最下位が default である以上、これは「誰も何も指定しなかったとき」の保険であり、通常運用で当たる想定にしてはいけません。
型制約とvalidationを入れる変数と省いてよい変数の切り分け
type と description は全変数に付ける前提でよい一方、validation は書くほど得をするわけではありません。判定基準は「誤った値がどこで壊れるか」です。
プロバイダ側が明確なエラーを返す値、たとえば存在しないインスタンスタイプなら、validation を足す価値は薄い。逆に、環境名の綴り違い、命名接頭辞の長さ超過、CIDRの範囲のように「そのまま適用できてしまい、後から気づく」類の誤りには必ず書きます。ここに validation を置いた分だけ、事故がプラン生成前に落ちます。
よくある質問
変数設計まわりで実際に検索されている質問へ、公式ドキュメントの記述をもとに答えます。
terraform variableとlocalsはどちらを使うべきですか?
外から値を差し替える必要があるなら variable、モジュール内部で式に名前を付けたいだけなら locals です。locals は定義したモジュール内でしか参照できず、外部からの上書きもできません。判定に迷ったら「この値をdevとprodで変えるか」を問い、変えるなら variable にします。同じ式が3箇所以上に現れる場合が locals の出番です。
tfvarsファイルはGitにコミットしてよいですか?
秘密を含まない環境設定値であれば、コミットしたほうが差分をレビューできて安全です。問題になるのはAPIキーやDBパスワードを書いた場合で、コミットせず除外設定に加えます。ただし秘密を tfvars で扱う運用自体が次善策で、v1.10以降なら ephemeral 変数、v1.11以降ならリソース側の write-only 引数を使い、値そのものを state とプランから外す構成を先に検討してください。
terraform.tfvarsと-var-fileはどちらが優先されますか?
コマンドラインの -var-file が優先されます。公式が示す優先順位は高い順に、-var と -var-file、*.auto.tfvars(辞書順)、terraform.tfvars.json、terraform.tfvars、TF_VAR_ 環境変数、variable の default の6段です。CIで環境変数を設定しているのに反映されないケースは、リポジトリに残った terraform.tfvars が勝っている状態を疑ってください。
validationで他の変数を参照するとエラーになるのはなぜですか?
Terraform v1.9より前のバージョンでは、validation の condition が当該変数そのものしか参照できなかったためです。v1.9でこの制約が外れ、他の入力変数・データソース・ローカル値を condition から参照できるようになりました。エラーが出る場合は、まず terraform version で実行中の版を確認してください。古い版のまま相互依存を検証したい場合は、リソースやデータソースの precondition へ条件を移す方法があります。
変数にAPIキーを渡すときstateに残さない方法はありますか?
sensitive = true だけでは足りません。公式ドキュメントは、sensitive を付けても値は state に保存されると明記しています。v1.10以降であれば ephemeral 引数を付けた変数を使うことで、state とプランファイルから値が除外されます。リソースの引数として渡す値は、v1.11の write-only 引数への対応をプロバイダのドキュメントで確認してください。いずれも使えない環境では、tfstate 側の暗号化とアクセス制御で守る前提になります。
関連記事
- IaCとは?Infrastructure as Codeの仕組み・メリットと導入判断を解説:宣言的な構成管理の考え方と導入判断。
- Terraform moduleの分割粒度とoutput設計|自作とRegistryの判断:モジュール境界と入出力の設計。
- terraform workspaceの使い方と環境分離の判断基準|分割方式の比較:環境分離方式の比較と選び方。
- terraform planの差分の読み方|init・apply・destroyの安全手順:差分をどこまで通すかの判定基準。
- Terraformコーディング規約(スタイルガイド)|fmt・命名規則・ディレクトリ構成の統一ルール:命名と記述の統一ルール。