Using Terraform to provision the VM
Once I had a VM template in Vsphere, I could use Terraform to create a VM. Managing VMs this way has many advantages:
- Allows an arbitrary number of identical VMs to be quickly provisioned.
- VMs can be destroyed just as quickly. This is especially useful when practicing something new or developing anything related to Infrastructure as Code.
- Resources can be modified as code. For example, adding memory or another vCPU to a VM is as simple as modifying code and applying the change.
- VMs can be re-created the same way eliminating the ‘snowflake’ effect of doing it manually.
- The template itself can be patched and updated as needed.
- Changes to the VMs or any other infrastructure can be predicted and planned using ‘dry-run’ techniques. For example, Terraform has a ‘plan’ mode that tells you what will be done before any action it taken.
- Since the definition of infrastructure is now code, it can be linted, tested and version-controlled.
To get started, I needed to declare the use of the Vsphere provider. Here’s the contents of the main.tf file:
provider "vsphere" {
user = var.vsphere_user
password = var.vsphere_password
vsphere_server = var.vsphere_server
# If you have a self-signed cert
allow_unverified_ssl = true
}
Next, I created a separate file for variables. These variable are common to my environment, and not specific to any VM. I called this file variables.tf.
variable "vsphere_user"{
type = string
default = "***************"
}
variable "vsphere_password"{
type = string
default = "***************"
}
variable "vsphere_server"{
type = string
default = "***************"
}
variable "vsphere_datacenter"{
type = string
default = "***************"
}
variable "vsphere_datastore"{
type = string
default = "***************"
}
variable "vsphere_vm_network"{
type = string
default = "***************"
}
variable "vsphere_cluster" {
type = string
default = "***************"
}
variable "network_domain"{
type = string
default = "***************"
}
variable "dns_servers"{
type = list(string)
default = "***************"
}
variable "default_gateway" {
type = string
default = "***************"
}
variable "netmask" {
type = number
default = 24
}
Note that in the example above, it’s not advisable to store credentials in source code. These should be stored in the environment or in a secret store such as Hashicorp Vault. The above is only used for this oversimplified example.
The last piece of the Terraform puzzle was to write a block of code to create and manage the actual VM:
data "vsphere_datacenter" "dc" {
name = "${var.vsphere_datacenter}"
}
data "vsphere_datastore" "datastore" {
name = "${var.vsphere_datastore}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_compute_cluster" "cluster" {
name = "${var.vsphere_cluster}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_network" "network" {
name = "${var.vsphere_vm_network}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_virtual_machine" "template" {
name = "tpl-ubuntu-18.04"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_virtual_machine" "vm" {
name = "wordpress.test.local"
resource_pool_id = "${data.vsphere_compute_cluster.cluster.resource_pool_id}"
datastore_id = "${data.vsphere_datastore.datastore.id}"
num_cpus = 2
memory = 4096
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 = "wordpress"
domain = "${var.network_domain}"
}
network_interface {
ipv4_address = "10.0.1.38"
ipv4_netmask = "${var.netmask}"
}
ipv4_gateway = "${var.default_gateway}"
dns_server_list = "${var.dns_servers}"
}
}
}
Note that many of the inputs to this file are references to the contents of the variables.tf file discussed above. Note also that the template name corresponds to the VM template for the Ubuntu VM template I created in the previous step.
To create a VM from all this, it is minimally necessary to run just two Terraform commands:
terraform plan
terraform apply
I wound up with a VM that was cloned from my template:

Leave a Reply