Terraformのbackendブロックはinit時にしか評価されない|s3・gcs・httpで必須キーと認証情報の渡し方が変わる仕組み

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > Terraformのbackendブロックはinit時にしか評価されない|s3・gcs・httpで必須キーと認証情報の渡し方が変わる仕組み
「backendブロックにvar.regionと書いたらエラーになった。変数が使えないなら認証情報はどう渡せばいいのか」
「terraform initを一度実行した後でbackendの設定を変えたのにplanに反映されない。なぜか」

この2つの疑問は、backendブロックが「terraform initのタイミングにしか評価されない」という動作仕様から来ている。plan・applyの段階ではbackendブロックは読み直されない。そのため変数解決も行われず、設定変更も無視される。

この記事では、backendブロックの評価タイミングの仕組みを整理した上で、s3・gcs・httpの3種別それぞれの必須キーと認証情報の渡し方を解説する。どのbackendを選ぶべきかという選定比較は本記事の対象外とし、実装上の設定キーと認証の仕組みに絞って説明する。

この記事のポイント

・backendブロックはterraform init時にしか評価されない(planでは読まれない)
・backendブロックで変数は使えない。-backend-configで外から渡す
・s3はbucket/key/region、gcsはbucket、httpはaddressが必須キー
・認証情報はbackendに書かず環境変数かIAMロールで渡すのが標準


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

なぜbackendブロックはinit時にしか評価されないのか

Terraformの初期化フローを確認すると理由がわかる。terraform initはbackendブロックを読み取り、接続先のstateバックエンドを確立する。この結果は.terraform/ディレクトリ内の.terraform.tfstate(ローカルバックエンドメタデータ)やプロバイダーキャッシュとして保存される。

terraform planterraform applyはこの保存済みのバックエンド設定を使ってstateを読み書きする。backendブロックをplan時に再評価してしまうと、同一のstateに対して「どのバックエンドを使うか」が実行ごとに変わりうる状況になり、stateの一貫性が保証できなくなる。そのため設計上、backendブロックの評価はinitの1回に限定されている。

つまり、backendブロックを変更したらterraform initを再実行しなければならない。再実行しないと変更は反映されないまま、古いバックエンド設定が使われ続ける。

# backendブロックを変更した後は必ずinitを再実行する $ terraform init Initializing the backend... Do you want to copy existing state to the new backend? Pre-existing state was found while migrating the previous "s3" backend to the newly configured "gcs" backend. No existing state was found in the newly configured "gcs" backend. Do you want to copy this state to the new backend? Enter "yes" to copy and "no" to start with an empty state. Enter a value: yes

バックエンドを変更してinitを再実行すると、Terraformは既存のstateを新しいバックエンドにコピーするかどうか確認してくる。これもbackendブロックの変更がinit時に処理されることを示している。

backendブロックで変数・式が使えない制約と回避策

1. 変数が使えない理由とエラーメッセージ

backendブロックの評価はTerraformの初期化フェーズ(init)で行われる。このタイミングはHCLの変数解決やlocalsの評価より前であるため、var.xxxlocal.xxxをbackendブロックの中に書いてもTerraformは値を解決できない。

以下は変数を使おうとした場合に出るエラーの例だ。

# NG: backendブロックでvar.xxxを使おうとした場合 terraform { backend "s3" { bucket = var.state_bucket # これはエラーになる key = "prod/terraform.tfstate" region = var.aws_region # これもエラーになる } }

$ terraform init ╷ │ Error: Variables may not be used here │ │ on main.tf line 3, in terraform: │ 3: bucket = var.state_bucket │ │ Variables may not be used here. ╵

同様にlocal.xxxや文字列テンプレート("${var.env}-bucket"形式)も使えない。backendブロック内の値はリテラル文字列か、後述の-backend-configで外から注入する方式に限定される。

2. -backend-configオプションで外から値を注入する

変数の代わりに使うのがterraform init-backend-configフラグだ。このフラグでキーと値のペアをコマンドライン引数として渡すと、backendブロックの対応する設定を上書き・補完できる。

# -backend-configで環境ごとにbucketとkeyを切り替える例 $ terraform init \ -backend-config="bucket=mycompany-tfstate-prod" \ -backend-config="key=prod/terraform.tfstate" \ -backend-config="region=ap-northeast-1"

