Blog

Devops

Creating Virtual Machines in vSphere with Terraform

For a recent client project, we needed to create multiple VMware vSphere virtual machines with Terraform. To accomplish this we knew we could have just used Ansible. However, we found the benefits of Terraform would make it the best choice. In this article, we’ll show you a simplified version of what we used. As a result, this repo highlights the concepts we used and allows you to build upon it yourself.

Let’s first break down the repo.

  • /group_vars/all/vm_config.yml – This is where you list the virtual machines you want to create with Terraform
  • /roles/terraform – the terraform roles
    • apply – runs terraform apply command
    • config – Converts our vm_config.yml to variables.tf in Terraform’s HCL syntax.
    • Destroy – runs the terraform destroy command, this will delete all the VM’s you’ve created.
    • init – Will initialize the Terraform module
  • /terraform/ – our Terraform module directory
    • data-retrieve.tf – This takes our variables like a template, host, datastore, and network and looks up there UUID in vSphere.
    • main.tf – The main terraform file which creates our resources
    • variables.tf – won’t exist until the playbook is run but holds our VM information for Terraform.
  • 01_apply.yml – Creates our resources
  • 02_destroy.yml – Destroys our resources
  • inventory.yml – lists our VM’s and the vCenter connection info. ( we store the password in plain text, this is not recommended)

Setup Ansible

Therefore the first thing we need to configure is /group_vars/all/vm_config.yml. Here is what one virtual machine configuration would look like.

    - {FQDN}:
    hostname: {hostname}
    domain: "{domain_name}"
    template: "{template_name}"
    hardware:
      vcpu: 2
      ram: 2048
      disk_size: 40
      datastore: "{datastore_name}"
    network:
      ip_address: "192.168.0.2"
      ip_netmask: "24"
      ip_gateway: "192.168.0.1"
      vdsport: "{vds_name}"

The settings listed are relatively simple ones, and ones you configure on most virtual machines you deploy. As a result, some things to note, the hostname can not contain a period or the post configuration will break. With the current setup, all fields are required. We are also assuming you have a cluster setup and a Distributed Virtual Switch and Distributed Port Groups configured. Another requirement is that the template will need to be Linux, have VM-tools, and Perl installed. In other words, this allows the post configuration to be completed.

The next part of the setup is configuring your vCenter information. For this open the inventory.yml file. Below are the settings we need to update

---
  all:
    children:
        vms:
          hosts:
            {VM1_FQDN}:
            {VM2_FQDN}:
          vars:
            vcenter_address: {vCenter_Address}
            vcenter_user: "{[email protected]}"
            vcenter_password: "{vCenter_password}"
            vcenter_allow_unverified_ssl: {True_false}
            vcenter_datacenter: "{Datacener_Name}"
            vcenter_cluster: "{vCenter_Cluster}"

Currently, this file includes the FQDN of the VM’s we’re making. This is not a requirement but it sets you up for post configuration using ansible. After that, you’ll need to configure the vars section. All these settings are required. After that, we’ll finally create VMware vSphere virtual machines with Terraform.

Virtual Machine Creation with Terraform

Terraform is an open-source infrastructure as code tool created by Hashicorp. As you know I and the rest of NetSyncrio is a huge believer in open source, and we use it in all our projects. The primary thing that is happening in our project today is the creation of the VM’s. This is done with Terraform resource modules. To accomplish this we also need to add data sources. Let’s break down a few examples.

In the data-retrieve.tf file we have this block

data "vsphere_datacenter" "dc" {
  name = var.vcenter.datacenter
}

What is happening here is we are looking up our datacenter. We defined this datacenter in our inventory.yml file earlier. Terraform uses the name we provided to look up the UUID of the datacenter. This is then used later on in Terraform resource creation to identify what datacenter the resource is created in.

For terraform to perform these lookups it needs to connect to vCenter. In Terraform’s terms, this is a “provider”. In my case, we provide the address, username, and password to the provider module and it will establish a connection.

provider "vsphere" {
  user           = var.vcenter.user
  password       = var.vcenter.password
  vsphere_server = var.vcenter.address

  # If you have a self-signed cert
  allow_unverified_ssl = var.allow_unverified_ssl
}

The variables I keep referencing are defined in our Jinja generated variables.tf file. As a result, an example variable is our datacenter.

variable "dc" {
  default = "My_Datacenter"
}

The final component of our Terraform module is the resource creation, where the “action” happens. Many things are happening here but we’ll focus on just the actual VM creation and not the customization.

resource "vsphere_virtual_machine" "vms" {
  for_each                   = var.vms
  name                       = each.value.hostname
  num_cpus                   = each.value.vcpu
  memory                     = each.value.ram
  datastore_id               = data.vsphere_datastore.datastore[each.key].id
  resource_pool_id           = data.vsphere_resource_pool.pool.id
  guest_id                   = data.vsphere_virtual_machine.template[each.key].guest_id
  scsi_type                  = data.vsphere_virtual_machine.template[each.key].scsi_type
  wait_for_guest_net_timeout = 0
  wait_for_guest_ip_timeout  = 0


  # Configure network interface
  network_interface {
    network_id = data.vsphere_network.network[each.key].id
  }

  # Configure Disk
  disk {
    name = "${each.value.hostname}.vmdk"
    size = each.value.disk_size
  }

  # Define template and customisation params
  clone {
    template_uuid = data.vsphere_virtual_machine.template[each.key].id
  }
}

Going through this line by line is beyond the scope of this article. If you have some programming experience you can probably read through this and make sense of most of it. We are looping through all our VM configuration to create multiple VM’s in this resource block.

Conclusion

Terraform is a powerful tool. It allows you to provision and manages resources in a programmatic manner. This allows you the scalability you couldn’t manage with the traditional creation of resources. It also adds a high level of accuracy and consistency. We’ve used Terraform for cloud provisioning, network provisioning, and virtual machine provisioning to name a few. It’s a tool we’ll keep in our pocket for the foreseeable future. If you’re curious about other tools we use check out our how it works page. If your company needs assistance provisioning resources at scale or converting legacy infrastructure to code contact us for a free consultation.

Leave your thought here