skip to Main Content

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


  1. For this to work, you have to have configuration_aliases defined in the module. In your example that would be:

    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = ">= 4.0"
          configuration_aliases = [ aws.acc1, aws.acc2 ]
        }
      }
    }
    

    You can find more information in the documentation.

    Login or Signup to reply.
  2. 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:

    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 4.14.0"
        }
      }
    }
    
    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"
      }
    }
    
    module "ssm_parameter_acc1" {
      source = "./ssm_parameter/"
    
      providers = {
        aws = aws.acc1
      }
    }
    
    module "ssm_parameter_acc1" {
      source = "./ssm_parameter/"
    
      providers = {
        aws = aws.acc2
      }
    }
    

    The providers argument in each module 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 just aws in the "ssm_parameter" module will refer to the same provider configuration as aws.acc1 or aws.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:

    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
    
          configuration_aliases = [ aws.acc1, aws.acc2 ]
        }
      }
    }
    
    resource "aws_ssm_parameter" "foo_acc1" {
      provider = aws.acc1
    
      name  = "foo"
      type  = "String"
      value = "bar"
    }
    
    resource "aws_ssm_parameter" "foo_acc2" {
      provider = aws.acc2
    
      name  = "foo"
      type  = "String"
      value = "bar"
    }
    

    Notice that now the resource "aws_ssm_parameter" blocks include explicit provider arguments. If you don’t specify provider here then Terraform defaults to provider = aws, but the configuration_aliases argument in the required_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:

    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 4.14.0"
        }
      }
    }
    
    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"
      }
    }
    
    module "ssm_parameter" {
      source = "./ssm_parameter/"
    
      providers = {
        aws.acc1 = aws.acc1
        aws.acc2 = aws.acc2
      }
    }
    

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search