Terraformで環境ごとにstateの保管先を切り替える設計|部分設定ファイルと環境変数によるinit時の分岐

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > Terraformで環境ごとにstateの保管先を切り替える設計|部分設定ファイルと環境変数によるinit時の分岐
「dev・stg・prodでterraform initするたびにbackend.tfの中身を書き換えている」「-backend-configというオプションを見かけたが、ファイルに何を書けばいいかわからない」
Terraformのbackend設定は、terraform initで一度確定するとその後のplan・applyすべてに引き継がれます。複数環境をコードで管理していると、どの環境でinitしたかがstateの向き先を決める分岐点です。ここを誤ると、devのリソースをprodのstateで管理してしまう重大な事故につながります。

この記事では、terraform backend-config file(部分設定ファイル)を使って、init時にstateの保管先を環境ごとに切り替える実装手順を解説します。AWS S3をバックエンドに使う構成を例に、ファイル分割の設計からコマンド操作・CI連携まで実際の出力例とともに示します。

この記事のポイント

・-backend-config fileはinit時に部分設定ファイルでbackendの向き先を切り替える仕組み
・backend.tfはtype宣言のみの空定義、バケット名等の実値は環境別.hclに分離する
・TF_CLI_ARGS_initで-backend-configを環境変数化するとCIパイプラインと連携しやすい
・initを再実行する時は-reconfigureを付けないと前回のbackend情報が残り失敗する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜbackendの向き先をinit時に切り替えるのか

Terraformのbackendは、stateファイル(tfstate)をどこに保管するかを定義します。ローカルのterraform.tfstateではなく、S3やAzure Blob Storageなどのリモートストレージにstateを置くのが本番運用での定石です。

複数環境を持つ構成では、dev・stg・prodそれぞれ別のstateファイルを持つ必要があります。同じstateを共有すると、devでの変更がprodのリソース状態に影響してしまうためです。

よくある対応として「backend.tfをgitで管理せずに環境ごとに書き換える」というやり方がありますが、次の問題を引き起こします。

・エディタのバッファに古い環境の値が残り、誤ったバケットでinitしてしまう
・CIからterraform initを呼ぶ際にどのbackend.tfを使えばいいか管理が煩雑になる
・設定ファイルをgitにコミットできないため、チームで共有しにくい

Terraformが公式に提供する解決策が部分設定(Partial Configuration)です。backend.tfにはバックエンドの型だけを宣言し、具体的な値はterraform initの引数として外から渡します。その手段の一つが-backend-config FILEオプションで使う部分設定ファイルです。

なお、「環境をどの粒度でstateごと分けるか」という設計判断論(モノリシックか分割か)は本記事の対象外です。本記事はbackend定義自体をinit時にどう差し替えるかという実装手段に限定して解説します。

terraform backend-config fileを使ったinit時の切り替え手順

実装は3つのステップです。ファイル構成から確認しましょう。

1. backend.tfを「空定義(型のみ宣言)」にする

通常のbackend.tfはS3バケット名やキーを直書きします。

# backend.tf(従来の直書き形式 — 環境ごとに書き換えが必要で管理しにくい) terraform { backend "s3" { bucket = "myapp-dev-tfstate" key = "env/dev/terraform.tfstate" region = "ap-northeast-1" } }

部分設定を使う場合は、バックエンドの型("s3")だけを残してキーも値も書きません。

# backend.tf(部分定義 — 環境別の値は外から -backend-config で渡す) terraform { backend "s3" {} }

この状態でそのままterraform initを実行すると、Terraformはバケット名等の必須値が不足しているとして対話入力を求めるか、CIではエラーで停止します。必須値は次のステップで渡します。

2. 環境別の部分設定ファイル(.hclファイル)を用意する

環境ごとに.hclファイルを用意して、バケット名・キー・リージョン等を記述します。

ディレクトリ構成の例です。

project/ ├── backend.tf # 型のみ(s3・azurerm等) ├── main.tf ├── variables.tf └── envs/ ├── dev.hcl # dev環境のbackend実値 ├── stg.hcl # stg環境のbackend実値 └── prod.hcl # prod環境のbackend実値

各.hclファイルの中身は次のとおりです。

# envs/dev.hcl bucket = "myapp-dev-tfstate" key = "env/dev/terraform.tfstate" region = "ap-northeast-1"

# envs/stg.hcl bucket = "myapp-stg-tfstate" key = "env/stg/terraform.tfstate" region = "ap-northeast-1"

# envs/prod.hcl bucket = "myapp-prod-tfstate" key = "env/prod/terraform.tfstate" region = "ap-northeast-1"

.hclファイルはHCL形式の設定ファイルです。backend.tf本体の書式(backend "s3" { ... })と異なり、値だけをフラットに列挙する点に注意してください。

このファイル群はgitにコミットしてよい内容です。バケット名は機密情報ではなく、アクセス制御はS3バケットポリシーやIAMロールで担保するためです。

3. terraform init -backend-configで初期化する

-backend-configオプションに.hclファイルのパスを渡してterraform initを実行します。

# dev環境でinitする場合 $ terraform init -backend-config=envs/dev.hcl Initializing the backend... Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes. Initializing provider plugins... - Finding hashicorp/aws versions matching "~> 5.0"... - Installing hashicorp/aws v5.65.0... - Installed hashicorp/aws v5.65.0 (signed by HashiCorp) Terraform has been successfully initialized!

# prod環境でinitする場合(別端末・別CIジョブ等) $ terraform init -backend-config=envs/prod.hcl Initializing the backend... Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes.

