はじめに
Terraform で、リモート state の置き場(S3 バケット名やリージョン)も 変数(var.)でまとめたいと思いました。ほかの場所は変数にしているので、backend も同じノリで…と書いてみたところ。
backend ブロックだけは、変数(
var.)が使えませんでした😅
環境
- Terraform(AWS プロバイダ 5 系)
- リモート state: Amazon S3
- リージョン: ap-northeast-1
起きたこと
DRY にしたくて、こう書きたくなりました。
terraform {
backend "s3" {
bucket = var.state_bucket # ← これがやりたかった
key = "app/terraform.tfstate"
region = var.region
}
}
ところが backend の中で var. を使うと、terraform init の段階で弾かれます(「変数は使えない」という趣旨のエラー)。最初は書き方のミスかと思いましたが、調べると仕様でした。
原因
backend の設定は、Terraform が一番最初に読む部分です。
変数(var.)が解決されるより前に backend の情報が必要になるため、backend ブロックの中では変数を参照できない仕様になっています。「ここだけ DRY にできない」のには、ちゃんと読み込み順の理由がありました。
対処
backend の値は直書きし、bootstrap(置き場づくり)で作った S3 バケット名や key と一致させる運用にしました。一致のルールは README にメモして、取り違えを防ぎます。
terraform {
backend "s3" {
bucket = "myapp-tfstate-xxxx" # bootstrap で作ったバケット名・key と一致させる
key = "app/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
}
}
どうしても環境(dev / prod など)で差し替えたい場合は、terraform init -backend-config=... を使うと、init 時に外から値を渡せます。ファイルで渡す例:
terraform init -backend-config=dev.s3.tfbackend
# dev.s3.tfbackend(backend 設定だけを書く)
bucket = "myapp-dev-tfstate-xxxx"
key = "app/terraform.tfstate"
region = "ap-northeast-1"
※ ファイル名は自由ですが、公式では
*.バックエンド名.tfbackend(S3 ならdev.s3.tfbackendのような名前)が推奨されています。backend 設定用ファイルだと分かりやすくなります。※
-backend-configは便利ですが、アクセスキーなどの機密情報は入れない方が安全です。Terraform は backend 設定を.terraform配下や plan ファイルに保持することがあり、平文で残る可能性があります。認証情報は環境変数や AWS プロファイルで渡すのが無難です。
学び
-
backend ブロックでは
var.が使えない。Terraform が最初に読む部分だから。 - だから backend の値は直書きし、bootstrap で作った名前と一致させる。README に一言書いておくと迷わない。
- 環境ごとに切り替えたいときは
-backend-config(init 時に外から渡す。変数とは別の仕組み)。ただし機密情報は入れず、認証は環境変数や AWS プロファイルで。 - 「ここだけ DRY にできない」には、たいてい読み込み順などの理由がある。仕様を知ると納得できる。
おわりに
全部変数でまとめたくなるけれど、backend だけは別腹でした😅 同じところで「あれ、変数が効かない」と戸惑った人の参考になれば嬉しいです🙌