Hashicorp’s Terraform is an Infrastructure as Code (IaC) declarative language that allows you to define the existence and state of objects within your infrastructure and then publish this new state to your infrastructure. Terraform is agnostic of platform, you can use a wide range of “providers” to manage a number of different infrastructure components from a single Terraform script.
In this simple example, i’ll show how to create a template and then deploy two virtual machines on VMware using this template using the Terraform tool. The example assumes that you have an Ubuntu 20.04 client machine that you’ll install Terraform onto, then a small VMware environment which has VMware vCenter and at least one ESXi host.
Install Terraform
- Make sure the system is up-to-date and install these packages which are needed for further steps:
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common curl
- Next add the Hashicorp GPG key needed by the repository:
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
- Then, add the official repository for HashiCorp for Linux:
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
- Now that we have added the repo to the list, let’s update to add the relevant repository content:
sudo apt update
- Once update, run this command to install Terraform:
sudo apt install terraform
- Run terraform to ensure its been installed correctly.
terraform
You should see the help guide, showing the available commands, if that is the case Terraform has been successfully installed.
Prepare VMware vCenter Permissions
The next step is to create an account within VMware vCenter that you’ll use to make changes to your VMware environment. If you are testing this on a production environment is recommended to restrict the permissions this account has to change your VMware environment.
Create “variables.tf” Terraform File
The first file we need to create is the “variables.tf” file, this defines some variables, in this case a file that defines the username, password and vCenter Server address.
variable "username" {
default = "terraform-admin@domain.local"
}
variable "password" {
default = "mypassword"
}
variable "vcenter" {
default = "vcenter.domain.com"
}
These variables are important because we’ll refer to them from the main.tf file shortly.
Create Ubuntu VMware Template
I’m assuming you know how to create a VMware VM template, the below gives a great simple guide of how to do this
https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/manage/hybrid/server/best-practices/vmware-ubuntu-template
A very brief procedure is to install Ubuntu (20.04) onto a VM, so you have a bootable Ubuntu machine, with whatever extra packages you require. You then run the following commands to prepare the machine for being turned into a template.
sudo apt-get update
sudo apt-get upgrade -y
sudo sed -i 's/preserve_hostname: false/preserve_hostname: true/g' /etc/cloud/cloud.cfg
sudo truncate -s0 /etc/hostname
sudo hostnamectl set-hostname localhost
sudo rm /etc/netplan/00-installer-config.yaml
cat /dev/null > ~/.bash_history && history -c
sudo shutdown now
Within VMware vCenter now find the VM you’ve just created, right click and select “Convert to Template”, once converted we’re ready to build our configuration and attempt to deploy some VMs (using the template) by declaring them within Terraform.
Create “main.tf” Terraform File
Create the “main.tf” file in the same directory as your variables.tf you created earlier. You notice that this file refers to the variables you declared in the variables.tf file earlier.
You should change the Datacenter name, datastore names etc. to what
provider "vsphere" {
user = var.username
password = var.password
vsphere_server = var.vcenter
allow_unverified_ssl = true
}
# A "data" resource is a readonly resource, something Terraform knows about but won't be changing.
data "vsphere_datacenter" "datacenter" {
name = "MyDatacenter"
}
data "vsphere_datastore" "datastore" {
name = "datastore1"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
data "vsphere_compute_cluster" "cluster" {
name = "My Cluster"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
data "vsphere_network" "network" {
name = "my-network-pg"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
data "vsphere_virtual_machine" "template" {
name = "ubuntu2204"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
resource "vsphere_virtual_machine" "bill" {
name = "bill"
folder = "Production"
resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
datastore_id = data.vsphere_datastore.datastore.id
num_cpus = 2
memory = 2048
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
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 = "bill"
domain = "domain.com"
}
network_interface {
ipv4_address = "172.16.0.10"
ipv4_netmask = 24
}
ipv4_gateway = "172.16.0.1"
dns_server_list = ["172.16.200.10","172.16.200.11"]
dns_suffix_list = ["internal.domain.com","domain.com"]
}
}
}
resource "vsphere_virtual_machine" "ben" {
name = "ben"
folder = "Production"
resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
datastore_id = data.vsphere_datastore.datastore.id
num_cpus = 2
memory = 2048
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
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 = "ben"
domain = "internal.sanger.ac.uk"
}
network_interface {
ipv4_address = "172.16.0.11"
ipv4_netmask = 24
}
ipv4_gateway = "172.16.0.1"
dns_server_list = ["172.16.200.10","172.16.200.11"]
dns_suffix_list = ["internal.domain.com","domain.com"]
}
}
}
Before running Terraform operations, download plugins using the terraform init command.
terraform init
This will then download the “provider” packages and intialise the Terraform environment ready for use with in this case vSphere (VMware). You can use various other providers the same way so its there ready for use.
Running Terraform and Observing the Results
Now we are ready to run Terraform, we’re going to run a plan first just to see what Terraform thinks it needs to do.
terraform plan
Check the output, you should see it says it will create two new VMs. If you are happy run the following to apply the configuration.
terraform apply
After a few seconds you should see the VMs have been deployed, then powered on, they’ll boot up, which means they run the “cloud-init” scripts, this will set the hostname, apply the IP network configurations.
After a few minutes you should have two working VMs deployed, in this example: Bill and Ben.
Destroy it All
If you want to remove what you’ve deployed, you can run:
terraform destroy
The thing to remember is that Terraform only manages (changes) what it knows about (in it’s state). Our script only declared two VMs, so this destroy command will only destroy these two VMs.
Conclusion
Hopefully this gives you a very simple introduction to what you can do with Terraform. Its very extensible, you can add various providers and drive different parts of your infrastructure all from one script.
For example, you could have Terraform, create DNS records within your DNS server for the VMs automatically, then deploy the VMs into a ready state, all in one fell swoop.
Additional Information
https://garyflynn.com/post/create-your-first-vsphere-terraform-configuration/
https://blogs.vmware.com/cloud/2019/11/19/infrastructure-code-terraform-vmware-vmware-cloud-aws/
https://github.com/diodonfrost/terraform-vsphere-examples
https://thenewstack.io/install-terraform-and-the-gaia-web-ui-on-ubuntu-server-22-04/