initが成功すると、プロジェクトルートに.terraform/ディレクトリが生成されます。この中にterraform.tfstate(ローカルのbackend追跡ファイル)が作成され、どのS3バケット・キーを参照しているかが記録されます。

$ cat .terraform/terraform.tfstate { "version": 3, "serial": 1, "lineage": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "backend": { "type": "s3", "config": { "bucket": "myapp-dev-tfstate", "key": "env/dev/terraform.tfstate", "region": "ap-northeast-1" }, "hash": 123456789 } }

backend.config.bucketが期待どおりのバケットを指していることを確認してから、terraform planを実行してください。

TF_CLI_ARGS_initで環境変数から-backend-configを渡す

CI/CDパイプライン(GitHub Actions・GitLab CI等)では、環境ごとにシェル変数を切り替えてterraform initに渡したい場面が多くあります。その場合はTF_CLI_ARGS_init環境変数を使うと、コマンド自体は変えずにbackend設定を切り替えられます。

# GitHub Actionsでの例(envブロックで環境に応じて切り替える) env: TF_CLI_ARGS_init: "-backend-config=envs/dev.hcl" steps: - run: terraform init # 上記 env が自動的に適用される

TF_CLI_ARGS_initはterraform initを呼ぶすべての場面に自動で付加されます。コマンドに-backend-configを明示しなくてもよいため、CIのscriptステップを環境ごとに書き分ける必要がなくなります。

ファイルだけでなくキーと値の形式(KEY=VALUE)でも渡せます。バケット名を環境変数から動的に構成したい時に便利です。

# KEY=VALUE形式でbackend値を直接渡す例(ファイルなしで渡す方法) $ terraform init \ -backend-config="bucket=myapp-prod-tfstate" \ -backend-config="key=env/prod/terraform.tfstate" \ -backend-config="region=ap-northeast-1"

ファイル形式とKEY=VALUE形式は混在させることもできます。共通値を.hclに書いておき、環境固有のキー(bucket名など)だけKEY=VALUEでオーバーライドする設計も可能です。

initをやり直す時のトラブルシュートと注意点

1. 「Backend configuration changed」エラーで再initが必要になった場合

一度initしたあとで-backend-configの値を変えてinitし直そうとすると、次のエラーが出ることがあります。

$ terraform init -backend-config=envs/prod.hcl Error: Backend configuration changed A change in the backend configuration has been detected, which may require migrating existing state. If you wish to attempt automatic migration of the state, use "terraform init -migrate-state". If you wish to store the current configuration with no changes to the state, use "terraform init -reconfigure".

dev環境でinitした後にprod環境でinitし直すといった場面で発生します。対処は次のいずれかです。

-reconfigure:既存stateの移行なしにbackendを切り替える(最も一般的な対処)
-migrate-state:古いbackendのstateを新しいbackendへコピーしてから切り替える

ローカルのPCで環境を切り替えて動作確認する場合は-reconfigureを使うのが安全です。

# -reconfigureで再初期化(stateは移行しない) $ terraform init -reconfigure -backend-config=envs/prod.hcl Initializing the backend... Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes. Terraform has been successfully initialized!

2. backend.tfを空定義にした後で対話入力が求められる場合

-backend-configを指定し忘れたままterraform initを実行すると、必須フィールドの入力を対話形式で要求されます。

$ terraform init Initializing the backend... bucket The name of the S3 bucket. Enter a value:

CIでは対話入力を受け付けないためエラーになります。コマンドに-backend-config=envs/dev.hclを必ず付けるか、TF_CLI_ARGS_initで設定してください。

3. .terraform/ディレクトリをgitignoreに入れているか確認する

.terraform/はinitが生成するローカルキャッシュです。gitにコミットしてはいけません。.gitignoreに次の行があることを確認してください。

# .gitignore .terraform/ *.tfstate *.tfstate.backup # .terraform.lock.hcl はチームの合意次第でコミットするか決める # (プロバイダーバージョン固定のためコミット推奨)

.terraform.lock.hcl(プロバイダーのバージョンロックファイル)はgitにコミットしてチームで共有するのが推奨です。

本記事のまとめ

やりたいこと コマンド・設定
環境別の部分設定ファイルでinitする terraform init -backend-config=envs/dev.hcl
KEY=VALUE形式でbackend値を渡す terraform init -backend-config="bucket=xxx"
CIで環境変数からbackendを渡す TF_CLI_ARGS_init="-backend-config=envs/prod.hcl"
別のbackendに切り替えて再init(移行なし) terraform init -reconfigure -backend-config=envs/prod.hcl
initした後にどのbackendを向いているか確認 cat .terraform/terraform.tfstate
backend.tfの空定義(部分設定の骨格) backend "s3" {}
terraform backend-config fileによる部分設定は、backend.tfをgitで一元管理しながら環境の向き先だけをinit時に切り替えられる公式の仕組みです。複数環境を持つプロジェクトであれば、早い段階でこの設計に切り替えておくと、誤ったstateへのapplyを防げます。

workspaceと-var-fileを使ってdev・stg・prodを1つのコードで管理する手法については「Terraformのworkspaceと-var-fileで環境を分離する方法」も参照してください。workspaceは同一backend内でstateキーを分ける設計で、本記事のようにbackend定義自体を差し替える手法とは目的が異なります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、terraform backend-config fileによる環境別state管理からCI/CD連携・tfstate分割設計まで、ハンズオン形式で習得できるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。