How to use variable variables for input variable names

How can I use a variable for the name of an input variable? Simple use-case below:

I have a list of input variables:

var.MANAGER_DIGITALOCEAN_NAME
var.MANAGER_DIGITALOCEAN_SIZE
var.WORKER_DIGITALOCEAN_NAME
var.WORKER_DIGITALOCEAN_SIZE

In Terraform I want to spin up a node for_each of these, using variable variables or variable interpolation but I can’t get it working. I get the error:

Error: Invalid character
on droplets.tf line 23, in resource “digitalocean_droplet” “droplets”:
23: image = “{var.{each.value}_DIGITALOCEAN_NAME}”
This character is not used within the language.

Here is what I’m trying:

locals {
  nodes = [
    "MANAGER",
    "WORKER"
  ]
}

resource "digitalocean_droplet" "droplets" {
  for_each = local.nodes
  name = each.value

  image = "${var.${each.value}_DIGITALOCEAN_NAME}"
  size = "${var.${each.value}_DIGITALOCEAN_SIZE}"
}

Hi @J7mbo,

I think best way to do this is to use a map data structure for your input variables so that you can look up the values you need dynamically by key, like this:

variable "digitalocean" {
  type = map(object({
    name = string
    size = number
  }))
}

resource "digitalocean_droplet" "droplets" {
  for_each = var.digitalocean

  name = each.key

  image = each.value.name
  size = each.value.size
}

You can then set this variable to define both the node names and their associated attributes at once:

{
  "MANAGER" = {
    name = "whatever-name"
    size = 2
  }
  "WORKER" = {
    name = "whatever-other-name"
    size = 4
  }
}

If the MANAGER and WORKER node names should not be customizable by the end-user, you can get there by using a more restrictive type for the variable that forces setting both of those:

variable "digitalocean" {
  type = object({
    MANAGER = object({
      name = string
      size = number
    })
    WORKER = object({
      name = string
      size = number
    })
  })
}

With the type constraint above, Terraform will return an error if the caller doesn’t provider both MANAGER and WORKER objects. To use this object-typed variable with for_each might require an explicit conversion to map, though:

  for_each = map(var.digitalocean)
1 Like

Thanks. However the variables come in from environment variables and the names are beyond my control.

Is there no way to use variables within names for other variables?

Terraform cannot directly read variable values from environment variables so I assume there must be something outside of Terraform making those values be set. If you could adjust that to map the environment variables to Terraform in a different way then that would be one way to do it.

Another option, if you can’t control how those variables are being passed to Terraform, would be to build that mapping inside Terraform itself, like this:

variable "MANAGER_DIGITALOCEAN_NAME" {
  type = string
}
variable "MANAGER_DIGITALOCEAN_SIZE" {
  type = number
}
variable "WORKER_DIGITALOCEAN_NAME" {
  type = string
}
variable "WORKER_DIGITALOCEAN_SIZE" {
  type = number
}

locals {
  digitalocean = tomap({
    MANAGER = {
      name = var.MANAGER_DIGITALOCEAN_NAME
      size = var.MANAGER_DIGITALOCEAN_SIZE
    }
    WORKER = {
      name = var.WORKER_DIGITALOCEAN_NAME
      size = var.WORKER_DIGITALOCEAN_SIZE
    }
  })
}

Now you can follow the same principle as what I suggested before but you’d use local.digitalocean instead of var.digitalocean because this is now a local value rather than an input variable.