So I am trying to use Terraform to provision multiple VM's in vSphere. I have a CSV that contains the information for provisioning the guests. When I hard code the data sources the VM's are provisioned, but when I try to use the csv to populate those same sources Terraform plan tells me that the datacenter is not defined. I have tried both CSVDECODE and the interpolation methods, neither has worked.
example csvdecode:
locals {
virtualmachines = csvdecode(file("${path.module}/virtualmachines.csv"))
}
data "vsphere_datacenter" "dc" {
count = length(local.virtualmachines)
name = local.virtualmachines[count.index].dcname
}
example interpolation:
locals {
virtualmachines = csvdecode(file("${path.module}/server_list.csv"))
}
# Creating terraform resources from a CSV file using interpolation
data "null_data_source" "csv_file" {
inputs = {
file_data = "${chomp(file("${path.module}/virtualmachines.csv"))}"
}
}
resource "null_resource" "csv_interpolation_method" {
count = "${length(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))))}"
triggers = {
01 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 0)}"
02 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 1)}"
03 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 2)}"
04 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 3)}"
05 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 4)}"
06 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 5)}"
07 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 6)}"
08 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 7)}"
09 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 8)}"
10 = "${element(split(",", element(slice(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")), 1, length(split("\n", lookup(data.null_data_source.csv_file.outputs, "file_data")))), count.index)), 9)}"
}
}
locals {
dcname = null_resource.csv_interpolation_method.*.triggers.01
storage = null_resource.csv_interpolation_method.*.triggers.02
pool = null_resource.csv_interpolation_method.*.triggers.03
vtemplate = null_resource.csv_interpolation_method.*.triggers.04
vname = null_resource.csv_interpolation_method.*.triggers.05
cpu = null_resource.csv_interpolation_method.*.triggers.06
memory = null_resource.csv_interpolation_method.*.triggers.07
disk = null_resource.csv_interpolation_method.*.triggers.08
network = null_resource.csv_interpolation_method.*.triggers.09
guest_id = null_resource.csv_interpolation_method.*.triggers.10
}
output "server_output" {
value = "${local.dcname}"
}
data "vsphere_datacenter" "dc" {
name = "${output.server_output}"
}
output "storage" {
value = "${local.storage}"
}
data "vsphere_datastore" "datastore" {
name = "${local.storage}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
output "pool" {
value = "${local.pool}"
}
data "vsphere_resource_pool" "pool" {
name = "${local.pool}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
output "network" {
value = "${local.network}"
}
data "vsphere_network" "network" {
name = "${local.network}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
output "template" {
value = "${local.vtemplate}"
}
data "vsphere_virtual_machine" "template" {
name = "${local.vtemplate}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_virtual_machine" "vm" {
count = length(local.virtualmachines)
name = local.virtualmachines[count.index].vname
resource_pool_id = "${data.vsphere_resource_pool.pool.id}"
datastore_id = "${data.vsphere_datastore.datastore.id}"
num_cpus = local.virtualmachines[count.index].cpu
memory = local.virtualmachines[count.index].memory
guest_id = local.virtualmachines[count.index].guest_id
wait_for_guest_net_timeout = 0
network_interface {
network_id = "${data.vsphere_network.network.id}"
}
disk {
label = "disk0"
size = local.virtualmachines[count.index].disk
}
clone {
template_uuid = "${data.vsphere_virtual_machine.template.id}"
sample csv content:
dcname,storage,pool,vtemplate,vname,cpu,memory,disk,network,guest_id
txpla-w01-dc,txpla-w01-vsan,txpla-w01-shared01/Resources,AHLC75-template,terraform-test1,2,1024,80,Guest VLAN 1017,centos64Guest
txpla-w02-dc,txpla-w02-vsan,txpla-w02-shared01/Resources,AHLC75-template,terraform-test2,3,2048,80,Guest VLAN 1017,centos64Guest
Error message from interpolation method (it never makes it down to the resource)
A managed resource "output" "server_output" has not been declared in the root module
Any help would be appreciated..
Thank you
Best Answer
In Terraform,
output
blocks declare values to be passed to the calling module (or, if you're already in the root module, to be shown in the console output after applying).You can't refer to them from within the module that defines them, and so Terraform is interpreting
output.server_output
as a resource reference, like you had a block in your configuration likeresource "output" "server_output"
, from a hypothetical provider named "output" (which doesn't actually exist).If you want to refer to a particular value from multiple places in the same module without duplicating its expression, you can use Local Values instead. You already have
local.dcname
, which is whatoutput "server_output"
refers to, so if you replace theoutput.server_output
reference withlocal.dcname
instead then it should produce the result value you were looking for.