TerraformPilot

DevOps

Terraform for VMware vSphere / ESXi: Complete Guide

Provision VMs on VMware ESXi and vCenter with Terraform: vsphere provider, templates, cloud-init, networking, and resource pools.

LLuca Berton1 min read

VMware vSphere / ESXi remains the dominant on-prem hypervisor in 2026. The official hashicorp/vsphere Terraform provider lets you create datacenters, clusters, resource pools, port groups, and virtual machines from templates with cloud-init customization — the same workflow you use for cloud VMs.

Quick Pattern (TL;DR)

#
terraform {
  required_providers {
    vsphere = {
      source  = "hashicorp/vsphere"
      version = "~> 2.10"
    }
  }
}
 
provider "vsphere" {
  user                 = var.vsphere_user
  password             = var.vsphere_password
  vsphere_server       = var.vcenter_server
  allow_unverified_ssl = false
}

Discover Existing Datacenter Objects

#
data "vsphere_datacenter" "dc" {
  name = "DC01"
}
 
data "vsphere_compute_cluster" "cluster" {
  name          = "Cluster01"
  datacenter_id = data.vsphere_datacenter.dc.id
}
 
data "vsphere_datastore" "ds" {
  name          = "vsanDatastore"
  datacenter_id = data.vsphere_datacenter.dc.id
}
 
data "vsphere_network" "vmnet" {
  name          = "VM Network"
  datacenter_id = data.vsphere_datacenter.dc.id
}
 
data "vsphere_virtual_machine" "ubuntu_template" {
  name          = "templates/ubuntu-24.04"
  datacenter_id = data.vsphere_datacenter.dc.id
}

Clone a VM from Template with cloud-init

#
resource "vsphere_virtual_machine" "web" {
  name             = "web-01"
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.ds.id
 
  num_cpus = 4
  memory   = 8192
  guest_id = data.vsphere_virtual_machine.ubuntu_template.guest_id
 
  network_interface {
    network_id   = data.vsphere_network.vmnet.id
    adapter_type = data.vsphere_virtual_machine.ubuntu_template.network_interface_types[0]
  }
 
  disk {
    label            = "disk0"
    size             = 80
    eagerly_scrub    = false
    thin_provisioned = true
  }
 
  clone {
    template_uuid = data.vsphere_virtual_machine.ubuntu_template.id
 
    customize {
      linux_options {
        host_name = "web-01"
        domain    = "lab.example.com"
      }
 
      network_interface {
        ipv4_address = "10.0.10.21"
        ipv4_netmask = 24
      }
 
      ipv4_gateway = "10.0.10.1"
      dns_server_list = ["10.0.10.1"]
    }
  }
 
  extra_config = {
    "guestinfo.userdata"          = base64encode(file("${path.module}/cloud-init.yaml"))
    "guestinfo.userdata.encoding" = "base64"
  }
}

Distributed Port Group for Production Networks

#
resource "vsphere_distributed_port_group" "prod" {
  name                            = "pg-prod-100"
  distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.dvs.id
  vlan_id                         = 100
  active_uplinks                  = ["uplink1", "uplink2"]
}

Resource Pools and Folders

#
resource "vsphere_resource_pool" "team" {
  for_each            = toset(var.teams)
  name                = each.key
  parent_resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
}
 
resource "vsphere_folder" "team" {
  for_each      = toset(var.teams)
  path          = "teams/${each.key}"
  type          = "vm"
  datacenter_id = data.vsphere_datacenter.dc.id
}

Best Practices

#
  • Bake templates with Packer, then clone with Terraform — don't try to install OSes from Terraform.
  • Use cloud-init via guestinfo for Linux; use customize { windows_options {} } for Windows Sysprep.
  • Pin provider version — vSphere provider has had breaking changes around v2.0/v2.5.
  • Tag VMs via vsphere_tag so you can drive backup, snapshot, and patching policies.
  • Hide vCenter creds in Vault or HCP Vault Secrets — never commit them.
#
#Terraform#VMware#vSphere#ESXi#vCenter

Share this article