CI/CDパイプライン(GitHub Actions、GitLab CI等)では、環境変数で格納した値を-backend-configに展開して渡すパターンが多い。

# GitHub Actionsでの例(リポジトリ変数から値を展開) - name: Terraform Init run: | terraform init \ -backend-config="bucket=$TF_STATE_BUCKET" \ -backend-config="key=$TF_STATE_KEY" \ -backend-config="region=$AWS_REGION" env: TF_STATE_BUCKET: ${{ vars.TF_STATE_BUCKET }} TF_STATE_KEY: ${{ vars.TF_STATE_KEY }} AWS_REGION: ${{ vars.AWS_REGION }}

3. partial configurationでファイルに分離する

設定項目が多い場合は、backendブロックを空のまま(型だけ宣言)にして、設定ファイルを-backend-configで指定するpartial configurationが使いやすい。

# backend.tf - 型だけを宣言。必須キーは書かない terraform { backend "s3" {} }

# backend-prod.tfvars - 値だけを別ファイルに切り出す bucket = "mycompany-tfstate-prod" key = "prod/terraform.tfstate" region = "ap-northeast-1" dynamodb_table = "terraform-lock-prod" encrypt = true

# initでファイルごと渡す $ terraform init -backend-config=backend-prod.tfvars

この方式なら、backend-prod.tfvarsをgit管理外(.gitignore)に置いてもよいし、環境ごとにbackend-dev.tfvars/backend-stg.tfvars/backend-prod.tfvarsと分けて切り替えることもできる。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、TerraformのbackendブロックやCI/CD連携・状態管理設計まで、ハンズオン形式で習得できるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

S3 backendの必須キーと認証情報の渡し方

1. 必須キーと推奨キー

S3 backendの必須キーはbucketkeyregionの3つだ。dynamodb_tableは必須ではないが、チームで同一stateを操作する場合はロック制御のために設定しておくべきだ。

