I have a few accounts on AWS. Now I want to have one module to be running on all of them.
For this I have prepared the terraform.tf file on my root
provider "aws" {
region = "eu-central-1"
}
provider "aws" {
alias = "acc1"
region = "eu-central-1"
assume_role {
role_arn = "arn:aws:iam::ACC1:role/MyRoleAcc1"
}
}
provider "aws" {
alias = "acc2"
region = "eu-central-1"
assume_role {
role_arn = "arn:aws:iam::ACC2:role/MyRoleAcc2"
}
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.14.0"
}
}
}
module "ssm-paramater-module" {
source = "./ssm_parameter/"
providers = {
aws.acc1 = aws.acc1
aws.acc2 = aws.acc2
}
}
But have a problem
Warning: Provider aws.acc1 is undefined
│
on terraform.tf line 34, in module "ssm-paramater-module":
34: aws.acc1 = aws.acc1
│
Module module.ssm-paramater-module does not declare a provider named aws.acc1.
If you wish to specify a provider configuration for the module, add an entry for aws.acc1 in the required_providers block within the module.
╵
╷
Warning: Provider aws.acc2 is undefined
│
on terraform.tf line 35, in module "ssm-paramater-module":
35: aws.acc2 = aws.acc2
│
Module module.ssm-paramater-module does not declare a provider named aws.acc2.
If you wish to specify a provider configuration for the module, add an entry for aws.acc2 in the required_providers block within the module.
How correct workaround this?
Because if I will do single provider like
module "ssm-paramater-module" {
providers = {
aws = aws.acc1
}
source = "./ssm_parameter/"
}
It works well.
The module like itself I want to implement is just a simple SSM parameter :
resource "aws_ssm_parameter" "foo" {
name = "foo"
type = "String"
value = "bar"
}
2
Answers
For this to work, you have to have
configuration_aliases
defined in the module. In your example that would be:You can find more information in the documentation.
There are two possible ways to set this up, and neither is necessarily "correct", just depends on what abstraction you’re intending to provide with this module.
The first option is that each call to your module works only with one AWS provider configuration, and then you call the module multiple times and send a different provider configuration to each one:
The
providers
argument in eachmodule
block tells Terraform which provider configuration to use as this module’s default ("unaliased") provider configuration. Each module has its own independent view of all of the provider configurations, so the provider configuration address named justaws
in the "ssm_parameter" module will refer to the same provider configuration asaws.acc1
oraws.acc2
in the root module, depending on which of the two module calls you’re looking at.The other way to set this up is for the shared module to itself be aware of multiple AWS provider configurations.
This will require you to change the contents of the child module to declare that it expects multiple additional (non-default, non-aliased) provider configurations, like this:
Notice that now the
resource "aws_ssm_parameter"
blocks include explicitprovider
arguments. If you don’t specifyprovider
here then Terraform defaults toprovider = aws
, but theconfiguration_aliases
argument in therequired_providers
block does not declare a default (unaliased)aws
configuration, so now you must explicitly assign each resource to one of your additional (aliased) provider configurations.When you call this module from your root module, the
module
block must pass in both of the provider configurations, like this:Which of the above to choose is a question of separation of concerns: which of these two modules is the one responsible for dealing with the fact that there are two accounts?
The first example above is the more common because describing a set of objects that should exist in one AWS account or region is often a nice abstraction boundary, and then the calling module can call that module as many times as necessary to create similar objects across multiple accounts or regions.
However, the second example would also be valid if there being two AWS accounts is just an inherent part of your architecture that this child module needs to deal with. One reason why I might choose the second option is if the module is responsible for writing different values into each account, and the details of which values go into which account are part of this module’s own abstraction.