# Multi-Region modules with count

**URL:** <https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625>\
**Category:** Terraform\
**Created:** [July 16, 2024, 3:01pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625 "2024-07-16T15:01:35Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![wblakecannon](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/wblakecannon/32/1737_2.png) [@wblakecannon](https://discuss.hashicorp.com/u/wblakecannon)\
**Post date:** [July 16, 2024, 3:01pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625/1 "2024-07-16T15:01:35Z")

</div>

I am trying to deploy hundreds of resources in two different regions (us-east-2, eu-central-1).

I had all my code written to have a separate module block for the resources in each region. For example here are snippets of my Cognito portion:

```auto
.
├── Modules
│ ├── Cognito
│ │ ├── Scripts
│ │ │ ├── cognito-generate-token.js
│ │ │ └── cognito-message.js
│ │ ├── cognito.tf
│ │ ├── lambda.tf
│ │ ├── main.tf
│ │ ├── sg.tf
│ │ └── var.tf
├── main.tf
└── var.tf

```

./main.tf:

```auto
################################################################################
# Terraform
################################################################################
terraform {
  required_version = ">= 1.5"

  backend "s3" {
    bucket = "projectcanary-terraform-state"
    key = "utilities-v2.tfstate"
    region = "us-east-2"
  }

  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  alias = "eu-central-1"
  region = "eu-central-1"
}

provider "aws" {
  alias = "us-east-2"
  region = "us-east-2"
}

################################################################################
# Modules
################################################################################

module "cognito_eu" {
  source = "./Modules/Cognito"
  contact = var.contact
  region_mapping = var.region_mapping
  service = var.service

  providers = {
    aws = aws.eu-central-1
  }
}

module "cognito_us" {
  source = "./Modules/Cognito"
  contact = var.contact
  region_mapping = var.region_mapping
  service = var.service

  providers = {
    aws = aws.us-east-2
  }
}

```

./Modules/Cognito.main.tf:

```auto
################################################################################
# Terraform
################################################################################
terraform {
  required_providers {
    archive = {
      source = "hashicorp/archive"
      version = "~> 2.0"
    }
    aws = {
      source = "hashicorp/aws"
      configuration_aliases = [
        aws
      ]
    }
  }
}

provider "aws" {
  alias = "eu-central-1"
  region = "eu-central-1"
}

provider "aws" {
  alias = "us-east-2"
  region = "us-east-2"
}

```

This worked as expected. Adding Cognito resources (as well as hundreds of others from different modules) in the two regions.

However, now I need to make a conditional in the module to only create the Cognito resources in the `dev` and `prod` workspaces. So I added the following:

./main.tf:

```auto
module "cognito_eu" {
  count = contains(["dev", "prod"], local.environment) ? 1 : 0

  source = "./Modules/Cognito"
  contact = var.contact
  region_mapping = var.region_mapping
  service = var.service

  providers = {
    aws = aws.eu-central-1
  }
}

module "cognito_us" {
  count = contains(["dev", "prod"], local.environment) ? 1 : 0
  
  source = "./Modules/Cognito"
  contact = var.contact
  region_mapping = var.region_mapping
  service = var.service

  providers = {
    aws = aws.us-east-2
  }
}

```

But I get the following:

```auto
│ The module at module.cognito_us is a legacy module which contains its own local provider configurations, and so calls to it may not use the count, for_each, or depends_on arguments.
│
│ If you also control the module "./Modules/Cognito", consider updating this module to instead expect provider configurations to be passed by its caller.

```

I tried going down to one Cognito module in `main.tf` with multiple configuration aliases declared in `./Modules/Cognito/main.tf`. But doing that only wants to deploy in one region.

What am I doing wrong? I need to conditionally create multiple resources in different environments.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [July 16, 2024, 3:07pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625/2 "2024-07-16T15:07:40Z")

</div>

Hi @wblakecannon,

I think you may need to complete the maneuver of moving the provider configurations into the root module as a separate step before you use `count` and `for_each`, because Terraform tracks the relationships between resources and provider configurations in the state and updates those only when you apply a change using a different provider configuration address.

In practice that means switching to using the `providers` argument in the `module` block and removing the `provider` block from your child module first, applying that change in isolation, and then applying the rest.

In the second step you will also need to add a `moved` block for each module call to tell Terraform that you intended to rebind the existing module instance to the new address with `[0]` at the end, rather than to destroy the old objects and create new ones at the new addresses.

---

<div class="post-metadata">

**Author:** ![wblakecannon](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/wblakecannon/32/1737_2.png) [@wblakecannon](https://discuss.hashicorp.com/u/wblakecannon)\
**Post date:** [July 16, 2024, 3:08pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625/3 "2024-07-16T15:08:17Z")

</div>

It appears all I have to do is comment out the providers section of `./Modules/Cognito/main.tf` and according to the plan it seems to work, but I don’t know why.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [July 16, 2024, 4:24pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625/4 "2024-07-16T16:24:56Z")

</div>

Ahh, okay! I assumed from what you described that you’d moved the `provider` blocks from the child module into the root module, but I guess you’d just _added_ `provider` blocks at the root but kept the ones inside the module.

I expect that if you hadn’t got the error you saw then you would’ve seen a different error about it not being valid to use the `providers` block to pass a provider configuration into an address that matches one the child module already explicitly defines: a particular provider configuration address can either have a `provider` block inline _or_ have a configuration passed by the caller, but not both.

Removing the `provider` block from the child module made this valid both because now you have no `provider` blocks in your shared module (which is not allowed when using `count` or `for_each`, as the error message explained) _and_ you’re passing in a suitable provider configuration from the caller using the `providers` argument.

---

<div class="post-metadata">

**Author:** ![wblakecannon](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/wblakecannon/32/1737_2.png) [@wblakecannon](https://discuss.hashicorp.com/u/wblakecannon)\
**Post date:** [October 15, 2024, 5:45pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625/5 "2024-10-15T17:45:47Z")

</div>

Revisiting this. Same error but doing something different using the kreuzwerker docker provider and my head is spinning.

I need to be able to make docker images in multiple regions and just can’t figure this out.

The docker provider can’t be declared in the root because it wants a region:

```auto
provider "docker" {
  registry_auth {
    address = format("%v.dkr.ecr.%v.amazonaws.com", var.aws_account_id, data.aws_region.current.name)
    username = data.aws_ecr_authorization_token.token.user_name
    password = data.aws_ecr_authorization_token.token.password
  }
}

```

So how can I put that in the root if I need each module to deploy in a different region?

tree:

```auto
.
├── Modules
│ ├── CodePipeline
│ │ ├── Slack
│ │ │ ├── requirements.txt
│ │ │ ├── slack-messenger.py
│ │ │ └── slack-setup.sh
│ │ ├── Templates
│ │ │ ├── sam-deploy.yml.tftpl
│ │ │ ├── slack-init.yml.tftpl
│ │ │ ├── terraform-apply.yml.tftpl
│ │ │ └── validation.yml.tftpl
│ │ ├── codebuild.tf
│ │ ├── codepipeline.tf
│ │ ├── iam.tf
│ │ ├── main.tf
│ │ ├── s3.tf
│ │ ├── sg.tf
│ │ └── var.tf
│ ├── Cognito
│ │ ├── Scripts
│ │ │ ├── cognito-generate-token.js
│ │ │ └── cognito-message.js
│ │ ├── cognito.tf
│ │ ├── lambda.tf
│ │ ├── main.tf
│ │ └── var.tf
│ ├── Ecs
│ │ ├── WebApi
│ │ │ ├── Templates
│ │ │ │ └── container-definitions.json.tftpl
│ │ │ ├── acm.tf
│ │ │ ├── cloudwatch.tf
│ │ │ ├── ecr.tf
│ │ │ ├── ecs.tf
│ │ │ ├── glue.tf
│ │ │ ├── main.tf
│ │ │ ├── route53.tf
│ │ │ ├── var.tf
│ │ │ └── waf.tf
│ │ ├── ecs.tf
│ │ ├── main.tf | (kreuzwerker docker provider declared here)
│ │ ├── s3.tf
│ │ ├── sg.tf
│ │ ├── sns.tf
│ │ ├── var.tf
│ │ └── waf.tf
│ └── TimeScaleDB
│ ├── Scripts
│ │ ├── Dockerfile
│ │ ├── entry.sh
│ │ └── pgbouncer.ini
│ ├── Templates
│ │ ├── container-definitions.json.tftpl
│ │ ├── user_data-db-prm.sh.tftpl
│ │ └── user_data-db-stb.sh.tftpl
│ ├── cloudwatch.tf
│ ├── ec2.tf
│ ├── ecr.tf
│ ├── ecs.tf
│ ├── main.tf | (kreuzwerker docker provider attempted to be added here)
│ ├── route53.tf
│ ├── s3.tf
│ ├── sg.tf
│ ├── sns.tf
│ └── var.tf
├── Scripts
│ └── git-hash.sh
├── main.tf
└── var.tf

```

I have the docker provider declared in `./Modules/Ecs/main.tf`

I tried to also put it in `./Modules/TimeScaleDB/main.tf` and now I have the error.

I don’t understand how I can declare the docker provider in root because each module has its own associated region:

```auto
module "database_ec2_us" {
  count = contains(["demo", "dev"], local.environment) ? 1 : 0

  source = "./Modules/TimeScaleDB"

  ...
  ...

  providers = {
    aws = aws.us-east-2
  }
}

```

```auto
module "ecs_us" {
  source = "./Modules/Ecs"
  
  ...
  ...

  providers = {
    aws = aws.us-east-2
  }
}

```

We have modules for us-east-2 and eu-central-1.

---

<div class="post-metadata">

**Author:** ![wblakecannon](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/wblakecannon/32/1737_2.png) [@wblakecannon](https://discuss.hashicorp.com/u/wblakecannon)\
**Post date:** [October 15, 2024, 6:11pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625/6 "2024-10-15T18:11:16Z")

</div>

Do I fix it by adding this to root `main.tf`:

```auto
provider "docker" {
  alias = "us-east-2"

  registry_auth {
    address = format("%v.dkr.ecr.%v.amazonaws.com", data.aws_caller_identity.current.account_id, "us-east-2")
    username = data.aws_ecr_authorization_token.token.user_name
    password = data.aws_ecr_authorization_token.token.password
  }
}

provider "docker" {
  alias = "eu-central-1"

  registry_auth {
    address = format("%v.dkr.ecr.%v.amazonaws.com", data.aws_caller_identity.current.account_id, "eu-central-1")
    username = data.aws_ecr_authorization_token.token.user_name
    password = data.aws_ecr_authorization_token.token.password
  }
}

```

And then adding these to each module?:

```auto
providers = {
    aws = aws.us-east-2
    docker = docker.us-east-2
  }

```

```auto
providers = {
    aws = aws.eu-central-1
    docker = docker.eu-central-1
  }

```

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/hashicorp/original/1X/79bf76adfd92a75631996933098f9ce29d37abf3.svg) [@system](https://discuss.hashicorp.com/u/system)\
**Post date:** [April 13, 2025, 9:14pm UTC](https://discuss.hashicorp.com/t/multi-region-modules-with-count/68625/8 "2025-04-13T21:14:56Z")

</div>

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.
