Terraform and vSphere - mk2

This article focuses on improving on our mk1 vm deployment project.

Deploying vSphere VMs with Terraform - mk2

In our previous article we went through a basic deployment of a vsphere vm, cloned from an ubuntu 20 server template.

Today I will be looking at how we can improve it and make it more production ready. For the full code example, please see the github link here: vsphere-vm-mk2


File organization

Putting all of your code in a main.tf is acceptable when getting started or writing our vSphere mk1 example. However, In most situations, you are better off following a file structure like this:

1
2
3
4
5
6
.
├── main.tf
├── variables.tf
├── outputs.tf
├── versions.tf
└── terraform.tfvars

main.tf - calls your modules, locals, and data sources to create all resources.

variables.tf - contains the declared variables used in your main.tf file.

outputs.tf - Contains all of the outputs from the resources created by your main.tf file.

versions.tf - Contains all of the version requirements for Terraform and the providers used.

terraform.tfvars - Contains all inputs that we put in variables declared in the variables.tf file.


Adding Variables

Variables are the next step in making our mk2 revision of our vSphere deployment.

The sections below contain the data we will use in the main.tf file. The significant difference with this code block compared to our mk1 is removing hard-coded data values; for example, name and num_cpus have been defined as variables; var.vm_name and var.num_cpus.

I’ve left disk label as a hard-coded value since I plan on having this setting the same on all of my VMs. However, you can choose to convert it to a variable as well.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
resource "vsphere_virtual_machine" "vm" {
  name             = var.vm_name
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id

  num_cpus = var.vm_num_cpus
  memory   = var.vm_memory
  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 {

      network_interface {
        ipv4_address = var.vm_ipv4_address
        ipv4_netmask = var.vm_ipv4_netmask
      }

      ipv4_gateway    = var.vm_ipv4_gateway
      dns_server_list = var.vm_dns_server_list
      dns_suffix_list = var.vm_dns_suffix_list

      linux_options {
        host_name = var.vm_name
        domain    = var.vm_domain
      }
    }
  }
  lifecycle {
    ignore_changes = [
      annotation,
      clone[0].template_uuid,
      clone[0].customize[0].dns_server_list,
      clone[0].customize[0].network_interface[0]
    ]
  }
}

Now that we have our variables placed, we need to declare them in a variables.tf file.


Declaring Variables

We will need to declare the variables that we are using in our main.tf to be populated. There are three main types of variables I will be using in our variables.tf file.


Type Contraints

These variable types are known in terraform as type keywords

string - String values are always treated as text, even if they contain only numbers.

number - number variables are variables that must take a number value 0, 1, 2,3,…)

bool - Boolean variables contain one of two possible values, true or false


Type Constructors

Type constructors work like functions, that take 0 or more values and give back a new value, such as a collection of data.

list

example of declaring variables:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
variable "vsphere_user" {
  type        = string
  description = "This is the username for vSphere API operations. "
  default     = "admin"
}

variable "vsphere_password" {
  type        = string
  description = "This is the password for vSphere API operations."
  sensitive   = true
  default     = "password"
}

variable "vsphere_server" {
  type        = string
  description = "This is the vCenter server name for vSphere API operations."
  default     = "vc-01"
}

variable "vsphere_allow_unverified_ssl" {
  type        = bool
  description = "Boolean that can be set to true to disable SSL certificate verification."
  default     = true
}


Outputs

You may already be familiar with outputs since we used them in our mk1 file. The difference here is that we take them out of the main.tf file and place them in a separate outputs.tf file. Doing so will allow us to consolidate our outputs code into one place.

Below is an example of the outputs.tf file I will be using:

There are many different outputs that can be given from a deployment. For simplicity, I am only specifying IP address and hostname.

1
2
3
4
5
6
7
output "vm_ip_address" {
  value = vsphere_virtual_machine.vm.default_ip_address
}

output "vm_hostname" {
  value = vsphere_virtual_machine.vm.name
}

TFVARS

WARNING

Make sure NOT to upload a populated tfvars file into any publicly available repository, or any place you don’t want secret information listed. The tfvars file I am hosting in this project has been sanitized for the purposes of the article.

.tfvars files are known as “variable definitions files,” which gives us a convenient method for inputting data into our variables. tfvars files can be specified directly during a deployment or automatically loaded depending on how they are named. for more insight into tfvars, please see the documentation listed here:

For the full example of the tfvars file I will be using: tfvars file

An excerpt of the tfvars file I will be using is listed below:

1
2
3
4
5
6
7
8
9
vsphere_datacenter           = "Datacenter"
vsphere_datastore            = "tpa-lab-01-ds-01"
vsphere_compute_cluster      = "lab"
vsphere_network              = "pg-v110"
vsphere_template             = "templates/packer/ubuntu/linux-ubuntu-20"
vm_name                      = "test-vm-01"
vm_domain_name               = "foggyclouds.net"
vm_num_cpus                  = 2
vm_memory                    = 1024

Closing thoughts

From here, you can see we are starting to get some more organization around our code. Moving forward, we will look at modules and how you can take your newly constructed mk2 vSphere project and transform it into a consumable module.


Acknowledgements