Set of module sources

Given a set of names of subdirectories, I am trying to manage each subdirectory as a module:

variable "subs" {
  default = ["a", "b", "c"]
  type    = set(string)

module "foo" {
  for_each = var.subs
  source   = "./${each.key}"

which yields

│ Error: Variables not allowed
│ On line 8: Variables may not be used here.

│ Error: Unsuitable value type
│ On line 8: Unsuitable value: value must be known

What’s the best idiom to express this?

You have to write a separate explicit module block for each subdirectory.

As with many other language ecosystems, Terraform deals with installation of external dependencies (including module packages) as a separate step from execution. In Terraform’s case, that separate step is part of terraform init.

In Terraform’s case, because even local modules like in your case can potentially add new external dependencies, this rule also applies to those.

Because of that, Terraform deals with this source argument during the init step rather than during runtime, and that is why dynamic decisions are not possible there. Instead, you must write a separate module block for each dependency. You can potentially set count inside each block to dynamical decide how many instances of each module to declare though, which may allow you to achieve a similar effect.

Yes, I have already created separate module blocks with conditional selection, plus a comment more readably describing the intent, complete with a link to this thread explaining why I had to unroll the loop:

# module "foo" {
#   for_each = var.subs
#   source   = "./${each.key}"
# }

module "foo-a" {
  count  = contains(var.subs, "a") ? 1 : 0
  source = "./a"

module "foo-b" {
  count  = contains(var.subs, "b") ? 1 : 0
  source = "./b"

module "foo-c" {
  count  = contains(var.subs, "c") ? 1 : 0
  source = "./c"

Presuming directories are not an uncommon way of collecting related files, I was hoping to find a more idiomatic (and later-eval’d) language element than modules to express it.

Since we were working only with a contrived example I didn’t try to suggest any alternative approaches, but if these different directories only represent data of some sort and not Terraform configuration directly, you could potentially use Terraform’s fileset function to enumerate interesting files inside, load them with file and decode them with functions like yamldecode in order to construct a dynamic data structure that you could then use with inline configuration (rather than module blocks) to declare resource instances based on the data structure.

I can’t give a concrete example without a more concrete use-case, but hopefully you can see what I mean and decide if this different approach would be better for your particular goals here.