How to Create ESXi Template in vSphere - using Packer
Overview
Packer uses the HashiCorp Configuration Language - HCL - designed to allow concise descriptions of the required steps to get to a build file. This page describes the features of HCL2 exhaustively
File structure i have used:
Below two files are commonly used by all the templates. which will be called using packer tool
1. common.pkrvars.hcl
1// Virtual Machine Settings
2common_vm_version = 19
3common_tools_upgrade_policy = true
4common_remove_cdrom = true
5
6// Template and Content Library Settings
7common_template_conversion = false
8common_content_library_name = "vm_library"
9common_content_library_ovf = false
10common_content_library_destroy = false
11common_content_library_skip_export = false
12
13// Removable Media Settings
14common_iso_datastore = "OS_ISO"
15
16// Boot and Provisioning Settings
17common_data_source = "http"
18common_http_ip = null
19common_http_port_min = 8000
20common_http_port_max = 8099
21common_ip_wait_timeout = "20m"
22common_ip_settle_timeout = "5s"
23common_shutdown_timeout = "15m"
2. vsphere.pkrvars.hcl
1// vSphere Credentials - replace with your configuration
2vsphere_endpoint = "vcsa.cloudbricks.local"
3vsphere_username = "Administrator@vsphere.local"
4vsphere_password = "SuperSecretPassword"
5vsphere_insecure_connection = false
6
7// vSphere Settings
8vsphere_datacenter = "Lab"
9//vsphere_cluster = "Cluster"
10vsphere_host = "esx-base.cloudbricks.local"
11vsphere_datastore = "SSD"
12vsphere_network = "cloudbricks"
13vsphere_folder = "Templates"
14
15vsphere_set_host_for_datastore_uploads = false
Below files are used for ESXi template (This template I'm using to crete nested VMware Lab)
1. variables.pkr.hcl
Contains all input variables to which you assign values. Any explicit values in this file will override the declared default values (these are found in the following file). The auto extension enables Packer to use this file automatically. It does not require you to reference or pass it in the command line explicitly
1variable "vsphere_endpoint" {
2 type = string
3 description = "The fully qualified domain name or IP address of the vCenter Server instance."
4}
5
6variable "vsphere_username" {
7 type = string
8 description = "The username to login to the vCenter Server instance."
9 sensitive = true
10}
11
12variable "vsphere_password" {
13 type = string
14 description = "The password for the login to the vCenter Server instance."
15 sensitive = true
16}
17
18variable "vsphere_insecure_connection" {
19 type = bool
20 description = "Do not validate vCenter Server TLS certificate."
21}
22
23// vSphere Settings
24
25variable "vsphere_datacenter" {
26 type = string
27 description = "The name of the target vSphere datacenter."
28 default = ""
29}
30
31variable "vsphere_cluster" {
32 type = string
33 description = "The name of the target vSphere cluster."
34 default = ""
35}
36
37variable "vsphere_host" {
38 type = string
39 description = "The name of the target ESXi host."
40 default = ""
41}
42
43variable "vsphere_datastore" {
44 type = string
45 description = "The name of the target vSphere datastore."
46}
47
48variable "vsphere_network" {
49 type = string
50 description = "The name of the target vSphere network segment."
51}
52
53variable "vsphere_folder" {
54 type = string
55 description = "The name of the target vSphere folder."
56 default = ""
57}
58
59variable "vsphere_resource_pool" {
60 type = string
61 description = "The name of the target vSphere resource pool."
62 default = ""
63}
64
65variable "vsphere_set_host_for_datastore_uploads" {
66 type = bool
67 description = "Set this to true if packer should use the host for uploading files to the datastore."
68 default = false
69}
70
71// Virtual Machine Settings
72
73variable "vm_guest_os_language" {
74 type = string
75 description = "The guest operating system lanugage."
76 default = "en_US"
77}
78
79variable "vm_guest_os_keyboard" {
80 type = string
81 description = "The guest operating system keyboard input."
82 default = "us"
83}
84
85variable "vm_guest_os_timezone" {
86 type = string
87 description = "The guest operating system timezone."
88 default = "UTC"
89}
90
91variable "vm_guest_os_family" {
92 type = string
93 description = "The guest operating system family. Used for naming and VMware Tools."
94}
95
96variable "vm_guest_os_name" {
97 type = string
98 description = "The guest operating system name. Used for naming."
99}
100
101variable "vm_guest_os_version" {
102 type = string
103 description = "The guest operating system version. Used for naming."
104}
105
106variable "vm_guest_os_type" {
107 type = string
108 description = "The guest operating system type, also know as guestid."
109}
110
111variable "vm_firmware" {
112 type = string
113 description = "The virtual machine firmware."
114 default = "efi"
115}
116
117variable "vm_cdrom_type" {
118 type = string
119 description = "The virtual machine CD-ROM type."
120 default = "sata"
121}
122
123variable "vm_cpu_count" {
124 type = number
125 description = "The number of virtual CPUs."
126}
127
128variable "vm_cpu_cores" {
129 type = number
130 description = "The number of virtual CPUs cores per socket."
131}
132
133variable "vm_cpu_hot_add" {
134 type = bool
135 description = "Enable hot add CPU."
136 default = false
137}
138
139variable "vm_mem_size" {
140 type = number
141 description = "The size for the virtual memory in MB."
142}
143
144variable "vm_mem_hot_add" {
145 type = bool
146 description = "Enable hot add memory."
147 default = false
148}
149
150variable "vm_disk_size" {
151 type = number
152 description = "The size for the virtual disk in MB."
153}
154
155variable "vm_disk_controller_type" {
156 type = list(string)
157 description = "The virtual disk controller types in sequence."
158 default = ["pvscsi"]
159}
160
161variable "vm_disk_thin_provisioned" {
162 type = bool
163 description = "Thin provision the virtual disk."
164 default = true
165}
166
167variable "vm_network_card" {
168 type = string
169 description = "The virtual network card type."
170 default = "vmxnet3"
171}
172
173variable "common_vm_version" {
174 type = number
175 description = "The vSphere virtual hardware version."
176}
177
178variable "common_tools_upgrade_policy" {
179 type = bool
180 description = "Upgrade VMware Tools on reboot."
181 default = true
182}
183
184variable "common_remove_cdrom" {
185 type = bool
186 description = "Remove the virtual CD-ROM(s)."
187 default = true
188}
189
190// Template and Content Library Settings
191
192variable "common_template_conversion" {
193 type = bool
194 description = "Convert the virtual machine to template. Must be 'false' for content library."
195 default = false
196}
197
198variable "common_content_library_name" {
199 type = string
200 description = "The name of the target vSphere content library, if used."
201 default = null
202}
203
204variable "common_content_library_ovf" {
205 type = bool
206 description = "Export to content library as an OVF template."
207 default = true
208}
209
210variable "common_content_library_destroy" {
211 type = bool
212 description = "Delete the virtual machine after exporting to the content library."
213 default = true
214}
215
216variable "common_content_library_skip_export" {
217 type = bool
218 description = "Skip exporting the virtual machine to the content library. Option allows for testing/debugging without saving the machine image."
219 default = false
220}
221
222// OVF Export Settings
223
224variable "common_ovf_export_enabled" {
225 type = bool
226 description = "Enable OVF artifact export."
227 default = false
228}
229
230variable "common_ovf_export_overwrite" {
231 type = bool
232 description = "Overwrite existing OVF artifact."
233 default = true
234}
235
236// Removable Media Settings
237
238variable "common_iso_datastore" {
239 type = string
240 description = "The name of the source vSphere datastore for the guest operating system ISO."
241}
242
243variable "iso_path" {
244 type = string
245 description = "The path on the source vSphere datastore for the guest operating system ISO."
246}
247
248variable "iso_file" {
249 type = string
250 description = "The file name of the guest operating system ISO."
251}
252
253// Boot Settings
254
255variable "common_data_source" {
256 type = string
257 description = "The provisioning data source. One of `http` or `disk`."
258}
259
260variable "common_http_ip" {
261 type = string
262 description = "Define an IP address on the host to use for the HTTP server."
263 default = null
264}
265
266variable "common_http_port_min" {
267 type = number
268 description = "The start of the HTTP port range."
269}
270
271variable "common_http_port_max" {
272 type = number
273 description = "The end of the HTTP port range."
274}
275
276variable "vm_boot_order" {
277 type = string
278 description = "The boot order for virtual machines devices."
279 default = "disk,cdrom"
280}
281
282variable "vm_boot_wait" {
283 type = string
284 description = "The time to wait before boot."
285}
286
287variable "common_ip_wait_timeout" {
288 type = string
289 description = "Time to wait for guest operating system IP address response."
290}
291
292variable "common_ip_settle_timeout" {
293 type = string
294 description = "Time to wait for guest operating system IP to settle down."
295 default = "5s"
296}
297
298variable "common_shutdown_timeout" {
299 type = string
300 description = "Time to wait for guest operating system shutdown."
301}
302
303// Communicator Settings and Credentials
304
305
306
307variable "communicator_proxy_host" {
308 type = string
309 description = "The proxy server to use for SSH connection. (Optional)"
310 default = null
311}
312
313variable "communicator_proxy_port" {
314 type = number
315 description = "The port to connect to the proxy server. (Optional)"
316 default = null
317}
318
319variable "communicator_proxy_username" {
320 type = string
321 description = "The username to authenticate with the proxy server. (Optional)"
322 default = null
323}
324
325variable "communicator_proxy_password" {
326 type = string
327 description = "The password to authenticate with the proxy server. (Optional)"
328 sensitive = true
329 default = null
330}
331
332variable "communicator_port" {
333 type = string
334 description = "The port for the communicator protocol."
335}
336
337variable "communicator_timeout" {
338 type = string
339 description = "The timeout for the communicator protocol."
340}
2. esxi.pkr.hcl
This file contains the blocks as mentioned in the refernece, from declared variables, build and source blocks. Packer uses this to automate the VM template creation.
1
2// BLOCK: packer config
3
4
5packer {
6 required_version = ">= 1.9.4"
7 required_plugins {
8 vsphere = {
9 source = "github.com/hashicorp/vsphere"
10 version = ">= 1.2.1"
11 }
12
13 }
14}
15
16// BLOCK: data
17// Defines the data sources
18
19// BLOCK: locals
20// Defines the local variables.
21
22locals {
23 iso_paths = ["[${var.common_iso_datastore}] ${var.iso_path}/${var.iso_file}"]
24 vm_name = "${var.vm_guest_os_name}${var.vm_guest_os_version}"
25
26}
27
28// BLOCK: source
29// Defines the builder configuration blocks.
30
31source "vsphere-iso" "esxi" {
32
33 // vCenter Server Endpoint Settings and Credentials
34 vcenter_server = var.vsphere_endpoint
35 username = var.vsphere_username
36 password = var.vsphere_password
37 insecure_connection = var.vsphere_insecure_connection
38
39 // vSphere Settings
40 datacenter = var.vsphere_datacenter
41 cluster = var.vsphere_cluster
42 host = var.vsphere_host
43 datastore = var.vsphere_datastore
44 folder = var.vsphere_folder
45 resource_pool = var.vsphere_resource_pool
46 set_host_for_datastore_uploads = var.vsphere_set_host_for_datastore_uploads
47
48 // Virtual Machine Settings
49 vm_name = local.vm_name
50 guest_os_type = var.vm_guest_os_type
51 firmware = var.vm_firmware
52 CPUs = var.vm_cpu_count
53 cpu_cores = var.vm_cpu_cores
54 NestedHV = true
55 CPU_hot_plug = var.vm_cpu_hot_add
56 RAM = var.vm_mem_size
57 RAM_hot_plug = var.vm_mem_hot_add
58 cdrom_type = var.vm_cdrom_type
59 disk_controller_type = var.vm_disk_controller_type
60 storage {
61 disk_size = var.vm_disk_size
62 disk_thin_provisioned = var.vm_disk_thin_provisioned
63 }
64 network_adapters {
65 network = var.vsphere_network
66 network_card = var.vm_network_card
67 }
68 vm_version = "21"
69 remove_cdrom = var.common_remove_cdrom
70 tools_upgrade_policy = var.common_tools_upgrade_policy
71
72
73 // Removable Media Settings
74 iso_paths = local.iso_paths
75
76 // Boot and Provisioning Settings
77 http_ip = var.common_data_source == "http" ? var.common_http_ip : null
78 http_port_min = var.common_data_source == "http" ? var.common_http_port_min : null
79 http_port_max = var.common_data_source == "http" ? var.common_http_port_max : null
80 boot_order = var.vm_boot_order
81 boot_wait = var.vm_boot_wait
82 http_directory = "/opt/code/packer/vmware/esxi8/data"
83 boot_command = ["<leftShift>O ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/esx-ks.cfg<enter>"]
84 ip_wait_timeout = var.common_ip_wait_timeout
85 ip_settle_timeout = var.common_ip_settle_timeout
86 shutdown_command = "esxcli system maintenanceMode set -e true -t 0 ; esxcli system shutdown poweroff -d 10 -r 'Packer Shutdown' ; esxcli system maintenanceMode set -e false -t 0 "
87 shutdown_timeout = var.common_shutdown_timeout
88
89 // Communicator Settings and Credentials
90 communicator = "ssh"
91 ssh_proxy_host = var.communicator_proxy_host
92 ssh_proxy_port = var.communicator_proxy_port
93 ssh_proxy_username = var.communicator_proxy_username
94 ssh_proxy_password = var.communicator_proxy_password
95 ssh_username = "root"
96 ssh_password = "VMware1!"
97 ssh_port = var.communicator_port
98 ssh_timeout = var.communicator_timeout
99
100 // Template and Content Library Settings
101 convert_to_template = true
102}
103
104// BLOCK: build
105
106build {
107 sources = ["source.vsphere-iso.esxi"]
108
109}
3. esxi.auto.pkrvars.hcl
Once a variable is declared in the configuration, we can set it as individual or using *.auto.pkrvars.hcl
Individually, with the -var foo=bar command line option.
In variable definitions files, either specified on the command line with the -var-files values.pkrvars.hcl or automatically loaded (*.auto.pkrvars.hcl).
1// Guest Operating System Metadata
2vm_guest_os_language = "en_US"
3vm_guest_os_keyboard = "us"
4vm_guest_os_timezone = "UTC"
5vm_guest_os_family = "esxi"
6vm_guest_os_name = "esxi"
7vm_guest_os_version = "8"
8
9// Virtual Machine Guest Operating System Setting
10vm_guest_os_type = "vmkernel8Guest"
11
12// Virtual Machine Hardware Settings
13vm_firmware = "efi"
14vm_cdrom_type = "sata"
15vm_cpu_count = 2
16vm_cpu_cores = 2
17vm_cpu_hot_add = false
18vm_mem_size = 8192
19vm_mem_hot_add = false
20vm_disk_size = 40960
21vm_disk_controller_type = ["pvscsi"]
22vm_disk_thin_provisioned = true
23vm_network_card = "vmxnet3"
24
25// Removable Media Settings
26iso_path = "ISO"
27iso_file = "esxi8_u2.iso"
28
29// Boot Settings
30vm_boot_wait = "1s"
31
32// Communicator Settings
33communicator_port = 22
34communicator_timeout = "30m"
4. esx-ks.cfg under data folder
All Packer can do without a kickstart file is provision the virtual machine and destroy it in vCenter (the build will time out as it has no connectivity to the VM itself). The kickstart automates the installation of the operating system, in this case, Rocky Linux.
1#
2# Sample scripted installation file
3#
4# Accept EULA
5vmaccepteula
6# Set root password
7rootpw VMware1!
8#Install on local disk overwriting any existing VMFS datastore
9install --firstdisk --overwritevmfs
10# Network configuration
11network --bootproto=dhcp --device=vmnic0
12#Reboot after installation completed
13reboot
14
15%firstboot --interpreter=busybox
16#esx/ssh
17vim-cmd hostsvc/enable_ssh
18vim-cmd hostsvc/start_ssh
19esxcli system settings advanced set -o /UserVars/SuppressShellWarning -i 1
20#esxi/ssh end
Now that we have all the required configurations in place, will process with build.
To start the build, enter the following command from /opt/code/packer folder
1packer build -var-file=common.pkrvars.hcl -var-file=vsphere.pkrvars.hcl vmware/esxi8/
it took, approximately 6 minutes for me to complete the template creation