terraform
terraform copied to clipboard
terraform providers lock command don't honour the .terraformrc file
Terraform Version
Terraform v1.7.4
on darwin_amd64
### Terraform Configuration Files
My `providers.tf`
```terraform
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "3.61.0"
}
}
}
my .terraformrc
provider_installation {
direct {
exclude = ["registry.terraform.io/*/*"]
}
network_mirror {
url = "https://artifactory.my-company.com/artifactory/api/terraform/terraform-virtual-dev/providers/"
}
}
Debug Output
https://gist.github.com/carlitos081/1744aa46e0d73b56aa3e6c52a292bfbf https://gist.github.com/carlitos081/8ea2d68ed52c4a4f08efa14e99946ef3
Expected Behavior
When I run terraform init
I can see that the provider is downloaded from my network_mirror https://artifactory.my-company.com/artifactory/api/terraform/terraform-virtual-dev/providers/
see the first gist
When I run the terraform providers lock -platform=linux_amd64
it will go directly to https://registry.terraform.io/v1/providers/hashicorp/aws/versions
instead of the mirror defined in .terraformrc
Actual Behavior
both terraform init
and terraform providers lock -platform=linux_amd64
must honour the .terraformrc
Steps to Reproduce
- Create a providers.tf file as posted above
- export TF_LOG=trace
- Create the
.terraformrc
file as posted above and change to your own network_mirror - run
terraform init
you will see it is honouring the network_mirror - Run
terraform providers lock -platform=linux_amd64
you will see it is to the public mirrorhttps://registry.terraform.io/
instead
Additional Context
No response
References
No response
Hi @carlitos081,
Thanks for filing the issue. Does the resulting lock file work as expected when you specify the mirror using the -net-mirror
flag? In order to get all the published signature information, Terraform will by default contact the provider's origin registry.
Indeed, the original idea of this command was that it would intentionally ignore the provider installation configuration so that you can generate a dependency lock file containing the official release checksums and then use it to validate that your mirror was constructed correctly. This command is an "escape hatch" alternative because those who are using mirrors otherwise don't get the default behavior of terraform init
checking directly against the upstream releases.
The -fs-mirror
and -net-mirror
options let you override that if you intend to treat the packages in your mirror as the authoritative "correct" checksums to use, but that's not the default because the primary use-case for filesystem and network mirrors is for them to contain exact copies of the official provider releases, and so the default assumes that you'd want to be able to verify that the mirror is correct.
Although I don't think this is a bug (Terraform behaved as it was designed to behave), it does seem to me that it could be reasonable for this command to offer a shorthand option -- mutually-exclusive with the existing fs-mirror/net-mirror options -- that just means "use the same installation methods that terraform init
would have used", for situations where the author wants to treat their configured mirrors as the authority and just wants to use this command for its ability to calculate checksums for multiple platforms at once.
I want to chime in on this issue since I looked into it in the past. It seems like not honoring Terraform CLI Config file is an explicit choice in the design of this particular command based on this comment: https://github.com/hashicorp/terraform/blob/bf0a6ed41cab4ad0f3f5ba492dd43951633997dc/internal/command/providers_lock.go#L95-L124
Specifying the mirror URL explicitly using -net-mirror
flag would probably solve the problem of the original author of this issue. However, is there a practical solution for when multiple installation methods are required? Here's a CLI config similar to what I use:
provider_installation {
direct {
exclude = ["registry.terraform.io/*/*", "some-provider-available-only-via-fs-mirror.corp.net/*/*"]
}
network_mirror {
url = "https://artifactory.my-company.com/artifactory/api/terraform/terraform-virtual-dev/providers/"
}
filesystem_mirror {
path = "/usr/share/terraform/plugins"
}
}
Let's say I have a config that requires two providers: one can be installed from a filesystem mirror and the other one from a network mirror. Here is what I discovered about providers lock
from experimenting with a config like this:
-
terraform providers lock
- without any additional flag will fail since it assumes that all providers need to be installed from an official registry. -
terraform providers lock -fs-mirror=/usr/share/terraform/plugins
- this fails because Terraform now tries to install all providers from the filesystem mirror, but only one of the providers is available at that mirror. -
terraform providers lock -fs-mirror=/usr/share/terraform/plugins some-provider-available-only-via-fs-mirror.corp.net/some/provider
- will populate the lockfile for the provider that needs to be installed from the filesystem mirror.
After the last command, I will need to run terraform providers lock -network-mirror
and explicitly list every single provider that needs to be installed from network mirror. If I don't explicitly list every single provider, it will try to install some-provider-available-only-via-fs-mirror.corp.net/some/provider
which is not available via network mirror.
The fact that the command needs to run several times (once for each installation method) and that providers need to be specified explicitly is super impractical. Adding proper support for Terraform CLI config similar to every other command would make things a lot easier.
I don't intend the following as an argument for or against changing anything here. Just sharing some historical context about the design intent of these features, so that we can hopefully then talk about either ways in which the design assumptions were incorrect or how we could extend the functionality to support new use-cases that it wasn't originally intended for.
The main thing to keep in mind about the current design is that the word "mirror" is meant to imply "exact copies of packages from the official source", and so the design is shaped by that assumption. The original intended user of filesystem or network mirrors has the following setup:
- In development environments there is no custom provider installation configuration at all. Configuration authors install packages directly from their upstreams, and check the generated lock file into version control.
- In the production environment changes are made using centralised automation and that automation has a restrictive network connection that either has no internet connection at all (if managing systems accessible on the LAN) or has its connectivity limited to the extent that origin registries are not reachable.
In this "happy path", the mirror naturally gets checked against the signed checksums that the developers fetched during their work. If the mirror has a missing or modified package then it will be detected by terraform init
due to mismatch with the lock file.
The terraform providers lock
command is a pragmatic slight modification to the above model: sometimes even the development environments are using the internal mirrors, perhaps as a way to achieve development/production parity. In that case the developers don't naturally obtain the official checksums as part of their work, so instead they run terraform providers lock
to build the lock file with the correct checksums and then the terraform init
checks that the mirror is correct in the same way as it would in production. This allows problems with the mirror to be detected during the development phase rather than only in production.
This issue is implying a third situation that our mirror features were not designed to support: there are some provider packages that exist only in a mirror, and have no upstream origin registry and so therefore no "official" checksums (in the sense that Terraform thinks of "official", where the origin registry is the authority for its own namespace).
In that case (as described in the comment above) the terraform providers lock
command fails trying to contact an origin registry that doesn't exist. It thinks its job is to find the official package checksums for the provider, but there are no such checksums to find and so it fails.
To make this work with today's Terraform, the provider packages would need to be published both in a provider registry and in the provider mirror, so that Terraform can verify that the mirror and the registry match. But if the mirror and the registry are both run by the same entity then that is essentially redundant: there is no separate "upstream authority" to check against.
The idea I offered in my earlier comment would work for organisations that wish to use their mirror as the authoritative source for all providers, and perhaps that compromise is sufficient.
A more general answer would be to allow some way to explicitly configure that a particular mirror is the authority for a specific set of providers and that there is not any upstream origin registry that the mirror is subordinate to. For example:
provider_installation {
network_mirror {
url = "https://tf.example.com/providers/"
# This mirror is authoritative for
# any providers whose hostname
# is tf.example.com. There is no
# upstream registry to compare
# against.
authority_for = ["tf.example.com/*/*"]
# Since there is no include/exclude here
# Terraform will also prefer to use this
# mirror for installing any other providers
# too, but will treat their origin registries
# as authoritative for official checksums.
}
}
In this hypothetical I'm imagining authority_for
as a stronger version of include
which essentially tells Terraform to treat the mirror as the origin registry for any matching provider addresses. Terraform would never try to use tf.example.com
as a provider registry directly, even when using terraform providers lock
. It would instead use the mirror to obtain any information that an origin registry would normally be considered the authority for, such as the "official checksums".
I don't know if this flexibility is actually needed. It might be sufficient to assume that if someone wants to treat their mirror as authoritative for anything then they want to treat it as the authority for everything, in which case I think my earlier idea of a new option on the lock command would be sufficient and considerably more straightforward.
Hi @apparentlymart, Sorry for the late answer, I appreciate you took time to explain clearly what are the criteria for which terraform has this behavior, I would love if what you proposed can be implemented, so we can consider authoritative the mirror only for our own provider and not the the publicly available one. Is something that can be implemented? Thanks again