how to install provider when terraform init

- terraform の 対象 version は v1.10
- install 全般を調べきるのは大変なので興味あるとこだけ

provider version の管理方法は terraform block と .terraform.hcl.lock を利用する2種類の方法がある
There are two ways for you to manage provider versions in your configuration.
- Specify provider version constraints in your configuration's terraform block.
- Use the dependency lock file

.terraform.lock.hcl に selection recorded があればそのバージョンに固定される
If a particular provider already has a selection recorded in the lock file, Terraform will always re-select that version for installation, even if a newer version has become available.
selection recorded は以下の version のこと(=5.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",
]
}

無邪気に version を書き換えると checksum との不一致で terraform init が落ちる
❯ g diff .terraform.lock.hcl
diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl
index b454c00..eb7f6e8 100644
--- a/.terraform.lock.hcl
+++ b/.terraform.lock.hcl
@@ -2,7 +2,7 @@
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
- version = "5.84.0"
+ version = "5.74.0"
constraints = "~> 5.0"
hashes = [
"h1:TpsSFMkiuLC1V29n4Ov99g4L6jlsBmBMWxqDX3GZNww=",
❯ t init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v5.74.0...
╷
│ Warning: Provider development overrides are in effect
│
│ The following provider development overrides are set in the CLI configuration:
│ - hashicorp.com/edu/hashicups-pf in /Users/sho/go/bin
│
│ Skip terraform init when using provider development overrides. It is not necessary and may error unexpectedly.
╵
╷
│ Error: Failed to install provider
│
│ Error while installing hashicorp/aws v5.74.0: the current package for registry.terraform.io/hashicorp/aws 5.74.0 doesn't match any of the checksums previously recorded in the
│ dependency lock file; for more information: https://www.terraform.io/language/provider-checksum-verification

ちなみに .terraform.lock.hcl が存在しない状態で terraform init すると以下のようなログになる
❯ t init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.84.0...
- Installed hashicorp/aws v5.84.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
╷
│ Warning: Provider development overrides are in effect
│
│ The following provider development overrides are set in the CLI configuration:
│ - hashicorp.com/edu/hashicups-pf in /Users/sho/go/bin
│
│ Skip terraform init when using provider development overrides. It is not necessary and may error unexpectedly.
╵
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

この辺りの log message は以下で管理してる

log message の出し分けロジックは以下

locked の有無はどう制御しているのか?
次の二箇所で制御している。一箇所目はエッジケースなので無視

ポイントはここの if 文に合致するかで、ここに合致すると locked = true になって reusing_previous_version_info
log message が出力される

どういうケースで locked = false になるのか?
locked = false
になり得るのはここだけ。つまり mightNeed が 1個以上で loop が回り、locked[provider] = false
が代入されるケース
mightNeed はここで追加される
なのでまずはこの loop に入ることが前提
reqs
の定義は以下。恐らく terraform block と .terraform.lock.hcl の両方を merge してこの変数に入れてるのだと思う
ここで locked[provider] = true
が代入されているので、このブロックを skip すると locked[provider] が初期値のまま以降の処理が実行されて結果 locked = false
になる
以降 locked[provider] = false
になる具体的なケースを見る

1. InstallMode == InstallUpgrades になるケース
InstallMode == InstallUpgrades だと以下のブロックを丸ごと skip する
--upgrade
オプションを指定した場合に InstallMode == InstallUpgrades になるっぽい

2. .terraform.lock.hcl から provider 情報が取得できないケース
locks.Provider(provider)
の結果が nil の場合は以下ブロックが skip される
最もよくあるのは .terraform.lock.hcl が存在しないパターン(他のパターンはちょっと思いつかない)

log message の出力としては以上だが、実際にどうやって install しているのか?

install はこの loop で provider ごとに行っている
最初に .terraform 配下をチェックして provider が install 済みの場合は continue して以降の install 処理を実行しない

次に globalCacheDir が設定されているかをチェックする(例:TF_PLUGIN_CACHE_DIR が設定されているケース)。globalCacheDir が設定されていて、globalCache が存在する場合は専用の install 処理を開始する
globalCache provider が .terraform.lock.hcl のいずれかの hash に match してるかをチェック

match のロジックは以下。globalCache provider はバイナリで置かれているので、HashScheme1 とマッチする。なので .terraform.hcl.lock から h1 ハッシュを削除するとマッチせず globalCache からの install を諦めて HTTP 経由で install する

いずれかの hash に match した場合は以下で install を実行する。install の中身としては globalCache を .terraform 配下に copy してる

次に globalCache が無い場合を見ていく
まずは install の前段階として packageMeta を取得する
terraform registry に HTTP リクエストを投げて取得した以下レスポンスを parse して packageMeta に代入している
該当箇所の DEBUG log の例
2025-01-25T20:54:16.930+0900 [DEBUG] GET https://registry.terraform.io/v1/providers/hashicorp/aws/5.84.0/download/darwin/amd64
また後で provider を download した際の認証で利用するをここで設定する

provider の install はここで行っている
meta.Location に応じて install 方法が分かれている。getproviders.PackageHTTPURL 以外のケースはどういう時に使われるのか良くわからない(filesystem mirror 使ってる時?)。が、普段遣いしないので無視して、installFromHTTPURL() の内部実装を見ていく

terraform init の TRACE log からも分かるが terraform registry から provider download する場合は zip ファイルを download している
2025-01-25T19:31:26.523+0900 [TRACE] HTTP client GET request to https://releases.hashicorp.com/terraform-provider-aws/5.84.0/terraform-provider-aws_5.84.0_darwin_amd64.zip
zip ファイルを download すると認証を実行して、ファイルの妥当性をチェックする
認証は三段階で処理される
- https://github.com/hashicorp/terraform/blob/f7640b36f3c4951e279e377f7bcdc72e2a7436e4/internal/getproviders/package_authentication.go#L341
- https://github.com/hashicorp/terraform/blob/f7640b36f3c4951e279e377f7bcdc72e2a7436e4/internal/getproviders/package_authentication.go#L294
- https://github.com/hashicorp/terraform/blob/f7640b36f3c4951e279e377f7bcdc72e2a7436e4/internal/getproviders/package_authentication.go#L403

最初の2つは hash の比較で、1つ目は terraform registry が提供している SHA-256 と一致してるかを見る。packageMeta で取得しておいた "shasums_url":"https://releases.hashicorp.com/terraform-provider-aws/5.84.0/terraform-provider-aws_5.84.0_SHA256SUMS"
からファイルをダウンロードする。中身は以下のようになっていて、 SHA-256 のハッシュ値と zip ファイル名が列挙されている
b058c0533dae580e69d1adbc1f69e6a80632374abfc10e8634d06187a108e87b terraform-provider-aws_5.84.0_darwin_amd64.zip
078f77438aba6ec8bf9154b7d223e5c71c48d805d6cd3bcf9db0cc1e82668ac3 terraform-provider-aws_5.84.0_darwin_arm64.zip
6f31068ebad1d627e421c72ccdaafe678c53600ca73714e977bf45ff43ae5d17 terraform-provider-aws_5.84.0_freebsd_386.zip
e9562c055a2247d0c287772b55abef468c79f8d66a74780fe1c5e5dae1a284a9 terraform-provider-aws_5.84.0_freebsd_amd64.zip
a1979ba840d704af0932f8de5f541cbb4caa9b6bbd25ed552a24e6772175ba07 terraform-provider-aws_5.84.0_freebsd_arm.zip
1f6591ff96be00501e71b792ed3a5a14b21ff03afec9a1c4a3fd9300e6e5b674 terraform-provider-aws_5.84.0_linux_386.zip
ここから対象の zip ファイル名のハッシュ値とその手前で terraform registry から取得してきたハッシュ値が一致してるかをチェックしている
「このチェックいるか?」という気もするが...一応コメントに理由書かれているがよくわからない

3つ目は疲れたので skip

認証をクリアしたらハッシュ値が .terraform.hcl.lock に列挙されているハッシュのいずれかにマッチするかを確認(これは認証とは別らしい)

最後に unzip しつつ .terraform 配下の所定のディレクトリ位置に provider を置いて install 完了

version 決定の仕組みはどうなっているのか?

まず terraform ファイルから terraform block の required_providers を parse して configs.File に入れる
configs.File の型は以下

上で config.File に格納した RequiredProviders を configs.Module に引き回す
configs.Module の定義

その後 getProviders() を呼び出す(ここが provider install の entry point 的な場所になっている)
.terraform.lock.hcl はここで parse している

最終的にここで install version を決定している
流れはざっと以下の通り
- required_providers から与えられた versionConstraints が適切な値かチェックしつつ parse して Set 型に代入
-
--upgrade
オプションを指定しておらず、かつ .terraform.lock.hcl 対象の provider 情報がある場合は記載の version を acceptableVersions として固定する - 「2」に合致しない場合は required_providers の versionConstraints を acceptableVersions として設定する
つまり .terraform.lock.hcl の設定を優先し、存在しない場合は required_providers の設定が効くようになっている

以下エッジケース
- .terraform.lock.hcl が存在せず required_providers 側に version 指定が無い場合
- latest version の provider が install される
- .terraform.lock.hcl はあるが required_providers が無い場合
- .terraform.lock.hcl に記載されている provider version が install される
- .terraform.lock.hcl と required_providers 両方とも存在しない場合
- provider block で指定した provider の latest version が install される