Hi @b0bu,
It looks like you already found a solution so in my response here I’m aiming to add a bit of “why is it so?” to this topic, in case you or some other future reader finds it useful.
I think what you have here is an interesting consequence of how implicit vs. explicit provider passing works in Terraform.
When you have a module
block which doesn’t have a providers
argument, Terraform aims to behave as if you had written a providers
argument which passes in all of the default (that is, “un-aliased”) provider configurations from the calling module.
For example, let’s assume that the following is the entire definition of a calling module (there aren’t any other .tf
files in the same directory bringing other provider configurations into play):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
archive = {
source = "hashicorp/archive"
}
}
}
provider "aws" {
region = "us-west-2"
}
provider "aws" {
alias = "use1"
region = "us-east-1"
}
provider "archive" {}
module "example" {
source = "(anything)"
}
The module
block at the end of this example does not include a providers
argument, so Terraform will study both the child module and the calling module to see which required_providers
they have in common. If both the calling module and the called module both make use of both hashicorp/aws
and hashicorp/archive
then Terraform will look for all of the provider configurations for both of those providers that don’t have aliases, and behaves as if those were passed in with providers
, like this:
providers = {
archive = archive
aws = aws
}
As long as both of these modules remain unchanged, there’s no material difference between leaving the providers
argument omitted or explicitly passing in the default configurations as I showed here.
However, one interesting implication of leaving them all implied is that if this child module begins using a new provider for some reason later then – assuming that the calling module already has a default provider configuration for that provider – the implied provider
argument would automatically include that default configuration, whereas the explicit provider
argument would need to have the new provider’s default provider configuration added explicitly.
Since provider aliases are namespaced on a per-module basis, Terraform will never automatically pass an aliased configuration and so for any module where there’s at least one required aliased configuration you’d have no option but to explicitly set providers
, which means you would need to explicitly set archive = archive
in the situation you described.
This rule interacts with a few other implicit behaviors in Terraform which are good to keep in mind too:
-
If a provider doesn’t have any required configuration arguments then Terraform will allow omitting the provider
block that would define its provider configuration, and will behave as if you had written an empty block.
This is true of the hashicorp/archive
provider, since it has no configuration arguments at all, and so the provider "archive" {}
block I wrote in the example above was actually unnecessary; if I had omitted it, Terraform would’ve assumed it automatically.
An implied empty provider
block still counts as a default provider configuration for the purposes of the rule I described above: Terraform will still automatically pass in the empty default provider configuration for hashicorp/archive
if you omit the providers
argument in the module
block.
-
Many modules that were written for older versions of Terraform don’t include explicit required_providers
blocks for providers that belong to the hashicorp/
namespace. If Terraform finds a configuration for provider "aws"
and the required_providers
block doesn’t include a definition of what “aws” means then it will assume you meant hashicorp/aws
, for backward compatibility with older versions of Terraform that only supported HashiCorp-published providers and therefore had no need for this required_providers
syntax.
Therefore in my above example it would’ve worked to omit the required_providers
block altogether; both of these providers belong to the hashicorp/
namespace.
When matching provider requirements between modules to decide how to build the implied providers
argument, Terraform uses the global provider source addresses like hashicorp/aws
or hashicorp/archive
to match them, rather than the local names chosen in the required_providers
block. This means that in unusual cases where a module needs to use a different local name for a provider (if, for example, a module for some reason needed to use both hashicorp/archive
and example/archive
at the same time, such that they can’t both have the local name “archive”) it only matters that the source addresses agree, not that the local names agree.