I posted recently about using Terraform, vCenter, and Chef, and promised a follow-up post about extending the Terraform plans to work with Windows and multiple Virtual Machines. Here is the follow-up, it will be significantly shorter than the previous post but it will still build off of this plan:

https://gist.github.com/jjasghar/f061f493ad8f631a6d4b5b5085c7cb35

Windows

Go ahead and open up the following link to show off the .diff that has Windows working with the above Terraform plan.

https://gist.github.com/jjasghar/dbd7348240f23a26a31cd7a02dcb4267

If you don’t know how to read diffs, the green or + lines are lines I’ve added, while the red or - are lines I have removed.

With a successful Linux VM build, the next natural progression is to get Windows working with the same base plan. There is some prep work that is required down this path and I chose the one that made the most sense for me. You’ll need to figure out what is the correct path for you here, and it will probably require talking to your VMware Administrators and Windows Licensing people to make sure you’re in compliance with your environments rules.

I don’t use Customization Specs very heavily so I’m going to move past it. If you do use them, you’ll need to first remove this line, that skips customization specs and just request the template in its base form. If you don’t have this line, and no customization spec, you’ll find that Terraform syspreps the machine, and can wipe out WinRM settings. This was a gotcha that caused the longest delay in figuring out how to get Terraform to request a Windows VM for me. These settings are required for Chef to be bootstrapped into your Windows VM, you need to enable and set some WinRM settings. Taken from the winrm-cli README, you need to set the following at a Powershell prompt:

winrm quickconfig # say y here ;)
winrm set winrm/config/service/Auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}'

Take either a snapshot of the Virtual Machine, or create a template out of it, and then change this line to that name. The next thing to take notice of is the diff of the variables.tf. Scroll down to the next file, and look at Lines 16 to 25 you’ll see the changes that are required to talk to the template I created. Notice the WinRM change, for the connection_type the user of Administrator, and my super secure Admini@ local administrator password. If you scroll back up to Line 55 in the diff you’ll notice one more setting I set for the connection. For the settings for WinRM above, we connect over http and by default Terraform connects over https. This option forces the correct protocol.

Just to make sure this is clear, the final major change is the middle stanza of lines 22 to 38. Not having specific Ubuntu available commands and bash on my Windows VM, these commands would ultimately fail on run. I am pretty confident that you can run Powershell commands instead of bash here, though to be honest, I haven’t tried it. Reading the remote-exec docs it seems that The remote-exec provisioner supports both ssh and winrm type connections which at least imply it can run whatever commands you type there.

Multiple machines

Now that we’ve walked through creating a virtual machine of both Linux and Windows based operating systems, we need to figure out how to make multiple machines spin up at once. This was surprisingly easy as soon as the base understanding of the .tf plans are put together. Let’s take a look at this diff to show how to extend the base plan to 3 virtual machines.

Looking first at the variables.tf diff, you’ll notice we added three lines for a default number of 3 and called it count. This should be pretty straightforward, and if you needed to override it you know how to from the previous post. Because we don’t know how many nodes we want we had to remove line 18 and 19 so we don’t hardcode the default name now.

Scroll back up to the main diff file, you’ll see that if you give the vsphere_virtual_machine resource a count it will create that many machines, as we do on line 8. There are a lot of examples on how to name the machines, I chose from:

variable "count" {
  default = 2
}

resource "aws_instance" "web" {
  # ...

  count = "${var.count}"

  # Tag the instance with a counter starting at 1, ie. web-001
  tags {
    Name = "${format("web-%03d", count.index + 1)}"
  }
}

And edited it to my liking on line 12. This way now my machines will be called terraform-0x where x is the number of the machine count we created. Finally, line 20 and 21 create the node objects on the chef server with the same name of the machine inside vCenter, which helps keep things in line.

Clean up

So if you’ve played with these Terraform plans, the easiest way is just to run terraform destroy to nuke the machines from vCenter. It won’t delete the node objects from the Chef server, that’s by design, and the reason why we have the recreate_client line set to true.

I hope this helps bootstrap your use cases of Terraform, vCenter, and Chef, makes the path to your success that much easier. I know as I stated in the beginning, starting off from nothing with these three technologies is hard, but as soon as you wrap your head around some simple examples you’ll find yourself programmatically requesting resources from repeatably and efficiently in no time.