Terraform
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+
- govc installed locally
- Docker Desktop installed and running
- vcsim vCenter API simulator
VMware's vcsim is an open-source vCenter and ESXi API simulator. If you do not have a vSphere environment, vcsim can simulate virtual machines in the vSphere API and back them with Docker containers. Because vcsim is an API simulator, there are some key differences when compared to vSphere and some features are not supported. This tutorial reviews those differences.
To run vcsim, first clone the vmware/govmomi repository, which contains the vcsim source code.
$ git clone https://github.com/vmware/govmomi
Navigate to the vcsim
directory in your terminal.
$ cd govmomi/vcsim
Build the vcsim binary.
$ make
Start vcsim.
$ ./vcsim
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 in vcsim.
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
Open vars.auto.pkrvars.hcl
in your text editor. The default values of this file configure Packer to connect to vcsim.
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"
Next, open the vsphere-iso_basic_ubuntu.pkr.hcl
file and uncomment the configuration_parameters
block. This block configures the Docker container that will back your simulated virtual machine and is only required when using vcsim.
## ...
- /* Uncomment when running on vcsim
ssh_host = "127.0.0.1"
ssh_port = 2222
configuration_parameters = {
"RUN.container" : "lscr.io/linuxserver/openssh-server:latest"
"RUN.mountdmi" : "false"
"RUN.port.2222" : "2222"
"RUN.env.USER_NAME" : "ubuntu"
"RUN.env.USER_PASSWORD" : "ubuntu"
"RUN.env.PASSWORD_ACCESS" : "true"
}
- */
## ...
Delete the boot_command
option, as vcsim does not currently support this feature. This option specifies the keys to type when the virtual machine first boots in order to start the OS installer. Since vcsim will use a Docker container to simulate your VM, this option is not required for this tutorial.
## ...
- //boot_command = ["<wait>e<down><down><down><end> autoinstall ds=nocloud;<F10>"]
## ...
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
Verify that Packer successfully created the template by using govc
.
$ GOVC_URL=https://user:pass@127.0.0.1:8989/sdk GOVC_INSECURE=1 govc vm.info tf-edu-ubuntu
Name: tf-edu-ubuntu
Path: /DC0/vm/tf-edu-ubuntu
UUID: a27e4e20-6722-5217-bba6-c304ffc5a839
Guest name: ubuntu64Guest
Memory: 1024MB
CPU: 2 vCPU(s)
Power state: poweredOff
Boot time: 2023-06-23 11:10:44.827554 -0400 EDT
IP address:
Host: DC0_C0_H0
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.
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
. The default values of this file configure Terraform to connect to vcsim.
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 using govc
.
$ GOVC_URL=https://user:pass@127.0.0.1:8989/sdk GOVC_INSECURE=1 govc vm.info learn-terraform
Name: learn-terraform
Path: /DC0/vm/learn-terraform
UUID: 2109e8a3-39ef-5a51-8ccb-4ad1a02d5864
Guest name: ubuntu64Guest
Memory: 1024MB
CPU: 2 vCPU(s)
Power state: poweredOn
Boot time: 2023-06-23 16:12:02.915642 -0400 EDT
IP address:
Host: DC0_H0
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 provisioned the VM successfully by using govc
.
$ GOVC_URL=https://user:pass@127.0.0.1:8989/sdk GOVC_INSECURE=1 govc vm.info learn-terraform-doubled
Name: learn-terraform-doubled
Path: /DC0/vm/learn-terraform-doubled
UUID: 2109e8a3-39ef-5a51-8ccb-4ad1a02d5864
Guest name: ubuntu64Guest
Memory: 2048MB
CPU: 2 vCPU(s)
Power state: poweredOn
Boot time: 2023-06-28 10:04:36.983535 -0400 EDT
IP address:
Host: DC0_H0
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 using govc
.
$ GOVC_URL=https://user:pass@127.0.0.1:8989/sdk GOVC_INSECURE=1 govc snapshot.tree -vm learn-terraform-doubled -d -i
[snapshot-95] learn-tf-ubuntu-snapshot - Created using Terraform
[snapshot-95] . - Created using Terraform
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