Help! Provider aliases no longer working since .14

I’m sure I’m missing something here. I used to be able to do this:


###  Provider
provider "aws" {
    alias    = "foo"
    region   = "ca-central-1"
}

module "s3test" {
  providers = {
    aws = aws.foo
  }
  count = 1
  source = "./modules/s3"
  bucket_name = "cwb-s3test-13579-${count.index +1}"
  configure_public_access_block = 0
}

Now I get warnings:

└─<!master!>$ tfp
module.s3test[0].aws_s3_bucket.this: Refreshing state... [id=cwb-s3test-13579-1]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
╷
│ Warning: Provider aws is undefined
│ 
│   on stack.tf line 13, in module "s3test":
│   13:     aws = aws.foo
│ 
│ Module module.s3test does not declare a provider named aws.
│ 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 can’t seem to get rid of these, having tried using the required_providers in the terraform block without success.

What am I missing here? My use case is defining multiple accounts or regions in provider blocks, then calling them as aliases to deploy modules.

I assume we used to inherit from the root module and now we _just_don’t? Can I ask why this was done? It’s created quite the workload for us to upgrade, and it’s also going to create work in the future so that’s also not-swell.

I’d love to understand what I’m not taking advantage of here. Currently it’s keeping us pinned on 0.13.x iirc, and we won’t be able to upgrade anytime soon. The warnings trip our pipelines so it’s not ok that “it’s just a warning”.

This is just argh imho.

Hi @aardvarq, I’m having the same issue. Where you able to fix it ?

Hi @aardvarq and @alejo_menocal,

The warning here is indicating that the module does not declare the provider name in required_providers, so the solution is to ensure that all modules have the providers they expect to use declared via required_providers. This allows terraform to statically validate the usage of providers in the configuration, preventing many common errors and confusing results.

While we left this as a warning for compatibility reasons, the documentation strongly suggests that all modules declare the specific providers they require. See: Provider Requirements and Providers Within Modules

So I have a TF setup like this …

terraform {
   required_version = ">= 1.0.0"
   required_providers {
       aws = {
       }
   }
}

provider "aws" {
}

module "example" {

providers= {
  aws = aws
}
}

Even in this situation where I have specified the ‘aws’ provider under ‘required_providers’ it still gives that warning:

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

But I don’t see any documentation which instruct to use ‘required_providers’ under module or is it? How should I approach this?

Hi @p-pal,

The warning means that it’s module.example which does not declare the provider, and is missing an entry in required_providers. The documentation (linked above here) states:

Although provider configurations are shared between modules, each module must declare its own provider requirements, …

The reason this sucks is now I need to add providers to all of my modules, instead of in one place. How do you deal with a remote module, it won’t have your provider details?

Still waiting to hear why this was done, it’s painful rather than helpful at my sites.

Hi @jbardin, thanks for your reply.

From my understanding, I did define the provider ‘aws’ under ‘required_providers’ section of the main terraform block:

terraform {
  required_version = ">= 1.0.0"

  required_providers {
    aws = {
        source  = "hashicorp/aws"
        version = ">= 1.24.0"
    }
   }
}

In the same file, I initiate a module & declare that this module must use providers aws = aws,

module "example" {
   providers = {
    aws = aws
   }
  source = "./example"
}

When I use required_providers under module, it says it is unsupported.

module "example" {
   required_providers = {
    aws = aws
   }
  source = "./example"
}

Error: Unsupported block type
│ 
│   on main.tf line 6, in module "database":
│    6:    required_providers {
│ 
│ Blocks of type "required_providers" are not expected here.

It would be great if I can get a simple example of what you are referring to, thanks :slight_smile:

@p-pal, the line “each module must declare its own provider requirements” means required_providers needs to be declared within the actual module itself, not the module block or the parent module. Because the behavior of the module and the validity of the configuration depends on the version and source and names of the providers, modules should declare which providers are required.

1 Like

Thanks @jbardin.

For anyone who is stuck with this warning, this helped me:

File structure

– main.tf
– – module.tf

main.tf

terraform {
  required_version = ">= 1.0.0"
  required_providers {
    aws = {
        source  = "hashicorp/aws"
        version = ">= 1.24.0"
    }
  }
}

provider "aws" {
 region  = "us-east-1"
 profile = "saml"
}

module "database" {
   providers = {
    aws = aws
   }
  source = "./database"
}

defined the required_providers under the module (not in the module block)

terraform {
  required_providers {
    aws = {
        source  = "hashicorp/aws"
        version = ">= 1.24.0"
    }
  }
}
resource "aws_instance" "app_server" {
  ami           = "ami-***"
  instance_type = "t2.micro"
  tags = {
    Name = "ExampleAppServerInstance"
  }
}
2 Likes

Finally, an answer and an example of a working solution to the problem. Thank you community; hashicorp - please note this in your documentation for others to use.

3 Likes

I would like to take this example just one small step further.
In my case, we were calling the shared module and passing in different alias provider. Here is the above example but with two different providers in the root tf calling the shared module.

- main.tf
-- module.tf

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
    }
  }
  required_version = ">= 0.15"
}

provider "aws" {
  region  = "us-west-2"
  alias   = "us-west-2"
  profile = "default"
}

