Hedronite · Cert Lesson · Cert-Prep / HashiCorp · Thu 2026-10-08

Terraform Associate provider version constraints, the dependency lock file, and init -upgrade

The constraint says what you allow. The lock file says what you got. Init obeys the lock until you say -upgrade.

Lesson Class: Cert (HashiCorp Terraform Associate 003)
Objective: Terraform basics: install and version providers · how Terraform finds and fetches providers
Verified: terraform 1.16.5 on the box · hashicorp/random 3.6.3 locked · init refusal reproduced · -upgrade to 3.9.1 · providers lock 2 platforms
Drills: 6 tap-to-reveal questions
Paired Ops: Terraform GCP Cloud KMS rotation census
Paired Dev: Rust TF integration tests over validate -json and plan -json
Rightmost Moves
~> lets only the rightmost written component grow.
The Lock Wins Until -upgrade
A new release changes nothing until init -upgrade and a committed diff.
Allowed is not installed.

The constraint says what you allow. The lock file says what you got. Init obeys the lock until you say -upgrade.

§I. Frame

The HashiCorp seat has covered refresh-only plans (10-05), encoding functions (10-02), lifecycle (09-29), and backends (09-11). Provider versioning has no lesson of its own. It sits in the Associate basics objective: install and version providers, and know how Terraform finds and fetches them.

Both paired lessons ran terraform init today. The Ops kms.tf asked for hashicorp/google ~> 7.0 and the lock file recorded 7.46.1. That file is the subject.

§II. Constraints: what you allow

A provider requirement lives in required_providers inside the terraform block. Core gets its own line, required_version.

terraform {
  required_version = ">= 1.6.0, < 2.0.0"
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6.0"
    }
  }
}

Operators: =, !=, >, >=, <, <=, and ~>. Commas join them with AND.

Rightmost Moves (named technique). ~> lets only the rightmost written component grow. Brikman gives the pair the exam tests (PDF p.458): ~> 4.0 means >= 4.0, < 5.0. Add a component and the window shrinks: ~> 3.6.0 means >= 3.6.0, < 3.7.0. On the box, ~> 3.6.0 installed hashicorp/random 3.6.3, the newest 3.6 patch.

source is the registry address. hashicorp/random expands to registry.terraform.io/hashicorp/random, and that full address is the key in the lock file.

§III. The lock file: what you got

The first init writes .terraform.lock.hcl. Brikman names its two jobs (PDF pp.458-459): the exact version of each provider, and the checksums of what was downloaded. The real file from the box:

provider "registry.terraform.io/hashicorp/random" {
  version     = "3.6.3"
  constraints = "~> 3.6.0"
  hashes = [
    "h1:Fnaec9vA8sZ8BXVlN3Xn9Jz3zghSETIKg7ch8oXhxno=",
    "zh:04ceb65210251339f07cd4611885d242cd4d0c7306e86dda9785396807c00451",
    # 11 more zh: lines
  ]
}

Four facts the exam reaches for:

  1. Commit it. Every later init, on any machine, installs the locked version even if a newer one matches the constraint.
  2. Providers only. The lock file does not record module versions. Pin modules with version (registry) or ?ref= (Git).
  3. Checksums are enforced. If a downloaded package does not match a recorded hash, init fails.
  4. Two hash schemes. zh: lines are the registry's zip checksums. h1: is a hash of the extracted package for the platform you installed on. One init on Linux gave 1 h1: and 12 zh: lines.

§IV. Constraint versus lock

The Lock Wins Until -upgrade (named technique). Change the constraint to something the locked version no longer satisfies and plain init refuses. Run on the box after editing ~> 3.6.0 to >= 3.7.0:

- Reusing previous version of hashicorp/random from the dependency lock file
Error: Failed to query available provider packages
... locked provider registry.terraform.io/hashicorp/random
3.6.3 does not match configured version constraint >= 3.7.0; must use
terraform init -upgrade to allow selection of new versions

Then terraform init -upgrade found >= 3.7.0, installed 3.9.1, and rewrote both version and constraints. Review the diff and commit it. A constraint widening that still covers the locked version changes nothing: init keeps the lock.

Multiple platforms. A lock written on Linux holds one h1: for Linux. Brikman warns that a teammate on macOS then fails init (PDF p.459). terraform providers lock -platform=linux_amd64 -platform=darwin_arm64 records both. On the box that left 2 h1: lines.

§V. Exam drills

Question 1
version = "~> 2.4". Which versions qualify?
tap to reveal
2.4 and any later 2.x (>= 2.4, < 3.0). 3.0 does not.
Question 2
version = "~> 2.4.1". Does 2.5.0 qualify?
tap to reveal
No. Only the patch may grow: >= 2.4.1, < 2.5.0.
Question 3
The lock pins 5.10.0. A newer 5.x is released. required_providers says ~> 5.0. What does terraform init install?
tap to reveal
5.10.0. The lock wins until you run terraform init -upgrade.
Question 4
Should .terraform.lock.hcl go in version control? Should .terraform/?
tap to reveal
Commit the lock file. Do not commit .terraform/, which holds the downloaded plugins and module copies.
Question 5
Does the lock file pin a registry module's version?
tap to reveal
No. It tracks providers only. Use the module block's version argument.
Question 6
CI runs on linux_amd64 and init fails with a checksum error after a macOS developer created the lock. Fix?
tap to reveal
Run terraform providers lock with -platform for every OS in use, then commit the updated lock.

§VI. Close

Rightmost Moves: count the components after ~> before you answer. The Lock Wins Until -upgrade: a new release changes nothing until someone runs init -upgrade and commits the diff. Open the lock file in today's Ops bundle and say which constraint and version it recorded before you look.

Related