Terraform and vSphere - mk1

This article details the basics of getting terraform to deploy a VM clone from a vsphere template.



Introduction

Terraform is pretty awesome. If you are trying to find your way in the world of DevOps or Infrastructure Automation, you will probably hear about Terraform, or Infrastructure as Code.

I decided to write this series as a way for someone who might be more familiar with vSphere and wants to use Terraform with vSphere.

We will be using the vSphere provider and several others during the next few posts. We will start with a basic VM deployment and add layers of composability and terraform magic.

Terraform offers a nifty way to connect to your infrastructure, through the use of a term called “Providers”. Terraform relies on plugins called “providers” to interact with cloud providers, SaaS providers, and other APIs.

The VMware vSphere provider gives Terraform the capability to integrate with VMware vSphere. This provider can manage multiple elements of a VMware vSphere deployment, including VMs, networks, datastores, and more.


Prerequisites

To follow along, I will be assuming a few things:

You have a general understanding of vSphere, VMs, or have deployed VMs before.

You have a lab or non-production virtual environment to work with and test deployments.

You have a basic understanding of what Terraform is or have gone through some getting started tutorials.

If not, check out Hashicorp’s Terraform tutorials here: Intro to IaC with Terraform


Getting Started with Mk1

Let’s get into it! You can find the public repo containing the code for this tutorial here: Link

I’ve also linked the vSphere provider documentation here: Link

We will go through each section, detailing the components and how to use them.


Terraform Configuration Section

The first section of our main.tf file will have the terraform global configuration section. This block identifies our required providers and any versions we want to specify. It’s a good practice to call out the version that runs your code successfully. This way, if any future versions change functions, you can trace back to the working plugin version.

1
2
3
4
5
6
7
8
terraform {
  required_providers {
    vsphere = {
      source  = "hashicorp/vsphere"
      version = "2.0.2"
    }
  }
}

Provider

The following section is the provider block. This block is for provider-specific configurations, like accessing VCenter and the account and password information.

Placing secrets like passwords in your provider block is NOT SECURE!

It’s good practice to use a secrets engine like Vault or environment variables to handle passwords. Using one of these methods helps keep passwords out of configuration and state files. I’ll try and cover that soon in another series.

1
2
3
4
5
6
7
8
provider "vsphere" {
  user           = "tf_admin@vsphere.local"
  password       = "VMware1!"
  vsphere_server = "tpa-vmw-vc-01.foggyclouds.net"

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

Data

The following section is the data block. This block is for accessing data sources defined in the provider. Using this block allows us to grab component details necessary to feed into the resource block.

The data commands are capturing details from the named objects in datacenter, datastore, cluster, network, and template.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
data "vsphere_datacenter" "dc" {
  name = "Datacenter"
}

data "vsphere_datastore" "datastore" {
  name          = "tpa-lab-01-ds-01"
  datacenter_id = data.vsphere_datacenter.dc.id
}

data "vsphere_compute_cluster" "cluster" {
  name          = "lab"
  datacenter_id = data.vsphere_datacenter.dc.id
}

data "vsphere_network" "network" {
  name          = "pg-v110"
  datacenter_id = data.vsphere_datacenter.dc.id
}

data "vsphere_virtual_machine" "template" {
  name          = "templates/packer/ubuntu/linux-ubuntu-20"
  datacenter_id = data.vsphere_datacenter.dc.id
}

Resource

In the resource block, we are defining how terraform will create the VM resource.

I’ve separated the block in 2 sections for readability. The first section details VM hardware specifics, such as compute cluster, networking interface, and storage devices.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
resource "vsphere_virtual_machine" "vm" {
  name             = "test-vm-01"
  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
    eagerly_scrub    = data.vsphere_virtual_machine.template.disks.0.eagerly_scrub
    thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned
  }

The second section below details VM clone specifics, such as the template to clone from and guest customization settings. The lifecycle section prevents VMs created from the template from being modified if the template is changed or removed in the future.

 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
  clone {
    template_uuid = data.vsphere_virtual_machine.template.id

    customize {

      network_interface {
        ipv4_address = "10.1.110.185"
        ipv4_netmask = 24
      }

      ipv4_gateway    = "10.1.110.254"
      dns_server_list = ["10.1.110.11"]
      dns_suffix_list = ["foggyclouds.net"]

      linux_options {
        host_name = "test-vm-01"
        domain    = "foggyclouds.net"
      }
    }
  }
  lifecycle {
    ignore_changes = [
      annotation,
      clone[0].template_uuid,
      clone[0].customize[0].dns_server_list,
      clone[0].customize[0].network_interface[0]
    ]
  }
}

Output

The output block details the IP and hostname of the VM created from the terraform run.

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
}

Acknowledgements


Back to Top