Mihai Albert
Mihai Albert

Reputation: 1483

Cycle Through External Data Source in terraform

I'm trying to provision n VMs (assume n=5) in terraform.

The catch is that for each IP that gets assigned to the corresponding VM, I first need to invoke an external tool (an .exe). This tool takes 2 parameters: a regex pattern for the IP to be obtained, and the machine name. The tool then reads out one single free IP address to be used based on the supplied regex, from a Sharepoint Excel file that acts as an IPAM (IP address management solution). The tool also updates the Excel file so that it stamps the supplied machine name next to the IP returned.

For a single VM, this works just fine, and everything completes as expected, leveraging one external data source and one resource. The machine name is read from an input file, same as the regex for the IP, which uses its own file. The external tool is then invoked:

data "external" "Machine_name" {
    program = ["cmd.exe", "/c type c:\\Users\\malbert\\Desktop\\Excel2VM\\MachineName.txt"]
}

data "external" "IPregexMatchPattern" {
    program = ["cmd.exe", "/c type c:\\Users\\malbert\\Desktop\\Excel2VM\\IPregexMatchPattern.txt"]
}

output "MachineName" {
  value = "${data.external.Machine_name.result.name}"
}

data "external" "NetworkObtainedData" {
      program = ["cmd.exe", "/c ExcelUpdateTool.exe ${data.external.IPregexMatchPattern.result.IPregex} ${data.external.Machine_name.result.name} available" ]
}

# Use as an output so the user can see the value as well
output "ip" {
    value = "${data.external.NetworkObtainedData.result.ip}"
}
output "netmask" {
    value = "${data.external.NetworkObtainedData.result.netmask}"
}

output "gw" {
    value = "${data.external.NetworkObtainedData.result.gw}"

Up next, the VM is named based on the input file:

resource "vsphere_virtual_machine" "vm" {
  # Name the VM
  name             = "${data.external.Machine_name.result.name}"

  resource_pool_id = "${data.vsphere_resource_pool.pool.id}"
  datastore_id     = "${data.vsphere_datastore.datastore.id}"

However when trying to scale for n VMs, I'm running into the following issue: I can iterate over n items within the resource section, thus spawning n VMs; however I can't figure out how can I do the same for the external data source, since this one doesn't support the constructs designed for loops (count / length(...)). What I've achieved so far follows. A new .tf file was defined, so that it contains all the names of the machines in a list:

variable machineNamesList {
        default = [ "terraform-firstMachine", "terraform-secondMachine"]
}

Under the resource section, the list is cycled through:

resource "vsphere_virtual_machine" "vm" {
  count = "${length(var.machineNamesList)}"
  # We'll name the VM the same as the guest running inside
  name             = "${var.machineNamesList[count.index]}"

  resource_pool_id = "${data.vsphere_resource_pool.pool.id}"
  datastore_id     = "${data.vsphere_datastore.datastore.id}"

But how can I invoke the external tool n times, each time a new VM is processed ?

Any pointer in the right direction would be appreciated.

Upvotes: 1

Views: 3481

Answers (1)

Martin Atkins
Martin Atkins

Reputation: 74124

The count argument is supported for all resource and data blocks, so you can achieve this by setting count on the various objects to the same expression:

variable "machine_count" {
}

data "external" "machine_name" {
  count = "${var.machine_count}"

  # (presumably in practice you'll use count.index in here somewhere)
  program = ["cmd.exe", "/c", "type c:\\Users\\malbert\\Desktop\\Excel2VM\\MachineName.txt"]
}

resource "vsphere_virtual_machine" "vm" {
  count = "${var.machine_count}"

  name = "${data.external.machine_name.*.result.name[count.index]}"

  # ...etc...
}

There's more information on this mechanism in the using variables with count section of the Terraform documentation. Although the examples there are talking about resource blocks, the count mechanism works the same for both resource and data blocks.

Upvotes: 1

Related Questions