December 2, 2020·6 min read·engineeringterraformdependenciessecurity

Terraform records provider selections in a dependency lock file

Terraform 0.14 records provider versions and package checksums in .terraform.lock.hcl. It also propagates sensitive input values through expressions while retaining those values in state.

A provider version constraint describes a set of acceptable versions. It does not record which member of that set an earlier initialization selected. Terraform 0.14 adds that missing record, so repeating initialization can reuse the same choice even after a newer acceptable version appears.

The December 2 release introduces the dependency lock file alongside sensitive input variables and expression-level sensitivity propagation. Both changes preserve information needed later in a workflow. The lock file retains a dependency decision, while sensitivity metadata follows values into expressions that derive from them.

I value explicit provider selection because Terraform's reusable configuration is only part of the program being executed. Providers supply the implementations of its infrastructure operations. Recording their selection makes a change in those implementations something a reviewer can identify separately from a change in resource arguments.

Requirements and selections have different responsibilities

The versioned lock-file documentation describes .terraform.lock.hcl as belonging to the whole configuration. Terraform creates it in the root working directory. It is distinct from both the configuration's .tf files and the cached material in .terraform.

Initialization considers the configured version constraints and any selections already recorded. Without a recorded selection for a provider, Terraform chooses the newest available version satisfying the constraints and records it. With a recorded selection, it reuses that version instead of automatically moving to a newer acceptable one.

The upgrade operation is explicit. Terraform provides an initialization option for deliberately reconsidering the recorded selections while retaining the configuration's compatibility requirements.

terraform init -upgrade

That command disregards existing selections and considers the newest versions allowed by the configuration. It does not remove the constraints. Broadening an acceptable range and selecting a newer member of the range remain separate decisions.

This distinction improves the meaning of a dependency review. A configuration can retain the same compatibility constraint while its lock file records a newer selected provider. The source-level claim of acceptable versions remains unchanged, but the executable dependency used for subsequent runs changes.

The lock file covers providers in this release

The documentation explicitly limits the initial implementation to provider dependencies. It does not remember remote-module version selections. Terraform continues selecting the newest available module version satisfying its configured constraint.

For a remote module whose version must remain fixed, the documented option is an exact version constraint. A provider lock file therefore cannot be presented as complete dependency locking for every piece of external configuration.

That scope is relevant when comparing two fresh working directories. Matching provider selections establish one important part of their inputs. They do not establish that a floating remote-module constraint selected identical module code on both occasions.

The file's configuration-wide scope also differs from requirement declarations. Modules declare the providers they require, but Terraform resolves one set of selections for the configuration using them. The lock record explains the result of considering those requirements together.

Package checksums add another identity check

A selected version identifies the intended release, while checksums identify acceptable packages for it. Terraform verifies installed packages against previously recorded checksums and reports an error when none matches. This gives the lock file a role beyond choosing a version number.

The documentation calls this trust on first use. The first recorded package requires an initial trust decision. Later initialization can detect a package that differs from the accepted record. A checksum cannot establish that the first package was appropriate merely by being written to a file.

Origin registries can provide cryptographically signed checksum sets. After verifying an installed package against that set, Terraform can record other signed package checksums too. This commonly gives the lock file information about packages for additional platforms.

The CLI reports signing-key information during this process. That identifies the signer whose checksum set is being accepted. It is useful evidence for reviewing the initial record, while the later package check compares downloads against the record already accepted.

These mechanisms answer different questions. A version selection identifies which release the configuration uses. Signature verification associates checksum information with a signing key. Checksum verification determines whether the obtained package matches an accepted digest.

Why two checksum prefixes can appear

The documentation describes two schemes, zh: and h1:. The former hashes the distribution ZIP archive. By comparison, the latter hashes package contents in a way that also works for equivalent unpacked files or recompressed archives.

Both schemes use SHA256, but they hash different representations. An archive can differ in metadata or compression while containing the same files. A content-based scheme can recognize that equivalence where an archive-byte checksum cannot.

Terraform may add an h1: checksum after verifying a package through an existing checksum. That allows the record to gain a content checksum for another platform without selecting a new provider version. A lock-file diff containing an additional hash consequently does not necessarily represent an upgrade.

This is useful information for reviewing generated changes. A version change replaces the selected release and normally its package digests. A checksum addition may record more verification information for the same release. The reviewer needs to distinguish those cases by examining the entry.

Mirrors and multiple platforms need sufficient checksum information

The documentation distinguishes an initial installation through an origin registry from one through a filesystem or network mirror. The mirror path may not provide the official signed checksums for other platforms. A record learned only from one platform can therefore lack the information another platform needs.

The documented terraform providers lock command can pre-populate checksums for selected platforms using the origin registry's packages. An example targeting Linux and macOS uses the following options.

terraform providers lock -platform=linux_amd64 -platform=darwin_amd64

That operation establishes verification information before another machine initializes from the chosen distribution arrangement. It does not require changing the provider's source identity merely because the package is obtained through a mirror.

I find this separation useful for infrastructure repositories shared across operating systems. A dependency decision should include enough package evidence for the intended execution platforms. Otherwise, a correct version selection can still leave an avoidable verification gap when the workflow moves between machines.

Sensitivity follows derived values

The release changelog introduces sensitive input variables and propagation through derived expressions. An output that references a sensitive value must itself declare sensitivity, or planning fails.

A provider-free example makes the declaration boundary visible. Both declarations describe sensitivity, while the value expression describes the actual data relationship between the input and output.

variable "credential" {
  type      = string
  sensitive = true
}

output "credential_copy" {
  value     = var.credential
  sensitive = true
}

The output has an explicit annotation even though its value comes directly from an annotated input. That declaration prevents an accidental module interface from silently exposing information Terraform knows is sensitive.

The versioned variable documentation explains the user-visible effect. Terraform suppresses the value in ordinary plan and apply output. In some nested-block cases, it redacts an entire block because one of its values is sensitive.

That can make a plan show less surrounding detail. The behavior follows the data and the structure used to represent it, so sensitivity is more than a formatting option on one input prompt.

Redaction does not change persistence or provider inputs

The variable documentation also states that sensitive values remain in state. Anyone able to read that state can access them. The annotation therefore does not establish encrypted persistence or make state safe to disclose.

Providers also receive the actual values. A provider error can include a sensitive value in its message, and a provider-defined resource identifier can reveal a value used to construct it. The documentation gives these as cases where the ordinary display suppression cannot cover every disclosure route.

This limit belongs in the feature's explanation because it defines what sensitivity means operationally. Terraform can propagate metadata through its own expressions and use that metadata in its output. External systems and provider diagnostics still have their own representations of the same data.

The release also retains experimental provider-attribute sensitivity propagation as a separate option. Ordinary sensitive input variables should not be confused with a promise that every provider-declared sensitive attribute automatically participates under every configuration.

Terraform 0.14 makes provider selection and sensitive-value handling more explicit at useful boundaries. The dependency file records versions and acceptable packages for later initialization. Sensitivity annotations guide display and module outputs while the underlying values continue to pass to providers and state storage.