🔖

terraform init は何をやっているのか?

2025/02/07に公開

「terraform init っていつ実行すべきなの?」という疑問を持ったことがある人は多いかと思います。terraform のコードを書き換えて terraform plan で差分を確認する前に terraform init を実行すべきか否か、判断が付かないので取り敢えず毎回 terraform init を実行している人もいると思います。

この記事では、terraform init がやっていることを解説しつつ、どのようなケースで terraform init を実行する必要があるのかを見ていきたいと思います。

なお terraform のバージョンは v1.10.0 を前提にします。

TL;DR

  • tfstate backend を変更した場合
  • required_providers の version を上げた場合
  • module を追加した場合
  • module のバージョンを変更した場合

terraform init がやっていること

細かいことを挙げるときりがありませんが、おおまかに言うと次の4つです。

  • backend の設定
  • module のインストール
  • provider のインストール
  • (存在しない場合).terraform.lock.hcl の生成

各ステップについて詳しく見ていきます。

backend の設定

terraform block から取得した内容をもとに tfstate の backend を決定します。

以下のような terraform block だと s3 を backend に設定します。

terraform {
  required_version = "~> 1.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    bucket = "hogehoge"
    key    = "terraform.tfstate"
    region = "ap-northeast-1"
  }
}

また .terraform/terraform.tfstate に backend の情報を保存します。これは backend の保存場所をどこに設定しているかに関わらず保存されます。

