Terraform trying to install incorrect provider

Hi,

I’m using terraform 0.15.1 and am getting this error, but I don’t understand how terraform is thinking this should be happening.

$ terraform init
Initializing modules...
- network in modules/vm-template

Initializing the backend...

Initializing provider plugins...
- Finding latest version of telmate/proxmox...
- Finding latest version of hashicorp/proxmox...     <==== what is this?
- Installing telmate/proxmox v2.6.8...
- Installed telmate/proxmox v2.6.8 (self-signed, key ID A9EBBE091B35AFCE)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html
╷
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider hashicorp/proxmox: provider registry registry.terraform.io does not have a provider named registry.terraform.io/hashicorp/proxmox
│ 
│ Did you intend to use telmate/proxmox? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on hashicorp/proxmox, run the following
│ command:
│     terraform providers
╵
$ terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/telmate/proxmox]
└── module.network
    └── provider[registry.terraform.io/hashicorp/proxmox]

My code looks like
main.tf

terraform {
  required_version = ">= 0.15"
  required_providers {
    proxmox = {
      source = "telmate/proxmox"
    }
  }
}

provider "proxmox" {
  pm_tls_insecure = true
  pm_api_url = "https://10.11.12.13:8006/api2/json"
  pm_user = "root@pam"
  pm_password = "vagrant"
}

module "network" {
  source         = "./modules/vm-template"
  vm_name = "whatever"
}

modules/vm-template/main.tf

variable "vm_name" {
  type = string
}

resource "proxmox_vm_qemu" "template" {
  count       = 1
  name        = var.vm_name
  target_node = "pve1"
  clone       = "template"
  full_clone  = "true"
  os_type     = "cloud-init"

  lifecycle {
    ignore_changes = [
      network,
      target_node,
    ]
  }
}

Per the initial error message, I specify the terraform provider in my module which doesn’t end up working.
main.tf

terraform {
  required_version = ">= 0.15"
  required_providers {
    proxmox = {
      source = "telmate/proxmox"
    }
  }
}

provider "proxmox" {
  pm_tls_insecure = true
  pm_api_url = "https://10.11.12.13:8006/api2/json"
  pm_user = "root@pam"
  pm_password = "vagrant"
  alias = "pm"
}

module "network" {
  source         = "./modules/vm-template"
  vm_name = "whatever"
  providers = {
    proxmox = proxmox.pm
  }
}
$ terraform  init
Initializing modules...
There are some problems with the configuration, described below.

The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.
╷
│ Warning: Provider proxmox is undefined
│ 
│   on main.tf line 74, in module "network":
│   74:     proxmox = proxmox.pm
│ 
│ Module module.network does not declare a provider named proxmox.
│ If you wish to specify a provider configuration for the module, add an entry for proxmox in the required_providers block within the module.
╵

╷
│ Error: Invalid type for provider module.network.provider["registry.terraform.io/hashicorp/proxmox"]
│ 
│   on main.tf line 74, in module "network":
│   74:     proxmox = proxmox.pm
│ 
│ Cannot use configuration from provider["registry.terraform.io/telmate/proxmox"].pm for module.network.provider["registry.terraform.io/hashicorp/proxmox"]. The given provider configuration is for a different provider
│ type.
╵

Hi @pgporada,

The intent of the first error message you saw is to direct you to specify that your child module requires this provider, by adding a block like this to one of the files in the ./modules/vm-template directory:

terraform {
  required_providers {
    proxmox = {
      source = "telmate/proxmox"
    }
  }
}

This statement will tell Terraform that when you say proxmox in that module you mean telmate/proxmox rather than hashicorp/proxmox, and thus the two modules (your root module and this one) will then agree about which proxmox provider they both depend on.

3 Likes

Aha, that’s new to me and I didn’t understand it from the error or documentation. Thank you!

Edit: Re-reading the error message again the 100th time it does make sense now.

@apparentlymart, to follow up on this old thread, I was avoiding doing this because our docs indicate that features such as depends_on and for_each are incompatible with modules that have their own provider definitions.

A module intended to be called by one or more other modules must not contain any provider blocks. A module containing its own provider configurations is not compatible with the for_each , count , and depends_on arguments that were introduced in Terraform v0.13. For more information, see Legacy Shared Modules with Provider Configurations.

However, when I added the provider block to my local module, the functionality of for_each was retained.

In my case:

/providers.tf - aws as the only required provider for the root level terraform project.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">3.0.0"
    }
  }
}
provider "aws" {
  region = var.aws_region
}

/modules/local_kube_module - Kubernetes module that is stored in the root modules directory, but is used by a terraform project that is a child of the root project, a folder called /working-environment

/working-environment/providers.tf

terraform {
  required_providers {
    kubernetes = {
      source = "hashicorp/kubernetes"
      version = "2.8.0"
    }
    kustomization = {
      source = "kbst/kustomization"
      version = "0.8.2"
    }
  }
}

provider "kubernetes" {
  host                   = var.kube_cluster_endpoint
  cluster_ca_certificate = base64decode(var.kube_cluster_ca)
  config_path = "~/.kube/config"
}

provider "kustomization" {
  context = var.cluster_name
  kubeconfig_path = "~/.kube/config"
}

terraform init fails because it looks up an invalid provider name: hashicorp/kustomization, which terraform says is required by ../modules/local_kube_module.

(venv) ❯ working-environment ✘ terraform init
Initializing modules...
- kubernetes_resources in ../modules/local_kube_module

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/kustomization...
- Finding hashicorp/kubernetes versions matching "2.8.0"...
- Finding kbst/kustomization versions matching "0.8.2"...
- Installing hashicorp/kubernetes v2.8.0...
- Installed hashicorp/kubernetes v2.8.0 (signed by HashiCorp)
- Installing kbst/kustomization v0.8.2...
- Installed kbst/kustomization v0.8.2 (self-signed, key ID A72E13094BE75DDF)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html
╷
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider hashicorp/kustomization: provider registry registry.terraform.io does not have a provider named registry.terraform.io/hashicorp/kustomization
│ 
│ Did you intend to use kbst/kustomization? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on hashicorp/kustomization, run the following command:
│     terraform providers
╵

In the way that I understand module provider inheritance:

  • …/modules/local_kube_module should inherit the providers from /working-environment/providers.tf as the module is invoked here, and a provider block is defined.

  • What occurs is that since there are no providers defined in this local module, any third party module appears to fall back onto the hashicorp namespace, which fails. In this case, it looked for hashicorp instead of the correct namespace, kbst. This is just a gut feeling, I don’t have evidence for this aside from my reporting here.

  • After adding the providers to /modules/local_kube_module which is invoked from /working-environment, terraform finds the correct namespace for the provider in the registry.

So the two asks I have here:

  • Is the documentation correct surrounding support for for_each in modules with their own provider definitions? What am I incorrect in my understanding if so?

  • Is the behavior of provider inheritance as shown above intentional, or a side effect of current terraform functionality? Do we support, or intend to support calling third party modules from sibling folders, in which the sibling defines the providers, but the invoked module does not define providers for itself?

Thanks!

Hi @webdog,

I believe the key point here is to recognise the difference between:

  • Declaring a provider requirement - terraform { required_providers { ... } }
  • Defining a provider configuration - provider "..." { ... }

Every module must declare its provider requirements - unless it can rely on the implicit defaulting of the “hashicorp/” prefix for common providers.

Just declaring requirements does not invalidate for_each etc. - only defining configuration does that.

The absence of the declaration in local_kube_module, is why Terraform is trying to resolve hashicorp/kustomization - this needs fixing. Once you do fix it, provider inheritance should work. Right now it can’t work because you can’t inherit a kbst/kustomization to satisfy a need for a hashicorp/kustomization.

But when you’re fixing it, you should only be adding the provider requirement to local_kube_module - not also the provider configuration. That gets inherited.

Does that make sense?

Thanks for those details @maxb!

I just wanted to add one more detail in the hope that it helps bring these ideas together:

When provider configurations pass between modules, whether by inheritance or by explicit passing, the configurations are tracked by the global provider source address kbst/kustomization, and not by whatever local names each module choose for that provider in required_providers.

Taking an example like the one in an earlier comment:

terraform {
  required_providers {
    kustomization = {
      source = "kbst/kustomization"
      version = "0.8.2"
    }
  }
}

This declaration tells Terraform three separate things:

  • This module needs the provider kbst/kustomization.
  • This module is only compatible with exactly version 0.8.2 of that provider.
  • Elsewhere in this particular module, we will refer to this provider as “kustomization” for short, so that we don’t need to keep restating the whole global provider address kbst/kustomization.

An analogy that might help with this is to imagine I am walking between rooms and introducing yourself to different groups who cannot see or hear each other. To the first group I might say “I am Martin Atkins, but please call me Martin”, and then to the second group I might say “I am Martin Atkins, but please call me apparentlymart.” Within those rooms the participants can use the nicknames I specified, but if later on people from group 1 need to talk to group 2 then they will need to use my full name “Martin Atkins” in order to be clear about who they mean, because they don’t have a shared agreement about what my “nickname” is.

Now in practice we do tend to use the last part of a provider’s name as its local name by convention, except in the rare case that a module uses two providers in different namespaces that have the same name – hashicorp/aws and apparentlymart/aws, for example. But that’s just a convention to help human readers, not something Terraform itself relies on. For Terraform itself, local names are always scoped only to a particular module’s source code, and we use the full source address as the real identifier for all tracking of the relationships between resources and their provider configurations, and provider configurations and their providers, so that it’s possible to pass provider configurations across module boundaries.

@apparentlymart Thanks. Indeed helped.
But I did not get why I need to specify the provider twice once in root module and again in child module. Could you please explain same? Thanks

They are doing very different things:

In the root module you are saying you want to install specific providers.

In other modules you are saying you need certain providers.

So for example a module might say “I need aws 4 or above” and another module might say “I need aws 5.3 or above and github 2.2 or above”. Those are the modules describing what it needs, in just the same way as it describes the variables you need to pass into it via variable blocks.

To root module however is saying what you actually are going to use. For example you might say “use aws 5.6, github 2.5 and grafana 2.4”. These versions would need to be compatible with whatever the modules say they need for it to actually work.

The details in the child module are important as you can’t assume it will work with any version of each provider - the code is usually only compatible with a particular major version of the provider, and the module might use features that are only available starting in a particular provider version - the required_providers inside the module documents that (and tells Terrafor so it can ensure you don’t install incompatible versions).

In addition to this, there’s a further detail that’s worth knowing:

Each module defines its own set of dependencies because otherwise the meaning of a module would vary depending on where it was called from. It would be strange if a module that was written for kbst/kustomization ended up using some other random provider whose name happens to be “kustomization” just because it was called from a module that uses that provider.

What’s going on here is that required_providers is mapping from a global namespace into a local one:

kbst/kustomization is a shorthand for registry.terraform.io/kbst/kustomization and that is the global identifier for this provider across all modules.

In my example above, I decided that the local name in one particular module would be kuztomization, which means that in the rest of the module I can just use that short name and not have to constantly restate kbst/kustomization. But that decision must be made on a per-module basis because some other module might need to use a provider from a different namespace that happens to also be called “kustomization”.

In the most extreme case, a particular module might need to use two different modules that both have the name “kustomization” but come from different namespaces, but thankfully that situation doesn’t arise very often because provider names normally match SaaS brands or software brands and it’s unlikely to need to use two different providers to manage the same piece of software.

Terraform’s provider installer searches all of the modules in your configuration and notices whenever two or more modules mention the same source address. If two different modules both refer to kbst/kustomization then that means they are both using the same provider and so they can share a single provider configuration. If one module were using kbst/kustomization while another were using example/kustomization then that would be two entirely separate providers from Terraform’s perspective, and so each one would need its own provider block and both would need to be installed.