2
votes

I'm currently working on an AWS terraform project where I have an array of ROLE IDs (as variables) for different accounts.

variable "slave_account_id" {
 default = ["5686435678", "9889865446"]
}

Each of these roles allow a my current AWS account (linked with terraform) to deploy a module on those accounts (assuming for each account the role id)

Thus, I would like to create different providers for each role based on the variable array "slave_account_id".

I tried to do it this way:

provider "aws" {
  counter = "${length(var.slave_account_id)}"

  alias  = "aws-assume-${counter.index}"
  region = "eu-west-1"

  assume_role {
    role_arn     = "arn:aws:iam::${var.slave_account_id[counter.index]}:role/slave_role_for_master"
    session_name = "${var.slave_session_name[counter.index]}"
    external_id  = "EXTERNAL_ID"
  }
}

This way I would have planned to use this code inside my module:

module "my_super_module" {
 counter = "${length(var.slave_account_id)}"
 providers = {
   aws = "aws.aws-assume-${counter.index}"
 }

 [...]
}

But this doesn't work (from what I understood I cannot 'concatenate" variable inside the alias of a provider because provider has to be defined before we can interpolate). Here is the execution result (error du to alias section of the provider):

Error: Invalid provider configuration alias

An alias must be a valid name. A name must start with a letter and may contain
only letters, digits, underscores, and dashes.


Error: Duplicate provider configuration

  on main.tf line 5:
   5: provider "aws" {

A default (non-aliased) provider configuration for "aws" was already given at
main.tf:1,1-15. If multiple configurations are required, set the "alias"
argument for alternative configurations.


Error: Unsuitable value type

  on main.tf line 8, in provider "aws":
   8:   alias  = "aws-assume-${counter.index}"

Unsuitable value: value must be known


Error: Variables not allowed

  on main.tf line 8, in provider "aws":
   8:   alias  = "aws-assume-${counter.index}"

Variables may not be used here.


Error: Invalid provider configuration reference

  on main.tf line 33, in module "my-lambda":
  33:     aws = "aws.aws-assume-${counter.index}"

A provider configuration reference must not be given in quotes.

Hence I am a bit lost...

How to deploy a module with a list of role ids (one module for each account) ?

1
You can interpolate inside a provider definition although I've not tried to use the count meta parameter here so it's possible this might not work. Do you get an error when you try to run with that set? If so can you edit your question to include it? I'd also question if you really want to be deploying to multiple AWS accounts at the same time in the first place really. AWS accounts give a great separation between things and help minimise blast radius so it surprises me that you'd like to avoid that separation.ydaetskcoR
@ydaetskcoR I've added the execution result (errors). The errors seem to be all related to the alias name in the provider section. Yes I really want to deploy to multiple AWS accounts :'( Maybe there's another way of assuming roles without "provider" but I didn't find ...John Doe

1 Answers

5
votes

Provider configurations in Terraform are not dynamically-constructable (that is, to decide which to create based on a value) because Terraform needs to associate providers with resources very early in the lifecycle, during graph construction and before expression evaluation is possible.

Instead, we can refactor the problem so that each module takes a fixed number of AWS providers (most often one, but in some cases two if the module's purpose is e.g. to set up peering between two regions or two accounts) and then instantiate the module multiple times in the root:

provider "aws" {
  alias = "eu-west-1_5686435678"

  region = "eu-west-1"
  assume_role {
    role_arn     = "arn:aws:iam::acct5686435678:role/admin"
    session_name = "whatever_session_name"
    external_id  = "EXTERNAL_ID"
  }
}

provider "aws" {
  alias = "eu-west-1_9889865446"

  region = "eu-west-1"
  assume_role {
    role_arn     = "arn:aws:iam::acct9889865446:role/admin"
    session_name = "whatever_session_name"
    external_id  = "EXTERNAL_ID"
  }
}

module "acct5686435678" {
  source = "./modules/aws-account"

  providers = {
    aws = aws.eu-west-1_5686435678
  }
}

module "acct9889865446" {
  source = "./modules/aws-account"

  providers = {
    aws = aws.eu-west-1_9889865446
  }
}


module "peering_5686435678_9889865446" {
  source = "./modules/aws-account-peering"

  providers = {
    aws.from = aws.eu-west-1_5686435678
    aws.to   = aws.eu-west-1_9889865446
  }
}

Instantiating the same module multiple times is a common technique for situations where the same infrastructure must be created over multiple AWS accounts or over multiple AWS regions.

With that said, if the multiple AWS accounts are representing separate environments rather than separate components within an environment, it's often preferable to use a separate root configuration per environment while still sharing modules, so that updates to each environment are entirely separated, each environment has its own state, etc.