{
  "version": 3,
  "terraform_version": "1.10.0",
  "backend": {
    "type": "s3",
    "config": {
      "access_key": null,
      "acl": null,
      "allowed_account_ids": null,
      "assume_role": null,
      "assume_role_with_web_identity": null,
      "bucket": "hogehoge",
...

このように terraform は前回の backend 情報を保存しているため、変更して terraform init すると以下エラーになります。

❯ git diff provider.tf 
diff --git a/provider.tf b/provider.tf
index c4d3a61..6b97579 100644
--- a/provider.tf
+++ b/provider.tf
@@ -8,10 +8,8 @@ terraform {
     }
   }
 
-  backend "s3" {
-    bucket = "hogehoge"
-    key    = "terraform.tfstate"
-    region = "ap-northeast-1"
+  backend "local" {
+    path = "terraform.tfstate"
   }
 }
│ 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".

terraform init を実行するたびに .terraform/terraform.tfstate に記載されている backend の情報と実際の backend の情報からそれぞれハッシュ値を計算し、その2つが一致しているかを確認しています。一致しない場合は上記のようなエラーを吐いて backend の情報を上書きするようメッセージを出します(該当コード)。

backend を変更したい場合、前回実行時の backend の中身を引き継ぎたい場合は terraform init -migrate-state を、単に backend の情報を変えたいだけの時は terraform init -reconfigure を実行します。

ちなみに .terraform/ を削除してから terraform init を実行すると terraform init -reconfigure と同等の動きになります。

module のインストール

以下のように module block が定義されている場合、source に応じて module をインストールします。この例の場合は terraform registry(registry.terraform.io) からダウンロードしてきます。

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"

  name = "hoge-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
}

module に関する情報は .terraform/modules/modules.json に以下のように保存されています。

❯ cat .terraform/modules/modules.json | jq
{
  "Modules": [
    {
      "Key": "",
      "Source": "",
      "Dir": "."
    },
    {
      "Key": "vpc",
      "Source": "registry.terraform.io/terraform-aws-modules/vpc/aws",
      "Version": "5.17.0",
      "Dir": ".terraform/modules/vpc"
    }
  ]
}

terraform plan 実行時に modules.json の内容とインストールした module のディレクトリの source address や version に合致するかをチェックします(厳密にはこの内容をチェックしています)。

provider のインストール

.terraform.lock.hcl が存在する場合は記載されている version の provider をインストールします。例えば以下のような .terraform.lock.hcl がある場合は aws provider v5.84.0 をインストールします。

provider "registry.terraform.io/hashicorp/aws" {
  version     = "5.84.0"
  constraints = "~> 5.0"
  hashes = [
    "h1:TpsSFMkiuLC1V29n4Ov99g4L6jlsBmBMWxqDX3GZNww=",
    "zh:078f77438aba6ec8bf9154b7d223e5c71c48d805d6cd3bcf9db0cc1e82668ac3",
    "zh:1f6591ff96be00501e71b792ed3a5a14b21ff03afec9a1c4a3fd9300e6e5b674",
    "zh:2ab694e022e81dd74485351c5836148a842ed71cf640664c9d871cb517b09602",
    "zh:33c8ccb6e3dc496e828a7572dd981366c6271075c1189f249b9b5236361d7eff",
    "zh:6f31068ebad1d627e421c72ccdaafe678c53600ca73714e977bf45ff43ae5d17",
    "zh:7488623dccfb639347cae66f9001d39cf06b92e8081975235a1ac3a0ac3f44aa",
    "zh:7f042b78b9690a8725c95b91a70fc8e264011b836605bcc342ac297b9ea3937d",
    "zh:88b56ac6c7209dc0a775b79975a371918f3aed8f015c37d5899f31deff37c61a",
    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
    "zh:a1979ba840d704af0932f8de5f541cbb4caa9b6bbd25ed552a24e6772175ba07",
    "zh:b058c0533dae580e69d1adbc1f69e6a80632374abfc10e8634d06187a108e87b",
    "zh:c88610af9cf957f8dcf4382e0c9ca566ef10e3290f5de01d4d90b2d81b078aa8",
    "zh:e9562c055a2247d0c287772b55abef468c79f8d66a74780fe1c5e5dae1a284a9",
    "zh:f7a7c71d28441d925a25c08c4485c015b2d9f0338bc9707443e91ff8e161d3d9",
    "zh:fee533e81976d0900aa6fa443dc54ef171cbd901847f28a6e8edb1d161fa6fde",
  ]
}

.terraform.lock.hcl が存在しない場合は terraform block の required_providers をもとにインストールします。例えば以下のような terraform block の場合、 aws provider v5系の最新をインストールします。

terraform {
  required_version = "~> 1.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

インストールした provider は 以下のように .terraform/providers 配下に保存されます。

❯ tree .terraform/providers/
.terraform/providers/
└── registry.terraform.io
    └── hashicorp
        └── aws
            └── 5.84.0
                └── darwin_amd64 -> /Users/sho/.terraform.d/plugin-cache/registry.terraform.io/hashicorp/aws/5.84.0/darwin_amd64

あまり無いケースだとは思いますが、.terraform/providers が削除された状態で terraform plan を実行すると以下エラーになります。

❯ terraform plan
╷
│ Error: Required plugins are not installed
│ 
│ The installed provider plugins are not consistent with the packages selected in the dependency lock file:
│   - registry.terraform.io/hashicorp/aws: there is no package for registry.terraform.io/hashicorp/aws 5.84.0 cached in .terraform/providers
│ 
│ Terraform uses external plugins to integrate with a variety of different infrastructure services. To download the plugins required for this configuration, run:
│   terraform init

結局 terraform init はいつ実行する必要があるのか?

まとめると以下のケースになります。

  • tfstate backend を変更した場合
  • required_providers の version を上げた場合
  • module を追加した場合
  • module のバージョンを変更した場合

「required_providers の version を上げた場合」が少し分かりづらいと思うので補足します。

provider のバージョンは上述の通り .terraform.hcl.lock の設定内容が利用されます。一方で terraform block の required_providers の設定も見ており、インストールされた provider が version 制約を満たしているかもチェックしています。

例えば以下のような .terraform.hcl.lock で terraform init したとします。この場合 aws provider v4.67.0 がインストールされます。

provider "registry.terraform.io/hashicorp/aws" {
  version     = "4.67.0"
  constraints = "~> 4.0"
  hashes = [
    "h1:P43vwcDPG99x5WBbmqwUPgfJrfXf6/ucAIbGlRb7k1w=",
    "zh:0843017ecc24385f2b45f2c5fce79dc25b258e50d516877b3affee3bef34f060",
    "zh:19876066cfa60de91834ec569a6448dab8c2518b8a71b5ca870b2444febddac6",
    "zh:24995686b2ad88c1ffaa242e36eee791fc6070e6144f418048c4ce24d0ba5183",
    "zh:4a002990b9f4d6d225d82cb2fb8805789ffef791999ee5d9cb1fef579aeff8f1",
    "zh:559a2b5ace06b878c6de3ecf19b94fbae3512562f7a51e930674b16c2f606e29",
    "zh:6a07da13b86b9753b95d4d8218f6dae874cf34699bca1470d6effbb4dee7f4b7",
    "zh:768b3bfd126c3b77dc975c7c0e5db3207e4f9997cf41aa3385c63206242ba043",
    "zh:7be5177e698d4b547083cc738b977742d70ed68487ce6f49ecd0c94dbf9d1362",
    "zh:8b562a818915fb0d85959257095251a05c76f3467caa3ba95c583ba5fe043f9b",
    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
    "zh:9c385d03a958b54e2afd5279cd8c7cbdd2d6ca5c7d6a333e61092331f38af7cf",
    "zh:b3ca45f2821a89af417787df8289cb4314b273d29555ad3b2a5ab98bb4816b3b",
    "zh:da3c317f1db2469615ab40aa6baba63b5643bae7110ff855277a1fb9d8eb4f2c",
    "zh:dc6430622a8dc5cdab359a8704aec81d3825ea1d305bbb3bbd032b1c6adfae0c",
    "zh:fac0d2ddeadf9ec53da87922f666e1e73a603a611c57bcbc4b86ac2821619b1d",
  ]
}

この状態で required_providers を以下のように変更すると terraform plan でエラーになります(terraform init --upgrade する必要がある)。

❯ g diff provider.tf 
diff --git a/provider.tf b/provider.tf
index c4d3a61..6172cfe 100644
--- a/provider.tf
+++ b/provider.tf
@@ -4,7 +4,7 @@ terraform {
   required_providers {
     aws = {
       source  = "hashicorp/aws"
-      version = "~> 4.0"
+      version = "~> 5.0"
     }
   }
❯ terraform plan
╷
│ Error: Inconsistent dependency lock file
│ 
│ The following dependency selections recorded in the lock file are inconsistent with the current configuration:
│   - provider registry.terraform.io/hashicorp/aws: locked version selection 4.67.0 doesn't match the updated version constraints "~> 5.0"
│ 
│ To update the locked dependency selections to match a changed configuration, run:
│   terraform init -upgrade

まとめ

以上が terraform int が必要なケースになります。細かい点を言うともう少しあるのですが、実際に利用していて遭遇するケースはカバーできていると思います。

terraform init && terraform plan のように脳死で毎回実行するのも手ですが、以外に時間がかかるのでローカルで何度も試行錯誤する時は意識してみると良いと思います。

Discussion