I’ve used Terraform extensively with AWS and Azure, but hadn’t yet spent much time exploring what it is capable of with vSphere. With that in mind, I set out to use Terraform to deploy and configure a Linux virtual machine on vSphere, from an existing template. This article will go through the process. Note that this isn’t designed to be a ‘how to’ around Terraform itself – there’s a lot of great information and tutorials on the Terraform website.
Terraform has a provider which gives it the ability to work with vSphere. With this you can create and manage many vSphere objects, such as virtual machines, resource pools, clusters, datastores and networks. To start with, my aim was to have Terraform provision a virtual machine from a template, and then perform some OS configuration tasks against the newly deployed VM – such as installing an application onto the VM from an rpm.
For this to work, there are a number of pre-requisites:
- Terraform, downloaded to somewhere convenient to run the plans from
- vSphere infrastructure to deploy to
- A suitable virtual machine template (In this case I will be using a Centos 7 template)
I will be creating two Terraform files. These will be
- main.tf contains the plan itself – in this case, the virtual machine configuration and post build configuration
- variables.tf contains the variables which can be passed into the main.tf file for processing
The main.tf file is the one that does most of the work, the contents of which is shown below. I’ve added comments throughout to break down what each block of code is doing:
# The Provider block sets up the vSphere provider - How to connect to vCenter. Note the use of # variables to avoid hardcoding credentials here provider "vsphere" { user = "${var.vsphere_user}" password = "${var.vsphere_password}" vsphere_server = "${var.vsphere_server}" allow_unverified_ssl = true } # The Data sections are about determining where the virtual machine will be placed. # Here we are naming the vSphere DC, the cluster, datastore, virtual network and the template # name. These are called upon later when provisioning the VM resource data "vsphere_datacenter" "dc" { name = "DC" } data "vsphere_datastore" "datastore" { name = "DATASTORE01" datacenter_id = "${data.vsphere_datacenter.dc.id}" } data "vsphere_compute_cluster" "cluster" { name = "CLUSTER01" datacenter_id = "${data.vsphere_datacenter.dc.id}" } data "vsphere_network" "network" { name = "VM Network" datacenter_id = "${data.vsphere_datacenter.dc.id}" } data "vsphere_virtual_machine" "template" { name = "centos7" datacenter_id = "${data.vsphere_datacenter.dc.id}" } # The Resource section creates the virtual machine, in this case # from a template resource "vsphere_virtual_machine" "vm" { name = "${var.servername}" resource_pool_id = "${data.vsphere_compute_cluster.cluster.resource_pool_id}" datastore_id = "${data.vsphere_datastore.datastore.id}" num_cpus = 2 memory = 8192 guest_id = "${data.vsphere_virtual_machine.template.guest_id}" scsi_type = "${data.vsphere_virtual_machine.template.scsi_type}" network_interface { network_id = "${data.vsphere_network.network.id}" adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}" } disk { label = "disk0" size = "${data.vsphere_virtual_machine.template.disks.0.size}" eagerly_scrub = "${data.vsphere_virtual_machine.template.disks.0.eagerly_scrub}" thin_provisioned = "${data.vsphere_virtual_machine.template.disks.0.thin_provisioned}" } clone { template_uuid = "${data.vsphere_virtual_machine.template.id}" customize { linux_options { host_name = "${var.servername}" domain = "test.internal" } network_interface {} } } } # This resource exists to call the file and remote-exec Terraform provisioners. This runs after # the virtual machine resource has been created. It uses the file provisioner to copy a .rpm file stored # locally to the newly created server, then uses the remote exec provisioner to run the commands necessary # to install the rpm resource "null_resource" "vm" { triggers { public_ip = "${vsphere_virtual_machine.vm.default_ip_address}" } connection { type = "ssh" host = "${vsphere_virtual_machine.vm.default_ip_address}" user = "root" password = "${var.password}" port = "22" agent = false } provisioner "file" { source = "c:/files/${var.rpm}" destination = "/tmp/${var.rpm}" } provisioner "remote-exec" { inline = [ "chmod +x /tmp/${var.rpm}", "rpm -i /tmp/${var.rpm}", ] } } # Finally, we're outputting the IP address of the new VM output "my_ip_address" { value = "${vsphere_virtual_machine.vm.default_ip_address}" }
Along with the above, we have a variables.tf file which defines the variables used in the code above:
variable "vsphere_server" { description = "vsphere server for the environment - EXAMPLE: vcenter01.hosted.local" default = "vcenter.test.local" } variable "vsphere_user" { description = "vsphere server for the environment - EXAMPLE: vsphereuser" default = "administrator@vsphere.local" } variable "vsphere_password" { description = "vsphere server password for the environment" } variable "rpm" { description = "rpm for software install" default = "installer.rpm" } variable "password" { description = "Root account password" } variable "servername" { description = "Server Name" }
The variables.tf file above defines the variables used in the main.tf file. With both of these we have the complete Terraform plan for deploying the VM then performing some post deployment configuration tasks via SSH. Terraform first uses the vSphere provider to deploy the Centos virtual machine from a template – it then ‘learns’ the DHCP configured IP address of the new VM, and uses it to connect to the virtual machine via SSH (using the remote-exec provider) in order to run some shell commands and install an application from an rpm.
Terraform and vSphere – a great combination!