Terraform: for_each queue creation from YAML data source fails on type conversion

Trying to generate multiple queues from a YAML config file using the external data source and for_each. The YAML has a list of queue objects, but Terraform chokes on the type conversion when mapping the YAML list to the genesyscloud_queue resource.

locals {
 queue_config = yamldecode(file("queues.yaml"))
}

data "external" "queue_map" {
 program = ["node", "./parse-queues.js"]
 query = {
 queues = local.queue_config
 }
}

resource "genesyscloud_queue" "queue" {
 for_each = jsondecode(data.external.queue_map.result.queues)
 name = each.value.name
 description = each.value.desc
}

The node script outputs a JSON string, but jsondecode fails with Inappropriate value for attribute "for_each": must be a map or set of strings, but have object. The YAML structure is just an array of objects. How do I map an array of objects to for_each without manually creating keys? The for_each docs say keys are required, but I don’t have unique IDs in the YAML yet.

This feels like a classic case of passing complex nested objects through the external data source when you really shouldn’t. The external data source expects a flat JSON object as input and output. When you shove a YAML list into it, the serialization gets messy, and Terraform’s type checker throws a fit because it can’t reconcile the dynamic types.

You don’t actually need the external data source or that Node script for this. You can map the YAML directly in Terraform locals using for_each on the decoded map. The trick is converting the list from your YAML into a map keyed by a unique identifier (like name or id) so for_each has something stable to iterate over.

Here is how I’d restructure it. First, ensure your queues.yaml looks something like this:

- name: Sales Support
 description: General sales queue
 out_of_office:
 enabled: false
 enabled_time: 2023-01-01T00:00:00.000Z
- name: Tech Support
 description: Technical issues
 out_of_office:
 enabled: false
 enabled_time: 2023-01-01T00:00:00.000Z

Then, in your main.tf, skip the external block entirely. Use yamldecode to get the list, then convert it to a map using a local. Terraform handles this conversion natively now.

locals {
 # Decode the YAML file
 queue_list = yamldecode(file("queues.yaml"))

 # Convert the list to a map keyed by name for for_each
 # This ensures stable identifiers during plan/apply
 queue_map = { for q in local.queue_list : q.name => q }
}

resource "genesyscloud_queue" "dynamic_queues" {
 for_each = local.queue_map

 name = each.value.name
 description = each.value.description
 
 # Handle nested blocks carefully
 out_of_office {
 enabled = each.value.out_of_office.enabled
 enabled_time = each.value.out_of_office.enabled_time
 }
}

The key here is that for_each needs a map or a set of strings. By pre-processing the YAML list into a map in the locals block, you avoid the type conversion errors that happen when trying to pass raw lists through external scripts. It’s also much faster since you’re not spinning up a Node process for every plan run.

If your YAML structure is more complex, you might need to flatten some nested attributes in the local map, but for standard queue configs, this direct mapping works cleanly. Just make sure your unique key (here, name) is actually unique across your config, otherwise Terraform will complain about duplicate keys.