TT Lab
Get started
Learn Learning paths Courses

Terraform/OpenTofu Fundamentals

One Edited Line in the Lock File Stopped init

Continue in TT Lab

Goal

You create a dependency lock file yourself, narrow it by attaching a constraint, delete and recreate it, and break a hash and restore it. You confirm by hand what init checks every time, and why only this file is committed and the installation directory is not.

Why it matters

The lock file is not a file you read but a contract for the team. Which version was chosen (version), what was allowed (constraints), and whether that package is the same as it was at the time (hashes) are written here, and init checks these three every time. Without the contract, no command can make a plan, and if a checksum is off, installation is blocked on the spot. A team that does not commit this file gets a different version for each person, and so "it works on my laptop" arises at the level of each provider. Conversely, the installation directory is not committed — because it is a binary that differs by platform, so it bloats the repository, and init can recreate it at any time.

Steps

  1. In /root/tfb-lock/app/main.tf, declare the local and random providers with no version constraint, and put random_pet.tag (length 2) and, writing its id to note.txt, local_file.note. After init, read the .terraform.lock.hcl that was created and write three lines to /root/tfb-lock/lock-read.txt: local_version=<값>, random_version=<값>, and local_hashes=<local 블록의 h1 해시 줄 수> (the placeholders are the versions and the number of h1 hash lines in the local block).
  2. Create /root/tfb-lock/pinned/main.tf anew, but this time write version = "2.9.0" for local from the start and leave random with no constraint. Declare local_file.fixed, which makes a single fixed.txt, and run init. Then read the per-provider constraints values from the lock file and write two lines to /root/tfb-lock/constraints.txt: local=<값> and random=none (the placeholder is the value).
  3. Under /root/tfb-lock/app/.terraform/providers/, find the location of the provider packages actually installed and write two lines to /root/tfb-lock/installed.txt: local=<버전>/<플랫폼> and random=<버전>/<플랫폼> (version/platform).
  4. Create /root/tfb-lock/fresh/ and copy only /root/tfb-lock/app/main.tf (do not bring the lock file and .terraform/). Run tofu plan without init and save the error output to /root/tfb-lock/no-init.txt. Do not run init in fresh.
  5. In /root/tfb-lock/badpin/main.tf, use only local, pin it with version = "2.5.0", and put in a single local_file.pinned. Run init and save the failure output to /root/tfb-lock/pin-fail.txt, and then fix the constraint to 2.9.0 so that init succeeds.
  6. In /root/tfb-lock/app/main.tf, only now attach version = "2.9.0" to local and run init again. Check whether a constraints line has appeared in the lock file's local block and on the first line of /root/tfb-lock/stale-lock.txt, write after_init=<그 값, 없으면 none> (that value, or none if absent). Then delete .terraform.lock.hcl and recreate it not with init but with tofu providers lock -fs-mirror=/opt/tofu-mirror -platform=linux_amd64, save its output to /root/tfb-lock/relock.txt, and on the second line of stale-lock.txt, write after_relock=<지금 값> (the current value).
  7. Change one local hash line of /root/tfb-lock/app/.terraform.lock.hcl to an arbitrary value, delete .terraform/, and run init. Save the checksum error output to /root/tfb-lock/tamper.txt. Then delete the lock file and run init again to restore the proper hashes.
  8. Create /root/tfb-lock/audit.sh. It reads the .terraform.lock.hcl of the directory given as the first argument and prints, one per line, the names of provider blocks that have no constraints line, and must finish with exit code 1 if there is even one and 0 if there is none. Save the result of running it on /root/tfb-lock/app to /root/tfb-lock/audit-app.txt and the result of running it on /root/tfb-lock/badpin to /root/tfb-lock/audit-pin.txt, and append exit=<종료 코드> (the exit code) to the last line of each file.

Notes

Read the contract the first init made

In /root/tfb-lock/app/main.tf, declare the local and random providers with no version constraint, and put random_pet.tag (length 2) and, writing its id to note.txt, local_file.note. After init, read the .terraform.lock.hcl that was created and write three lines to /root/tfb-lock/lock-read.txt: local_version=<값>, random_version=<값>, and local_hashes=<local 블록의 h1 해시 줄 수> (the placeholders are the versions and the number of h1 hash lines in the local block).

