Component · Init
Prepares the working directory by installing modules and provider plugins, then writing a dependency lock file. No cloud APIs are called for infrastructure.
InitCommand.Run() [command/init.go:50] │ ├─► Parse flags & arguments ├─► c.initConfigLoader() — create configload.Loader ├─► loadConfig(path) — parse *.tf files in dir │ ├─── MODULES ─────────────────────────────────────────────────── │ installModules(ctx, path, testsDir, upgrade) │ └─► ModuleInstaller.InstallModules() [initwd/module_install.go:66] │ │ │ ├── Walk root *configs.Module.ModuleCalls │ │ Each ModuleCall has a Source (registry, git, local) │ │ and a VersionConstraint │ │ │ ├── For registry sources: │ │ registry.Client.ModuleVersions() → available versions │ │ Select highest version matching constraint │ │ registry.Client.ModuleLocation() → download URL │ │ │ ├── Download & unpack to .terraform/modules// │ │ │ ├── Recurse into child modules (BFS) │ │ │ └── Write .terraform/modules/modules.json │ { "Modules": [ { "Key": ".", "Source": "...", "Dir": "..." } ] } │ ├─── PROVIDERS ───────────────────────────────────────────────── │ getProviders(ctx, config, state, upgrade) │ │ │ ├─► Build requirements map from *configs.Config │ │ RequiredProvider blocks → map[addrs.Provider]version.Constraints │ │ │ ├─► getproviders.Source.AvailableVersions(provider) │ │ Default: Terraform Registry registry.terraform.io │ │ Alternatives: local mirror, network mirror │ │ │ ├─► Version selection (highest matching all constraints) │ │ │ ├─► getproviders.Source.PackageMeta(provider, version, platform) │ │ → download URL + expected SHA256 │ │ │ ├─► HTTP download → .terraform/providers/registry.terraform.io/…/ │ │ │ └─► Verify SHA256 checksum │ └─── LOCK FILE ───────────────────────────────────────────────── depsfile.Locks.SetProviderLocked(provider, version, hashes) └─► Write .terraform.lock.hcl provider "registry.terraform.io/hashicorp/aws" { version = "5.0.0" constraints = ">= 4.0, < 6.0" hashes = [ "h1:...", "zh:..." ] }
The InitCommand struct lives in internal/command/init.go.
Its Run method is the first thing called after the CLI routes
terraform init.
internal/command/init.go:44–111
GitHub
type InitCommand struct {
Meta
// ...
}
func (c *InitCommand) Run(args []string) int {
// Parse flags (upgrade, backend, reconfigure, etc.)
// ...
// Load configuration from the target directory
path, err := modulePath(args)
// ...
// Install modules from source addresses in config
installAbort, installDiags := c.installModules(ctx, path, testsDir,
upgrade, false, hooks)
diags = diags.Append(installDiags)
if installAbort || diags.HasErrors() {
return 1
}
// ...
}
ModuleInstaller in internal/initwd/module_install.go is
responsible for walking the module call graph and downloading each external module
into the local .terraform/modules/ cache.
internal/initwd/module_install.go:35–64
GitHub
type ModuleInstaller struct {
modsDir string // .terraform/modules/
loader *configload.Loader
reg *registry.Client
// caches of registry responses
registryPackageVersions map[addrs.ModuleRegistryPackage]*response.ModuleVersions
registryPackageSources map[addrs.ModuleRegistryPackage]map[string]*response.ModuleSource
// ...
}
// InstallModules walks the module call graph breadth-first,
// downloading each remote source into modsDir.
func (i *ModuleInstaller) InstallModules(
ctx context.Context,
rootDir string,
testsDir string,
upgrade bool,
installErrsOnly bool,
hooks ModuleInstallHooks,
) (*configs.Config, tfdiags.Diagnostics)
registry.terraform.io/namespace/module/provider — fetched via the Terraform Registry APIgit::https://… — cloned with go-getter./modules/vpc — symlinked or copiedAfter installation, the module manifest is written to .terraform/modules/modules.json.
The config loader reads this at runtime to locate each module's source directory.
.terraform/modules/modules.json (example)
{
"Modules": [
{
"Key": "",
"Source": "",
"Dir": "."
},
{
"Key": "vpc",
"Source": "registry.terraform.io/terraform-aws-modules/vpc/aws",
"Version": "5.1.0",
"Dir": ".terraform/modules/vpc"
}
]
}
Provider binaries are discovered via the getproviders.Source interface
and cached in .terraform/providers/. The default source is the
Terraform Registry at registry.terraform.io.
internal/getproviders/package_authentication.go + source interfaces
GitHub
// Source is the interface for provider package discovery.
// Implementations: RegistrySource, FilesystemMirrorSource, MultiSource.
type Source interface {
AvailableVersions(ctx context.Context, provider addrs.Provider) (
VersionList, Warnings, error)
PackageMeta(ctx context.Context, provider addrs.Provider,
version Version, target Platform) (PackageMeta, error)
}
// PackageMeta describes a single provider binary package.
type PackageMeta struct {
Provider addrs.Provider
Version Version
TargetPlatform Platform // e.g. "linux_amd64"
Filename string
Location PackageLocation // URL or local path
Authentication PackageAuthentication
}
.terraform/providers/ (directory structure)
.terraform/providers/
└── registry.terraform.io/
└── hashicorp/
└── aws/
└── 5.0.0/
└── darwin_arm64/
└── terraform-provider-aws_v5.0.0_x5
After resolving and downloading providers, init writes
.terraform.lock.hcl using depsfile.Locks.
This file should be committed to version control to ensure reproducible installs.
internal/depsfile/locks.go
GitHub
// Locks records the locked provider selections.
type Locks struct {
providers map[addrs.Provider]*ProviderLock
// ...
}
type ProviderLock struct {
addr addrs.Provider
version getproviders.Version
constraints getproviders.VersionConstraints
hashes []getproviders.Hash
}
.terraform.lock.hcl (example)
provider "registry.terraform.io/hashicorp/aws" {
version = "5.0.0"
constraints = ">= 4.0.0, < 6.0.0"
hashes = [
"h1:AbCdEf...",
"zh:1234567890abcdef...",
]
}
Two hash formats:
h1: is a hash of the zip archive contents (used for the current platform).
zh: is a hash of a list of hashes for all platforms — used to verify
cross-platform consistency in CI environments.