Terraform error 'Call to function “index” failed: item not found.'

Creating terraform for OCI provider. I am trying to create an OCI instance and then later attach a VNIC to it. I am attempting to do this by creating an output list with the display name and id of the compute instance.

Then after this, I have a module which creates the VNIC and I state the display_name of the compute instance I want to attach it to, searching the output from the compute instance module by the display_name and returning the instance id. The problem I have is when I don’t know how to create a list from the output of the instance and it turns into an object.

this is the error message,

Error: Error in function call
  on ../common/networking/vnic_attachment/main.tf line 11, in resource "oci_core_vnic_attachment" "this" 
  11:     instance_id = var.compute_ids[index(var.compute_ids.*.name, var.vnic_attachments[count.index].instance_name)].ocid
    |----------------
    | count.index is 0
    | var.compute_ids is object with 2 attributes
    | var.vnic_attachments is list of object with 2 elements
Call to function "index" failed: item not found.

This is my code,

creating compute instance,

locals {
    compute_ocids                       = oci_core_instance.this.*.id
    compute_names                        = oci_core_instance.this.*.display_name
}


resource oci_core_instance this {
    count                               = length(var.compute_instances)
    availability_domain                 = var.compute_ad
    compartment_id                      = var.compute_compartment_ocid

    display_name                        = var.compute_instances[count.index].instance_name
    shape                               = var.compute_instances[count.index].instance_shape
    
    state                               = var.compute_instances[count.index].instance_state                        #    ToDo:  Change to parameter, if this needs to be configurable
    fault_domain                        = var.compute_instances[count.index].instance_fault_domain
    
    create_vnic_details {
        subnet_id                       = var.vcn_subnets[index(var.vcn_subnets.*.name, var.compute_instances[count.index].instance_subnet)].ocid    #    Lookup Subnet OCID from the Subnet Map using the Subnet Name
        assign_public_ip                = false
    }
    
    source_details {
        boot_volume_size_in_gbs         = var.compute_instances[count.index].instance_block_volume
        source_type                     = var.compute_instances[count.index].instance_source_type
        source_id                       = var.compute_instances[count.index].instance_source_ocid
    }
    
    shape_config {
        memory_in_gbs                   = var.compute_instances[count.index].instance_shape_mem
        ocpus                           = var.compute_instances[count.index].instance_shape_ocpus
    }  
}

output compute_ocids {
    value = {
        name    = local.compute_names
        ocid    = local.compute_ocids
    }
    description = "Compute OCIDs"
}

The code which creates the VNIC,

resource oci_core_vnic_attachment this {
    count = length(var.vnic_attachments)

    create_vnic_details {
        display_name = var.vnic_attachments[count.index].vnic_name
        nsg_ids = var.vnic_attachment_create_vnic_details_nsg_ids
        subnet_id = var.vcn_subnets[index(var.vcn_subnets.*.name, var.vnic_attachments[count.index].vnic_subnet)].ocid
    }
    instance_id = var.compute_ids[index(var.compute_ids.*.name, var.vnic_attachments[count.index].instance_name)].ocid
}

How do I make the output of a looped resource a list?

Not sure I can fix that code specifically but a couple of suggestions.

Use for_each instead of count for the instance and the vnic_attachment. Though an array is easy, if you can make a map with the display name (hopefully unique) as the key and the values like you have for the different variables, then the vnic_attachment could just search for oci_core_resource.this[each.value.instance_name].id and that will do the attachment.

Otherwise, on your outputs/locals, I would do a for loop over the instances with the splat operator like you have it. That should pick up all of the ids and names.

Thank you, and how would I return a list of ( name, id) with the for expression? Examples I have seen only seem to return one item rather than two ?

So I would actually return a map instead of a list. The map will have the name as the key and all the attributes as the values.

output "subnets" {
  description = "The returned resource attributes for the subnets."
  value = oci_core_subnet.this != null && length(oci_core_subnet.this) > 0 ? {
    for x in oci_core_subnet.this :
    x.display_name => x
  } : null
}

I have some extra in there to make sure that it wasn’t null. But the key part is

output "subnets" {
  description = "The returned resource attributes for the subnets."
  value =  { for x in oci_core_subnet.this :
    x.display_name => x
  }
}

That way the map has the display_name as the key and everything else as the values.