I'm hoping you'd be able to assist me.
I've got a Terraform pipeline set up with the hasicorp/vsphere provider and I'm trying to create a virtual machine with 2 NICs from a tfvars file. I'm trying to make a module out of the vsphere_virtual_machine resource and all I'm getting are errors
I've got my tfvars file formatted as follows
Terraform\VMWare\NewBuildCluster\vmware_server_build.tfvars
tfvars = {
# Core vSphere Vars
vsphere_server = "vcentre.example.com" # Address of vCentre
vsphere_datacenter = "vsphere_datacenter" # Location to build new servers
vsphere_datastore = "datastore0001" # Data store to save VM files
vsphere_cluster = "NewBuildCluster" # Cluster to build on
#VM Related Vars
vm = {
TestVM01 = {
vsphere_vm_template = "WindowsTemplate2022" # Name of template
vsphere_vm_name = "TestVM01" # Name of VM
vsphere_vm_cpu_cores = 4 # Total cores
vsphere_vm_num_cores_per_socket = 2 # Cores per socket
vsphere_vm_memory = 8 # RAM assignment (Math conducted in module for MB conversion)
#VM Customisation Variables
vm_nics = {
eth0 = {
vsphere_vm_network = "VLAN10" # Name of Virtual Network (defined in vSphere)
vsphere_vm_domain = "ad.example.com" # Domain to join server to once built
vsphere_vm_ip = "10.200.10.240" # Static assigned IP address
vsphere_vm_ip_gateway = "10.200.10.1" # Gateway IP Address
vsphere_vm_ip_dnslist = ["10.200.10.10", "10.200.10.30"] # List of DNS addresses
},
eth1 = {
vsphere_vm_network = "VLAN10" # Name of Virtual Network (defined in vSphere)
vsphere_vm_domain = "ad.example.com" # Domain to join server to once built
vsphere_vm_ip = "10.200.10.241" # Static assigned IP address
vsphere_vm_ip_gateway = "10.200.10.1" # Gateway IP Address
vsphere_vm_ip_dnslist = ["10.200.10.10", "10.200.10.30"] # List of DNS addresses
}
}
data_disk = {
disk1 = {
size_gb = 50 # Data disk size
},
disk2 = {
size_gb = 10 # Data disk size
}
}
}
}
And my network data block is like this but I can't access the vm_nics as a whole.
Pipeline\VMWare\main.tf
data "vsphere_virtual_machine" "network" {
for_each = flatten([
for vm_key, vm_value in var.tfvars.vm : [
for nic_key, nic_value in vm_value.vm_nics : {
name = nic_value.vsphere_vm_network
datacenter_id = data.vsphere_datacenter.datacenter.id
}
]
])
name = each.value.name
datacenter_id = each.value.datacenter_id
}
This has brought me as close as I can get to the variable but I get this error
│ The given "for_each" argument value is unsuitable: the "for_each" argument
│ must be a map, or set of strings, and you have provided a value of type
│ tuple.
I'd advice you to create a child module to handle the creation of one VM and just loop that module instead.
module "my_module" {
source = "./modules/my_module"
# Deploy this module for each configured VM
for_each = var.tfvars.vm
# Pass VM confiuration (properties inside the VM object)
vm_property = each.value.vm_property
# Pass shared properties (properties not in the VM object)
shared_property = var.tfvars.shared_property
# Pass the NIC configuration for this VM only
vm_nics = each.value.vm_nics
}
Then in your module you could do this:
variable "vm_nics" {
# Can use map(any) but I prefer explicit declaration
type = map(object({
# Declare the expected properties in here
})
}
data "vsphere_virtual_machine" "network" {
# Gives you each NIC object for the VM
for_each = var.vm_nics
name = each.value.vsphere_vm_network
datacenter_id = data.vsphere_datacenter.datacenter.id
}
# Outputs a list of all IDs
output "ids" {
value = data.vsphere_virtual_machine.network.*.id
}
Though I'm a bit confused about this setup, perhaps the vsphere_virtual_machine should be a vsphere_network?
Side note, if you want to you could limit the module to only getting the NIC IDs for each VM. In that case you don't need to declare the VM property variables, just give it the list of NICs for each VM and then you can access them like:
# In the main module, for example when creating the VMs
resource "some_resource" "this" {
for_each = var.tfvars.vm
network_interfaces = module.my_module[each.key].ids
}