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情報が残り失敗する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ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" } }
# backend.tf(部分定義 — 環境別の値は外から -backend-config で渡す) terraform { backend "s3" {} }
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実値
# 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"
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.
.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".
・-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:
-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" {} |
workspaceと-var-fileを使ってdev・stg・prodを1つのコードで管理する手法については「Terraformのworkspaceと-var-fileで環境を分離する方法」も参照してください。workspaceは同一backend内でstateキーを分ける設計で、本記事のようにbackend定義自体を差し替える手法とは目的が異なります。
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:TerraformでRoute 53のaliasレコードとCNAMEを使い分ける設計|ALBへの向き先をHCLで宣言し名前解決を安定させる
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます