Defining provider aliases with string interpolation not working in Terraform 0.12

Hi,

I am trying to upgrade to Terraform 0.12 from Terraform 0.11.14, however, I am getting issues when trying to define resources that use different providers.

For instance, I try to define a resource such that:

variable region {}

resource "...." "..." {
    provider = "kubernetes.mycluster-${var.region}"
    ...
}

But terraform 0.12 gives this error:

Error: Invalid provider reference
Provider argument requires a provider name followed by an optional alias, like "aws.foo".

I am not sure what is the correct way of templating here.

Thanks!

EDIT: Minimum reproducible example
Template for Terraform 0.11.14 (works as expected)

variable "region" {}

provider "google" {
  credentials = "${file("adc.json")}"
  project     = "my-project-id"
  region      = "${var.region}"
}

provider "kubernetes" {
  alias  = "cluster-${var.region}"
}

resource "kubernetes_config_map" "autoscaler" {
  provider = "kubernetes.cluster-${var.region}"

  metadata {
    name = "autoscaler"
  }

  data {
    "foo"         = "bar"
  }
}

Upgraded template using terraform 0.12upgrade

variable "region" {
}

provider "google" {
  credentials = file("adc.json")
  project     = "my-project-id"
  region      = var.region
}

provider "kubernetes" {
  alias = "cluster-$${var.region}"
}

resource "kubernetes_config_map" "autoscaler" {
  provider = "kubernetes.cluster-$${var.region}"

  metadata {
    name = "autoscaler"
  }

  data = {
    "foo" = "bar"
  }
}

Notice that it produces the provider alias as kubernetes.cluster-$${var.region} which would be translate to this string literal kubernetes.cluster-${var.region} rather than kubernetes.cluster-central1 if the value provided for region was central1.

If I remove the double $$ and replace it with a single $ for string interpolation, I get the following error using client version 0.12:

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: Unsuitable value type

  on master.tf line 11, in provider "kubernetes":
  11:   alias = "cluster-${var.region}"

Unsuitable value: value must be known


Error: Variables not allowed

  on master.tf line 11, in provider "kubernetes":
  11:   alias = "cluster-${var.region}"

Variables may not be used here.


Error: Invalid provider configuration reference

  on master.tf line 15, in resource "kubernetes_config_map" "autoscaler":
  15:   provider = "kubernetes.cluster-${var.region}"

A provider configuration reference must not be given in quotes.

@harsh-dialpad could you share the Terraform configuration (and resources) you are trying to migrate?

Hi @mishra, thanks for your response. I have added a minimal reproducible example to highlight the issue as an edit in the question.

Hi @harsh-dialpad!

Provider selections cannot be dynamic like this. Although it didn’t produce an error in Terraform 0.11, it also didn’t work: Terraform 0.11 just ignored it and treated it as a literal string, just as the terraform 0.12upgrade tool showed. Terraform 0.12 has an explicit validation check for it to give you better feedback that it’s not supported.

The connections between resources and their providers happens too early for Terraform to be able to evaluate expressions in that context, because the provider must be known in order to understand the other contents of the block.

If the configuration you’re showing here is in a re-usable module that is intended to work in any region, you should configure the providers in the calling module and then pass them explicitly to the child module:

provider "google" {
  alias = "uswest"

  region = "us west"
}

provider "kubernetes" {
  alias = "uswest"

  # other region-specific settings
}

module "example" {
  source = "./modules/google-cloud-kubernetes"

  # (other module arguments, but no longer "region"
  # because that is implied by the provider configurations
  # passed below)

  providers = {
    google     = google.uswest
    kubernetes = kubernetes.uswest
  }
}

The providers map in the above example states that the unaliased provider google in the child module uses the same configuration as the google.uswest provider in the root module, and likewise for the kubernetes provider. As a result, you don’t need to use provider arguments on any of the resource blocks inside the module, because from the module’s perspective it is the unaliased provider you need to use.

This method allows instantiating the same module multiple times with different provider aliases to create infrastructure across multiple regions, while still allowing Terraform to understand exactly which provider configuration each resource belongs to without resolving any expressions.

4 Likes

Is this also the case with a provider on resources within a module?

In a scenario where valid regions are all 4 aws usa regions but each account is enabled in 1-2 regions we still need to figure a solution. So if I have a module I want to apply to an account in either 1 or 2 regions, each of which could be any of the valid US regions. I’ve tried solving this various ways but can’t come up with a good solution I have terraform 0.12.13