Stuck on getting 'outputs.tf' to work for multiple instances generated via for_each on display name list

Using this as a reference: Terraform module

I have created a module that generates multiple computer instances in an Oracle Cloud Infrastructure tenancy, per display name that it receives as a list. However, I am stuck with the output component.

As per this YouTube video: HashiCorp 0.12 tour (the link is set to the timestamp I am referring to), you do not need to output specific attributes, and can just output the entire thing. This works fine for me when creating a single computer, as I can reference the output, and attributes I require.

Example

# Compute module main.tf
resource "oci_core_instance" "test-instance" {
    ...
}

# Compute module outputs.tf
output "instance" {
    value = oci_core_instance.test-instance
}

...

# main.tf #

module "test_instance" {
    ...
}

resource "test_volume" {
    ...
}

resource "oci_core_volume_attachment" "test_volume_attachment" {
    ...
    instance_id = "${module.test_instance.instance.id}"
}

However, when trying to to do this when it generates multiple computers - as shown in the code as show below, I get errors such as “the key expression produced an invalid result” for the ‘outputs.tf’ file.

Can someone tell me where I have gone wrong in the output, please?

Example code would be really helpful.

modules/compute/main.tf

resource "oci_core_instance" "test-instance" {

    for_each = local.display_name

    availability_domain = "${var.availability_domain}"
    compartment_id = "${var.compartment_id}"
    shape = "${var.shape}"
    subnet_id = "${var.subnet_id}"
    display_name = "${each.key}"
    source_details {

        source_id = "${var.source_details-source_id}"
        source_type = "image"
    }
}

modules/compute/variables.tf

variable "availability_domain" { default = "NZWJ:AP-SYDNEY-1-AD-1" 
variable "compartment_id" {}
variable "shape" {}
variable "subnet_id" {}
variable "display_name" {

    type = list(string)
    default = []
}
locals { display_name = { for v in var.display_name: v => v } }
variable "preserve_boot_volume" { default = false }
variable "source_details-source_id" {}

modules/compute/outputs.tf

output "instance" {

    value = {

        for instance in oci_core_instance.test-instance:
        instance => instance
    }
}

main.tf

...

module "test_instance" {

    source = "./modules/compute"
    compartment_id = "${oci_identity_compartment.test_compartment.compartment_id}"
    shape = "VM.Standard.E2.1"
    subnet_id = "${oci_core_subnet.test_subnet.id}"
    display_name = list("Test One", "Test Two")
    source_details-source_id = "ocid1.image.oc1.ap-sydney-1.aaaaaaaazy24niulp5e5a5oyaadjrwnwoa2g6f2hay2f26dqy63pn5sljjma"
}

resource "oci_core_volume" "test_volume" {

    availability_domain = "NZWJ:AP-SYDNEY-1-AD-1"
    compartment_id = "${oci_identity_compartment.test_compartment.compartment_id}"
    display_name = "Test Volume"
    size_in_gbs = 50
}

resource "oci_core_volume_attachment" "test_volume_attachment" {

    attachment_type = "paravirtualized"
    instance_id = "${module.test_instance.id}"
    volume_id = "${oci_core_volume.test_volume.id}"
    display_name = "Test Volume Attachment"
}

Hi @ASG-Github-Admin!

I think the problem here is in your output "instance" for expression:

output "instance" {
    value = {
        for instance in oci_core_instance.test-instance:
        instance => instance
    }
}

instance in there is an object value, so it can’t be used as a map key: map keys can only be strings.

Since you’re using for_each with oci_core_instance.test-instance anyway, its value is already a map with string keys you could potentially return directly:

output "instance" {
    value = oci_core_instance.test-instance
}

If the strings from var.display_name are not suitable keys to return, you can use a for expression to transform it like you already tried but you’ll need to select a specific string attribute from the object to use as the key. For example:

output "instance" {
    value = {
        for instance in oci_core_instance.test-instance:
        instance.id => instance
    }
}

I used the id attribute in the above example because I’m not really familiar with oci_core_instance and so I don’t know what might serve as a good identifier otherwise. However, I will caution that because id is a value not known until apply time it may not be a good key to use if you intend to use this map result as the for_each expression for some other resource; for_each requires that all of the map keys be known at plan time, and so your display names would be a better candidate for that.

1 Like

Thanks for taking the time to respond to me.