provider "aws" {
  region  = "us-east-1"
  alias   = "us-east-1"
  profile = "default"
}

module "database_west" {
   providers = {
    awswest = aws.us-west-2
   }
  source = "./database"
}

module "database_east" {
   providers = {
    awseast = aws.us-east-2
   }
  source = "./database"
}

defined the required_providers under the module (not in the module block)

terraform {
  required_providers {
    awswest = {
        source  = "hashicorp/aws"
        version = ">= 1.24.0"
    }
    awseast = {
        source  = "hashicorp/aws"
        version = ">= 1.24.0"
    }
  }
}
resource "aws_instance" "app_server" {
  ami           = "ami-***"
  instance_type = "t2.micro"
  tags = {
    Name = "ExampleAppServerInstance"
  }
}

Hi @vbarros,

What you shared here ends up working through a series of coincidences, but isn’t really the intended design and this solution doesn’t generalize across all providers.

The local names for providers within a module are only to avoid retyping the full name hashicorp/aws everywhere, and so declaring two different local names for the same provider doesn’t really do anything – honestly, it should probably be disallowed, but I guess we missed a validation rule for that.

Below the surface configuration level, Terraform tracks providers internally by the full source address (registry.terraform.io/hashicorp/aws) and so all of these different provider_requirements entries are all talking about the same provider, and all of your provider "aws" blocks are declaring configurations for the same provider.

Your “under the module” example includes a resource "aws_instance" block that doesn’t have the provider meta-argument set, to Terraform will by default try to associate it with a provider whose local name is aws. You don’t have any such provider in your module, but it happens to work anyway because of the backward-compatibility rule that an undeclared dependency is implicitly a hashicorp/ source, and so that resource ends up associated with the default configuration for registry.terraform.io/hashicorp/aws. Those awseast and awswest local names are only declaring version constraints and not otherwise contributing to the configuration at all.

Here’s an example equivalent to yours which avoids implicitly depending on the backward-compatibility fallback and matches the recommendations in the documentation

The root module:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
    }
  }
  required_version = ">= 0.15"
}

provider "aws" {
  region  = "us-west-2"
  alias   = "us-west-2"
  profile = "default"
}

provider "aws" {
  region  = "us-east-1"
  alias   = "us-east-1"
  profile = "default"
}

module "database_west" {
  source = "./database"
  providers = {
    aws = aws.us-west-2
  }
}

module "database_east" {
  source = "./database"
  providers = {
    aws = aws.us-east-2
  }
}

The ./database module:

terraform {
  required_providers {
    aws = {
        source  = "hashicorp/aws"
        version = ">= 1.24.0"
    }
  }
}
resource "aws_instance" "app_server" {
  ami           = "ami-***"
  instance_type = "t2.micro"
  tags = {
    Name = "ExampleAppServerInstance"
  }
}

This is a situation where the child module only needs a single (“default”) provider configuration for aws, but in the calling module we assign different additional (aliased) configurations to it for each instance of the module, which creates the effect of pointing each module call at a different provider configuration even though the module itself is completely unaware that there are multiple configurations for that provider.

1 Like

@apparentlymart Can I just clarify from your response, does that mean from terraform 0.15, we have to duplicate the required_providers block in every single module where we want to pass a provider alias into a sub-module by using providers = { aws = aws.somealias }?

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

We didn’t have to do this in terraform 0.14, which this post is about, but as of upgrading to 0.15.5, we are seeing all these warnings now which we didn’t see before.

In terraform 0.14.x, our setup is as below, and we don’t get this “provider aws is undefined warning” – only when upgrading to 0.15.x are we seeing this…

main.tf:

terraform {
  required_version = "= 0.15.5"

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

provider "aws" {
  region = "ap-southeast-2"
  alias  = "ap-southeast-2"
}

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

module "vpc" {
  source = "../../shared/vpc"

  name               = "vpc"
  cidr_block         = var.cidr_block
  availability_zones = var.availability_zones[local.region_ap_southeast_2]

  providers = {
    aws = aws.ap-southeast-2
  }
}

module "vpc" {
  source = "../../shared/vpc"

  name               = "vpc"
  cidr_block         = var.cidr_block
  availability_zones = var.availability_zones[local.region_ap_southeast_2]

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

This creates one vpc module for us west 2, and one for ap southeast 2. Previously we didn’t seem to need a required_providers block in the VPC module itself, as we passed aws down. The only way it seems to remove the provider is undefined warning, is to add the required_providers block to every module. Is that the correct way? The documentation that covers our case is the module “example” here, it would be good if the docs showed what was in the example module, as the next example, with 2 aliased providers, does not match our use case - we only need to pass one provider down to submodules.

I have got about 20 tabs open from trying to read all the documentation, and can’t find if the “correct” way, is to add about another 1000 lines of code to our repo to copy and paste required_providers in every single submodule…

This was an excellent example. Thank you for explaining it so well with illustrated examples.
I think I thought that the aliases were to be defined inside of the terraform.reequired_providers {} block, but it would seem that they can live outside of that. I also understand that in v. 0.15.x and above there is an additional option you can put inside of that block to support it that looks like:
"configuration_alias = [“uswest1”, “useast2”]