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.