Hello @rxxk-cg,
As a general rule, only root modules (the directory where you run terraform apply
) should have provider
blocks. Shared modules should not have provider
blocks, and should instead either inherit or have explicitly passed provider configurations from the root.
For a shared module which only needs one configuration for a particular provider it’s sufficient to just declare a dependency on the provider itself, and therefore just inherit the default (unaliased) configuration for that provider from the root module, without any explicit extra wiring on the module user’s part:
terraform {
required_providers {
postgresql = {
# I'm assuming that cyrilgdn's provider is the
# one you meant when you said "postgresql";
# specify a different username here if not.
source = "cyrilgdn/postgresql"
}
}
}
As long as the root module includes a configuration for cyrilgdn/postgresql
, the child module will automatically inherit it from the root without any explicit passing by the caller. I would recomment using this approach in most cases; the rest of this post is about a less common situation.
The extra configuration_aliases
argument deals with the more complex situation where the provider needs to use more than one configuration for the same provider, in which case configuration_aliases
declares which alias names the module will use:
terraform {
required_providers {
postgresql = {
source = "cyrilgdn/postgresql"
configuration_aliases = [
postgres.a,
postgres.b,
]
}
}
}
The above declares two alternate (“aliased”) provider configurations called postgres.a
and postgres.b
. You can then select one of those configurations for each of the resources in the module which is implemented by that provider.
In this situation with multiple provider configurations, Terraform can’t automatically infer the meaning of the aliases a
and b
and so instead of automatic inheritance of the default provider configurations the users of your module will need to specify which of their own provider configurations correlates with each one. For example, if the caller of your module is a root module which declares its own concrete provider configurations foo
and bar
then it could associate them with your module’s a
and b
like this:
terraform {
required_providers {
postgresql = {
# NOTE: The caller and callee must both agree
# about which provider source they are using,
# since this is how Terraform knows that both
# modules are using the same provider.
source = "cyrilgdn/postgresql"
}
}
}
provider "postgres" {
alias = "foo"
# ...
}
provider "postgres" {
alias = "bar"
# ...
}
module "example" {
source = "../modules/example"
providers = {
postgres.a = postgres.foo
postgres.b = postgres.bar
}
# ...
}
The above module
block’s providers
argument specifies that the child module’s a
configuration correlates with the root module’s foo
, and the child module’s b
correlates with bar
.
As I mentioned above, I would suggest not using this more complicated approach unless your module is specifically representing some sort of connection between two configurations. For example, a module which sets up a peering connection between two AWS VPCs in different accounts or regions naturally ends up needing two provider configurations due to how the AWS provider is designed: account and region are provider-configuration-level settings rather than resource arguments. But I would typically separate that particular need into its own focused module, and then let the rest of the problem be described with simpler modules that only work with one configuration at a time.