Passing provider configuration to sub sub module

Hi,

I have a module in my root main.tf that whose own main.tf references other child modules.
So in root main.tf:

module "my_module" {
  providers = {
    aws.dev      = aws.dev
    aws.stg      = aws.stg
    aws.prod     = aws.prod
  }
  source = "./modules/aws/my_module"
} 

to specify the providers.

Then in the main.tf of the module itself:


module "set_iam_dev" {

  providers = {
    aws.dev      = aws.dev
  }
  environment = "dev"
  source      = "./iam"
} 

module "set_iam_stg" {
  providers   = {
    aws.stg   = aws.stg
  }
  environment = "stg"
  source      = "./iam"
}

And finally under ./iam I have two iam resources, one role and one policy.

The problem I’m facing is that I cannot pass the providers from the root module all the way down to the child modules. The warning I get is:

│ Warning: Provider aws.dev is undefined
│ 
│   on modules/aws/my_module/main.tf line 18, in module "set_iam_dev":
│   18:     aws      = aws.dev
│ 
│ Module module.my_module.module.set_iam_dev does not declare a provider named aws.dev
│ If you wish to specify a provider configuration for the module, add an entry for aws in the required_providers block within the module.

I tried to set required_providers in both child module and its child module. Nothing seems to work.

Any ideas on how to achieve this?

Thanks!

Hi @nivimorbgp,

It looks like you’re intending to declare multiple instances of this ./iam module, each with a different provider configuration.

To do this, it’s important to first recognize that each module has its own “view” of the provider configurations, and so they will each have their own set of supported configuration addresses – with or without aliases – and the providers argument serves to translate from the calling module’s addresses to the addresses expected by the module you’re calling.

Because your ./iam module only needs to work with one provider configuration, it makes sense to use that module’s default (unaliased) provider configuration to represent the one it should use. Your intermediate module has to declare three different aliases because it needs to work with multiple configurations at the same time, but that’s not necessary for the ./iam module which only needs one.

With that said then, I would expect the top-level module block to be the same as what you shared:

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
    } 
  }
}

provider "aws" {
  alias = "dev"

  # ...
}

provider "stg" {
  alias = "stg"

  # ...
}

provider "prod" {
  alias = "prod"

  # ...
}

module "my_module" {
  source = "./modules/aws/my_module"
  providers = {
    aws.dev  = aws.dev
    aws.stg  = aws.stg
    aws.prod = aws.prod
  }
} 

Inside this ./modules/aws/my_module you’d declare the requirement for these three additional (aliased) provider configurations, and then pass them down individually to the three ./iam calls:

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"

      configuration_aliases = [
        aws.dev,
        aws.stg,
        aws.prod,
      ]
    } 
  }
}

module "set_iam_dev" {
  source = "./iam"
  providers = {
    aws = aws.dev
  }

  environment = "dev"
} 

module "set_iam_stg" {
  source      = "./iam"
  providers = {
    aws = aws.stg
  }

  environment = "stg"
}

module "set_iam_prod" {
  source      = "./iam"
  providers = {
    aws = aws.prod
  }

  environment = "prod"
}

The providers argument in the module "set_iam_prod" block (for example) says “the default aws provider configuration in the child module is the one called aws.prod in this module”, which I believe is the effect you intended.

Finally, inside the iam module itself you don’t need to declare any required configuration_aliases because it only uses the default (unaliased) provider, which from that module’s perspective is just called aws and will be selected by default for any resource which doesn’t have a provider argument to override that selection. That module need only declare that it needs that provider and will call it aws:

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
   } 
  }
}

# (and then AWS provider resource and data blocks,
# as usual.)
1 Like

Thanks @apparentlymart!
I will follow your advice and post a followup.

Thanks again!