terraform { backend "s3" { bucket = "mycompany-tfstate" # 必須: S3バケット名 key = "prod/ec2/terraform.tfstate" # 必須: バケット内のパス region = "ap-northeast-1" # 必須: バケットのリージョン dynamodb_table = "terraform-lock" # 推奨: stateロック用 encrypt = true # 推奨: サーバーサイド暗号化 } }

keyは「どのプロジェクト・環境のstateか」を識別するパスになる。prod/ec2/terraform.tfstateのように階層を付けておくと、同一バケット内で複数のTerraform管理リソースを分離して管理できる。

2. AWS認証情報の渡し方

S3 backendはAWSのCredential Chainに従って認証情報を解決する。backendブロックにaccess_key/secret_keyを直接書く方法もあるが、コードに認証情報が残るため推奨しない。

# ローカル開発: 環境変数で渡す $ export AWS_ACCESS_KEY_ID="AKIA..." $ export AWS_SECRET_ACCESS_KEY="..." $ export AWS_DEFAULT_REGION="ap-northeast-1" $ terraform init Initializing the backend... Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes.

# GitHub Actions: aws-actions/configure-aws-credentialsでIAMロールを引き受ける - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/terraform-state-role aws-region: ap-northeast-1

~/.aws/credentialsが存在する場合はそちらも使える。プロファイルを指定したい場合はbackendブロックにprofile = "myprofile"を追記するか、環境変数AWS_PROFILEを設定する。

GCS backendの必須キーと認証情報の渡し方

1. 必須キーと推奨キー

GCS(Google Cloud Storage)backendの必須キーはbucketの1つだけだ。prefixを指定しないとdefault.tfstateというファイル名でバケット直下に保存される。環境ごとにstateを分けるためにprefixは実質的に必須になる。

terraform { backend "gcs" { bucket = "mycompany-tfstate" # 必須: GCSバケット名 prefix = "prod/terraform/state" # 推奨: バケット内のパスプレフィックス } }

GCS backendはロック機能をCloud Storageのオブジェクトロック(.tflockファイル)で実現するため、DynamoDBのような追加リソースは不要だ。

2. GCP認証情報の渡し方

GCS backendはApplication Default Credentials(ADC)を使って認証する。ローカル開発ではgcloud auth application-default loginが最もシンプルだ。

# ローカル開発: gcloudでADCを設定する $ gcloud auth application-default login Credentials saved to file: [/home/user/.config/gcloud/application_default_credentials.json] # または: サービスアカウントキーをJSONファイルで指定する場合 $ export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json" $ terraform init

# CI/CD: サービスアカウントのJSONをGOOGLE_CREDENTIALSに直接渡す場合 $ export GOOGLE_CREDENTIALS='{ "type": "service_account", "project_id": "myproject", ... }' $ terraform init

GOOGLE_CREDENTIALSにJSONファイルのパスではなくJSON文字列そのものを渡す方法は、CIでシークレットとして扱いやすい。GKEやCloud Runで実行する場合はWorkload Identityが使えるため認証情報のファイル管理が不要になる。

HTTP backendの必須キーと認証情報の渡し方

HTTP backendはRESTful APIでstateを読み書きするバックエンドで、GitLab Managed Terraform State等に使われる。

1. 必須キーと任意キー

HTTP backendの必須キーはaddressの1つだけだ。stateロック機能を使う場合はlock_addressunlock_addressも設定する。

terraform { backend "http" { address = "https://gitlab.example.com/api/v4/projects/42/terraform/state/prod" lock_address = "https://gitlab.example.com/api/v4/projects/42/terraform/state/prod/lock" unlock_address = "https://gitlab.example.com/api/v4/projects/42/terraform/state/prod/lock" lock_method = "POST" # デフォルト: LOCK unlock_method = "DELETE" # デフォルト: UNLOCK } }

2. 認証情報の渡し方(TF_HTTP_USERNAME/PASSWORD)

HTTP backendへの認証はbackendブロックのusername/passwordまたは対応する環境変数で設定する。変数が使えないbackendブロックに認証情報を直接書くとコードに残ってしまうため、環境変数を使う方が安全だ。

# TF_HTTP_*環境変数で渡す(backendブロックには書かない) $ export TF_HTTP_USERNAME="myuser" $ export TF_HTTP_PASSWORD="mytoken" # GitLabならPersonal Access Token $ terraform init

# GitLab CI/CDの場合: CI変数を展開して渡す $ export TF_HTTP_ADDRESS="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}" $ export TF_HTTP_LOCK_ADDRESS="${TF_HTTP_ADDRESS}/lock" $ export TF_HTTP_UNLOCK_ADDRESS="${TF_HTTP_ADDRESS}/lock" $ export TF_HTTP_USERNAME="gitlab-ci-token" $ export TF_HTTP_PASSWORD="${CI_JOB_TOKEN}" $ terraform init

よくあるエラーと対処法

「Variables may not be used here」
backendブロックでvar.xxxlocal.xxxを使おうとしたときのエラー。-backend-configかpartial configurationに切り替えること。

「Backend initialization required」
backendブロックの内容を変更したのにterraform initを再実行していない状態でplanを実行すると発生する。terraform initを再実行することで解消する。

$ terraform plan ╷ │ Error: Backend initialization required, please run "terraform init" │ │ Reason: Backend configuration changed for "s3" ╵ # 対処: terraform initを再実行する $ terraform init

「Failed to get existing workspaces」または「NoSuchBucket」
S3 backendでバケット名・リージョン・認証情報のいずれかが誤っている場合に出る。aws s3 ls s3://バケット名 --region リージョンでバケットへのアクセスを確認してから再実行する。

GCS backendで「google: could not find default credentials」
Application Default Credentialsが設定されていない状態。gcloud auth application-default loginGOOGLE_APPLICATION_CREDENTIALSの設定が必要だ。

本記事のまとめ

backendの種別 必須キー 認証情報の渡し方
s3 bucket / key / region 環境変数(AWS_ACCESS_KEY_ID等)またはIAMロール
gcs bucket GOOGLE_APPLICATION_CREDENTIALS またはADC
http address TF_HTTP_USERNAME / TF_HTTP_PASSWORD 環境変数
backendブロックはterraform initのときにしか評価されない。この仕様から3つの実装上のルールが導かれる。

・変数・式はbackendブロックに書けない → -backend-configかpartial configurationで渡す
・backendブロックを変更したらterraform initを必ず再実行する
・認証情報はbackendブロックに直書きせず環境変数かIAMロールで渡す

この3点を押さえれば、s3・gcs・http、どのbackend種別でも設定上のハマりどころを避けて実装できる。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、TerraformのbackendブロックやCI/CD連携・状態管理設計まで、ハンズオン形式で習得できるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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