The lock file is HCL, and one provider block is created per provider. Inside the block go version, constraints if there are any, and a list of hashes. You do not have to apply yet.

If you start by writing a constraint, the allowed range is left in the contract too

Create /root/tfb-lock/pinned/main.tf anew, but this time write version = "2.9.0" for local from the start and leave random with no constraint. Declare local_file.fixed, which makes a single fixed.txt, and run init. Then read the per-provider constraints values from the lock file and write two lines to /root/tfb-lock/constraints.txt: local=<값> and random=none (the placeholder is the value).

The constraints line appears in the lock file only when a version constraint was written in the configuration. The block of a provider with no constraint has no such line at all. What was chosen (version) and what was allowed (constraints) are different pieces of information.

Find where the real thing the lock file points to is

Under /root/tfb-lock/app/.terraform/providers/, find the location of the provider packages actually installed and write two lines to /root/tfb-lock/installed.txt: local=<버전>/<플랫폼> and random=<버전>/<플랫폼> (version/platform).

The installation path goes deeper in the order of registry address, namespace, name, version, and platform. This directory is recreated by init, so it is not committed, but the lock file must be reviewed by a person, so it is committed.

Without the contract, you cannot even make a plan

Create /root/tfb-lock/fresh/ and copy only /root/tfb-lock/app/main.tf (do not bring the lock file and .terraform/). Run tofu plan without init and save the error output to /root/tfb-lock/no-init.txt. Do not run init in fresh.

This is the first error people meet in a freshly cloned repository. Think about what the message tells you to do and why it blocks at the plan stage. Without providers, you cannot make a plan.

If you pin a version that is not in the mirror, init rejects it

In /root/tfb-lock/badpin/main.tf, use only local, pin it with version = "2.5.0", and put in a single local_file.pinned. Run init and save the failure output to /root/tfb-lock/pin-fail.txt, and then fix the constraint to 2.9.0 so that init succeeds.

The providers in this Pod come only from the offline mirror. If you ask for a version that is not in the mirror, the tool says there is nothing to choose. In practice too, an organization that uses only an internal mirror meets the same message.

A constraint added later is not reflected in the contract by itself

In /root/tfb-lock/app/main.tf, only now attach version = "2.9.0" to local and run init again. Check whether a constraints line has appeared in the lock file's local block and on the first line of /root/tfb-lock/stale-lock.txt, write after_init=<그 값, 없으면 none> (that value, or none if absent). Then delete .terraform.lock.hcl and recreate it not with init but with tofu providers lock -fs-mirror=/opt/tofu-mirror -platform=linux_amd64, save its output to /root/tfb-lock/relock.txt, and on the second line of stale-lock.txt, write after_relock=<지금 값> (the current value).

If the choice init already made still fits the constraint, it does not choose again, and so it does not write the lock file anew either. To reflect the allowed range in the contract, you have to make that entry be created anew. providers lock does not install the package but only computes and writes the checksums.

If you tamper with a hash, installation is blocked

Change one local hash line of /root/tfb-lock/app/.terraform.lock.hcl to an arbitrary value, delete .terraform/, and run init. Save the checksum error output to /root/tfb-lock/tamper.txt. Then delete the lock file and run init again to restore the proper hashes.

The hash in the lock file is the device that checks whether the package is the same as it was at the time. No package matches a tampered hash, so installation stops. Leaving the wrong hash as it is and adding only -upgrade does not clear it.

Build a check that finds providers with no constraint

Create /root/tfb-lock/audit.sh. It reads the .terraform.lock.hcl of the directory given as the first argument and prints, one per line, the names of provider blocks that have no constraints line, and must finish with exit code 1 if there is even one and 0 if there is none. Save the result of running it on /root/tfb-lock/app to /root/tfb-lock/audit-app.txt and the result of running it on /root/tfb-lock/badpin to /root/tfb-lock/audit-pin.txt, and append exit=<종료 코드> (the exit code) to the last line of each file.

You can just read the lock file line by line. A line that starts with provider is the start of a new block, and all you need to remember is whether a constraints line follows it. You must also judge the last block in awk's END block so that you do not miss one.