Manage VMs and snapshots on vSphere
Terraform, HashiCorp's infrastructure as code (IaC) offering, provides a consistent workflow for provisioning and maintaining infrastructure and services. Terraform allows organizations to take a programmatic approach to infrastructure management. By logging, auditing, and versioning infrastructure changes, organizations gain better insight into the current state of their infrastructure.
VMware is a key component of many organizations' on-premises and private cloud infrastructure. The vSphere provider enables operators to adopt Terraform to build, change, and manage common VMware resources.
In this tutorial, you will create a vSphere template using Packer and provision a virtual machine (VM) from that template using Terraform. You will then use Terraform to modify the VM's name and resource allocations and create a snapshot of the VM.
Prerequisites
This tutorial assumes you are familiar with the standard Terraform workflow. If you are unfamiliar with Terraform, complete the Get Started tutorials first.
For this tutorial, you will need:
- Terraform CLI installed locally
- Packer 1.6.6+
- An existing vSphere environment
Clone repository
Clone the example repository.
$ git clone https://github.com/hashicorp-education/learn-terraform-vsphere
Navigate to the repository directory in your terminal.
$ cd learn-terraform-vsphere
This directory contains the Packer files you need to create an Ubuntu template image and an initial Terraform configuration to provision a virtual machine on your vSphere cluster.
If you already have a VM in vSphere that you want to use as a template, skip to the Provision a VM from template step.
Create a vSphere template using Packer
Navigate to the packer
directory.
$ cd packer
This directory contains four files.
- The
vsphere-iso_basic_ubuntu.pkr.hcl
file is the base Packer template. It uses thevsphere-iso
builder to create an Ubuntu 22.04.3 server image namedtf-edu-ubuntu
. - The
variables.pkr.hcl
file contains variable declarations for the following variables:vsphere_server
,vsphere_user
,vsphere_password
,datacenter
,cluster
,datastore
, andnetwork_name
. - The
vars.auto.pkrvars.hcl.example
file contains sample variable definitions forvsphere-iso_basic_ubuntu.pkr.hcl
. - The
user-data
file is automated server install configuration that installs and configures Ubuntu on your VM. Notice how the user-data file installsopen-vm-tools
. Packer uses the VMware tool set to extract the IP from the VM.
Copy the contents of vars.auto.pkrvars.hcl.example
into a new file named vars.auto.pkrvars.hcl
.
$ cp vars.auto.pkrvars.hcl.example vars.auto.pkrvars.hcl
Update vars.auto.pkrvars.hcl
with your vSphere cluster connection information.
vsphere_server = "127.0.0.1:8989"
vsphere_user = "user"
vsphere_password = "pass"
datacenter = "DC0"
cluster = "DC0_C0"
datastore = "LocalDS_0"
network_name = "VM Network"
Warning
Do not commit sensitive values into version control. The .gitignore
located in the root directory of the repo includes *.pkrvars.hcl
. It also includes **/packer_cache/*
so you don't commit sensitive values or cached ISOs into version control.
This Packer configuration retrieves the Ubuntu 22.04.3 ISO from the vSphere datastore.
To use this Packer file, download the Ubuntu 22.04.3 ISO here, then upload it to a vSphere datastore.
Next, update the iso_path
in vsphere-iso_basic_ubuntu.pkr.hcl
to point to the datastore containing the Ubuntu ISO, by replacing vsanDatastore
with your datastore name.
source "vsphere-iso" "this" {
## ...
iso_paths = ["[vsanDatastore] ISO/ubuntu-22.04.3-live-server-amd64.iso"]
## ...
}
Next, initialize the Packer configuration.
$ packer init .
Finally, build the Ubuntu template to your vSphere cluster.
$ packer build .
vsphere-iso.this: output will be in this color.
==> vsphere-iso.this: Creating VM...
## …
Build 'vsphere-iso.this' finished after 4 minutes 47 seconds.
==> Wait completed after 4 minutes 47 seconds
==> Builds finished. The artifacts of successful builds are:
--> vsphere-iso.this: tf-edu-ubuntu
Tip
This Packer configuration should work for most vSphere clusters. Refer to the Packer Builder for VMware vSphere documentation to customize this template to your exact vSphere environment.
Verify that Packer successfully created the template and loaded it into to your vSphere cluster.
Explore configuration and define variables
Now that you have created the template, you are ready to provision a VM with Terraform using that template.
Return to the repository's root directory containing the Terraform configuration you will use to provision your VM on vSphere.
$ cd ..
Here you will find main.tf
, variables.tf
, terraform.example.tfvars
, and versions.tf
.
Explore main.tf
Open main.tf
. This file uses the vSphere provider to deploy a virtual machine from your newly created Ubuntu template. This file contains four main sections.
The
vsphere
provider block contains the connection information Terraform uses to authenticate and connect to the vSphere API.provider "vsphere" { user = var.vsphere_user password = var.vsphere_password vsphere_server = var.vsphere_server # If you have a self-signed cert allow_unverified_ssl = true }
Terraform uses the
vsphere_virtual_machine
data source to retrieve information about the Ubuntu template that you created with Packer. The data includes the data center ID, data store ID, cluster ID, network ID, and the Ubuntu template ID. Terraform uses these values to provision the virtual machine.data "vsphere_virtual_machine" "ubuntu" { name = "/${var.datacenter}/vm/${var.ubuntu_name}" datacenter_id = data.vsphere_datacenter.datacenter.id }
The
vsphere_virtual_machine
resource defines the configuration that Terraform uses to provision the virtual machine. Notice how this resource references the previously defined data sources (for example:data.vsphere_compute_cluster.cluster.resource_pool_id
). By using data sources to retrieve these IDs instead of hardcoding them, the configuration is more user-friendly, comprehensible, and robust.resource "vsphere_virtual_machine" "learn" { name = "learn-terraform" resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id datastore_id = data.vsphere_datastore.datastore.id num_cpus = 2 memory = 1024 network_interface { network_id = data.vsphere_network.network.id } wait_for_guest_net_timeout = -1 wait_for_guest_ip_timeout = -1 disk { label = "disk0" thin_provisioned = true size = 32 } guest_id = "ubuntu64Guest" clone { template_uuid = data.vsphere_virtual_machine.ubuntu.id } }
Tip
The Terraform Registry contains provider-specific documentation. The
vsphere_virtual_machine
Registry page includes a full list of arguments, attributes, and example configurations that you can reference when customizing your provider.
- The output block will display the
vsphere_vitrual_machine.learn
's IP address.
output "vm_ip" {
value = vsphere_virtual_machine.learn.guest_ip_addresses
}
Define variables
Open variables.tf
. This file contains the input variables this configuration uses. Because the values of these variables depend on your specific configuration, you will need to customize them to your cluster.
Copy the contents of terraform.tfvars.example
into a new file named terraform.tfvars
.
$ cp terraform.tfvars.example terraform.tfvars
Open terraform.tfvars
and modify the values to match your vSphere cluster.
vsphere_server = "127.0.0.1:8989"
vsphere_user = "user"
vsphere_password = "pass"
datacenter = "DC0"
datastore = "LocalDS_0"
cluster = "DC0_C0"
network_name = "VM Network"
ubuntu_name = "tf-edu-ubuntu"
Warning
This file contains sensitive information used to connect to your cluster. You should never be commit sensitive values into source control. The .gitignore
file found in this repo ignores all .tfvars
files. You should include this file in any of your future Terraform repos.
Provision a VM from template
In your terminal, initialize your Terraform workspace.
$ terraform init
Apply your configuration. Remember to confirm your apply with a yes
.
$ terraform apply
## ...
vsphere_virtual_machine.learn: Creating...
vsphere_virtual_machine.learn: Still creating... [10s elapsed]
vsphere_virtual_machine.learn: Still creating... [20s elapsed]
vsphere_virtual_machine.learn: Creation complete after 24s [id=420dee3e-3e08-c45c-b0b6-33aaf7777583]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
vm_ip = tolist([])
Verify that Terraform provisioned the VM successfully by viewing the vSphere Client.
View the recently provisioned VM's IP address.
$ terraform output vm_ip
tolist([])
Notice that it returns an empty list. This is because the VM did not have an IP address immediately after Terraform provisioned it.
Terraform can pull in the current VM state using terraform refresh
. Refresh your configuration's state.
$ terraform refresh
vsphere_virtual_machine.learn: Refreshing state... [id=420d29d4-c35b-cce8-3a61-d211ae06fbe9]
Outputs:
vm_ip = tolist([
"172.16.0.49",
"fe80::250:56ff:fe8d:3590",
])
Terraform successfully retrieved the VM's current state.
Modify VM
Now that you have provisioned the VM, modify the configuration in main.tf
to double the memory and change the name to to learn-terraform-doubled
.
resource "vsphere_virtual_machine" "learn" {
- name = "learn-terraform"
+ name = "learn-terraform-doubled"
resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
datastore_id = data.vsphere_datastore.datastore.id
num_cpus = 2
- memory = 1024
+ memory = 2048
## ...
}
Apply your configuration to update your VM. Remember to confirm your apply with a yes
.
$ terraform apply
vsphere_virtual_machine.learn: Refreshing state... [id=420d29d4-c35b-cce8-3a61-d211ae06fbe9]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# vsphere_virtual_machine.learn will be updated in-place
~ resource "vsphere_virtual_machine" "learn" {
id = "420d29d4-c35b-cce8-3a61-d211ae06fbe9"
~ memory = 1024 -> 2048
~ name = "learn-terraform" -> "learn-terraform-doubled"
tags = []
# (62 unchanged attributes hidden)
# (3 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
## ...
vsphere_virtual_machine.learn: Modifying... [id=420d29d4-c35b-cce8-3a61-d211ae06fbe9]
vsphere_virtual_machine.learn: Still modifying... [id=420d29d4-c35b-cce8-3a61-d211ae06fbe9, 10s elapsed]
vsphere_virtual_machine.learn: Modifications complete after 16s [id=420d29d4-c35b-cce8-3a61-d211ae06fbe9]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
vm_ip = tolist([
"172.16.0.49",
"fe80::250:56ff:fe8d:3590",
])
Verify that Terraform modified the VM successfully by viewing the vSphere Client.
Create a snapshot
Next, you will create a snapshot of the VM. Add the following resource to your main.tf
.
resource "vsphere_virtual_machine_snapshot" "learn" {
virtual_machine_uuid = vsphere_virtual_machine.learn.id
snapshot_name = "learn-tf-ubuntu-snapshot"
description = "Created using Terraform"
memory = "true"
quiesce = "true"
remove_children = "false"
consolidate = "true"
}
Notice how the vsphere_virtual_machine_snapshot
references the VM you provisioned earlier in virtual_machine_uuid
.
Apply your configuration to create your snapshot. Remember to confirm your apply with a yes
.
$ terraform apply
vsphere_virtual_machine.learn: Refreshing state... [id=420d29d4-c35b-cce8-3a61-d211ae06fbe9]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# vsphere_virtual_machine_snapshot.learn will be created
+ resource "vsphere_virtual_machine_snapshot" "learn" {
+ consolidate = true
+ description = "Created using Terraform"
+ id = (known after apply)
+ memory = true
+ quiesce = true
+ remove_children = false
+ snapshot_name = "learn-tf-ubuntu-snapshot"
+ virtual_machine_uuid = "420d29d4-c35b-cce8-3a61-d211ae06fbe9"
}
Plan: 1 to add, 0 to change, 0 to destroy.
## ...
vsphere_virtual_machine_snapshot.learn: Creating...
vsphere_virtual_machine_snapshot.learn: Still creating... [10s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [20s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [30s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [40s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [50s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [1m0s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [1m10s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [1m20s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [1m30s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [1m40s elapsed]
vsphere_virtual_machine_snapshot.learn: Still creating... [1m50s elapsed]
vsphere_virtual_machine_snapshot.learn: Creation complete after 1m56s [id=snapshot-95]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
vm_ip = tolist([
"172.16.0.49",
"fe80::250:56ff:fe8d:3590",
])
Verify that Terraform created the snapshot successfully by viewing the vSphere Client.
You have successfully created a VM and snapshot in vSphere using Terraform.
Clean up your infrastructure
Destroy the resources you created when you finish this tutorial. Remember to respond to the confirmation prompt with yes
.
$ terraform destroy
## ...
Plan: 0 to add, 0 to change, 2 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
vsphere_virtual_machine_snapshot.learn: Destroying... [id=snapshot-95]
vsphere_virtual_machine_snapshot.learn: Destruction complete after 0s
vsphere_virtual_machine.learn: Destroying... [id=2109e8a3-39ef-5a51-8ccb-4ad1a02d5864]
vsphere_virtual_machine.learn: Destruction complete after 0s
Destroy complete! Resources: 2 destroyed.
Next steps
In this tutorial, you created a template in vSphere using Packer then cloned and modified a virtual machine in vSphere using Terraform. In addition, you created a snapshot.
To learn more about managing vSphere resources with Terraform, including how to create modules and use the vSphere provider, visit the following resources:
- The Terraform vSphere Provider Registry page
- See a sample configuration of a VM deployment from an OVF/OVA template
- The
vsphere-iso
Packer documentation - Reuse Configuration with Modules tutorials
- List of Terraform vSphere modules