What is the best practise with .terraform.lock.hcl in modules ... commit or not?

Hi.

We have a .terraform.lock.hcl file in the root of the project that contains the versions and hashes of the providers for the OSs we develop on / deploy with. This file is committed to the repo.

Our pipelines caches the following paths:

    terraform-plugins: /root/.terraform.d/plugin-cache
    terraform-local-plugins: .terraform/plugins
    terraform-modules: .terraform/modules

With this setup, our pipelines do not need to download and install providers that the pipeline has already seen. If the developers update a provider version, then this will be added to the cache. The pipeline automatically expires the caches after 7 days.

Developers who are using Terraform for multiple projects will also benefit locally as they keep their provider versions uptodate.

All is nice and efficient and the world is Terraformed. Nearly.

The problem we have at the moment is the use of GitHub - antonbabenko/pre-commit-terraform: pre-commit git hooks to take care of Terraform configurati (v1.50.0).

Some of the projects have a modules directory with multiple modules as sub-directories (only 1 level deep so far, but I can imagine that this may or may not always be the case). These modules are not externally stored and pulled in via a git or http URL, but just modules that are really only reusable within the project in which they are placed.

One element of the pre-commit tool is to run terraform validate in each directory. This has the side effect of producing a .terraform.d directory and a .terraform.lock.hcl file in each module.

What we are unsure of is if these new .terraform.lock.hcl files should be committed in the same way as the one in the root of the project.

In addition to this, we do regularly update the versions of the providers we want to use as we evaluate the changes.

We use the terraform providers lock command in the root of the project. Should we be running this in each module also as the only changes are in the root directory.

Are we being over critical/restrictive with regard to the .terraform.lock.hcl files?

If we were only using semantic versions in our versions.tf file, then we are leaving ourselves open to abuse from a re-tagged commit/build of the provider, so having the .terraform.lock.hcl file would seem to a good security measure and matches what we have with other languages (PHP’s composer and Node).

So, really, just trying to work out what the best / recommended practise here is.

We have about 20 Terraform projects, some with dozens of modules.

We use a makefile to simplify the most common day-to-day tasks for the developers, and so, if we need to clear out the .terraform.d directory and the .terraform.lock.hcl file from the module, we can do that without breaking the developers workflow.

Hi @rquadling,

Because provider dependencies are common to an entire configuration (across all modules), .terraform.lock.hcl doesn’t do anything when present in a module you’re calling using a module block.

However, you mentioned that you’re using terraform init followed by terraform validate as part of verifying changes to your individual modules, which is certainly a reasonable thing to do. In that case, the meaning of checking in .terraform.lock.hcl for those would be to say that you intend all future validations of that module to use those same versions of providers until you explicitly upgrade. It would not ever affect any other configurations which call those modules with module blocks though; each configuration’s own .terraform.lock.hcl (in the root module directory) is the only relevant lock file.

Given that, I think your decision point here is how you’d prefer to react to a newer version of a provider that might include a breaking change that affects your module. If you’d prefer to find out as soon as possible then it could be reasonable to not check in .terraform.lock.hcl and just let those versions float, but if you’d prefer to instead separate the problem of dealing with incompatible new versions from any other changes you need to make then checking in .terraform.lock.hcl would be a good way to get that effect.


The other thing to keep in mind is that .terraform.lock.hcl is part of Terraform’s strategy for defense against supply-chain attacks where an attacker might somehow cause your system to install a modified version of a provider. The checksums recorded in .terraform.lock.hcl serve to ensure that you’re still running the same provider version you ran last time. However, there are other mechanism in place that complement the lock file:

  • Terraform verifies the certificate of the remote registry against your system’s trusted certificate store, preventing certain kinds of misdirection or man-in-the-middle attacks.
  • If you’re using the default direct installation method then Terraform checks the downloaded package against a set of approved checksums signed using a signature selected by the registry, so an attacker would need to compromise the checksums, the signature, and the package.

If you’re running terraform validate in an isolated environment without access to any credentials then you might conclude also that the risk of executing arbitrary untrusted code is relatively low in this case.

Ultimately this security tradeoff, along with the workflow tradeoff I described above, are both things you’ll need to weigh yourself, but I hope the information here gives you a starting point for doing so!

4 Likes

As always, thank you for a great explanation.

1 Like