Packer variables

This blog entry will demonstrate how to use variables inside Packer Template file.

In the previous post we've build our first vSphere Windows image using Packer. Today I will show you how to use variables inside Packer template file.

Packer variables

Packer variables allow you re-use same Packer template file multiple times within different vSphere environments. If we would hardcode all settings inside template file, like we did in my previous post, we would then have to create Packer template file for each vSphere instance. You will probably agree that this is not very efficient as we need change only few settings inside existing template file , mostly for vCenter server objects, to re-use it within different environment..

My assumption is that you want to build your images across different vCenter instances. Even with single vCenter instance variables can be quite helpful as you could use same template file to build Windows Server 2016 and Windows Server 2019.

Since we want to fully automate image build process using Azure DevOps, it's advised to use variables for security and flexibility reasons.

Define variable inside template file

To define variables we need to first define variables section inside Packer template file. Variable naming convention is up to you, but I suggestto use names that are self explanatory. Also do not use spaces naming your variables.

Below is an example how to define variable section:

1"variables": {
2    "vsphere_server": "",
3    "vsphere_user": "",
4    "vsphere_password": ""
5  },

In this example we want to set three variables for vCenter server name, vCenter username and vCenter password. Next we need to define those variables inside Packer template file:

 1 "builders": [
 2    {
 3      "type": "vsphere-iso",
 4
 5      "vcenter_server":      "{{user `vsphere_server`}}",
 6      "username":            "{{user `vsphere_user`}}",
 7      "password":            "{{user `vsphere_password`}}",
 8      "insecure_connection": "true"
 9    }
10 ]

Define variable values

To define values for our variables we need to create dedicated variable file. Same as Packer template file, variable file is stored in JSON format. You can use random naming convention, but for simplicity, let's name our file vars.json and store it in same directory as Packer template file. Variable file should look like this:

1{
2    "vsphere_server": "vcsa01.lab.local",
3    "vsphere_user": "packer@vsphere.local",
4    "vsphere_password": "VMware1!"
5    }

Which values should you store as variable?

Packer is very flexible and allow to store each option in template file as variable. This is very handy. For example, you can store patch to you answer file as variable, place answer files in dedicated directories and point Packer to proper answer file depending which OS you want to create. From the other hand I don't see the point of storing options like CPU number or amount of memory as variable. Basically those values are not important as, at the end, we change those values during provision process based on system/user requirements. So what values should be stored as variable? From my experience those should be:

VariableDescription
vsphere_serverStores vCenter server name.
vsphere_userStores vCenter username.
vsphere_passwordvSphere password for account that has required permissions to create images.
vsphere_folderStores vCenter Folder path where our image will be placed.
vsphere_dc_nameStores vCenter datacenter name.
vsphere_clusterStores vCenter cluster name where our image will be placed.
vsphere_hostStores ESXi host name where our image will be placed
vsphere_portgroup_nameStores vCenter portgroup name where our image will be connected to.
vsphere_datastoreStore vCenter datastore name where our image will be deployed.
winadmin_passwordWindows password used by WinRM to connect with image to perform customization.
os_iso_pathPath to datastore where Windows installation media is placed.
vmtools_iso_pathPath to datastore where VMware Tools installation media is placed.
autounattend_pathPath to autounattend.xml answer file.
template_nameImage name that we want to apply on the image.
versionVM hardware version that we want to apply on our image.

You may wonder why we need to put so many variables into single template file? Well - because each vSphere environment can be different. You may have vSphere 6 and 6.7 that you manage so for each environment image hardware version can be different etc. It will make more sense later on once we start build our images fully automatically, trust me.

Ok, but why I have specified "vsphere_host" since "vSphere _cluster" has been set as variable? The thing is that account that we specified and assigned through terraform, provides permissions on cluster level without propagation. We assign sufficient permissions on host level to better secure our environment.

At the end you should get one additional file inside your folder and all listed variables should be offloaded from your template file.
Under 03 - Packer variables folder you will find working set of files with offloaded variable files. You can simply place your sensitive values inside vars.json file and run: packer build -var-file=".\vars.json" windows-server-2016.json command once you are in directory with your template file.
This command will tell Packer to use vars.json as variable file and windows-server-2016.json as main template file.

 1PS C:\Packer\Blog_variables> packer build -var-file=".\vars.json" windows-server-2016.json
 2
 3vsphere-iso: output will be in this color.
 4
 5==> vsphere-iso: Creating VM...
 6==> vsphere-iso: Customizing hardware...
 7==> vsphere-iso: Mounting ISO images...
 8==> vsphere-iso: Adding configuration parameters...
 9==> vsphere-iso: Creating floppy disk...
10    vsphere-iso: Copying files flatly from floppy_files
11    vsphere-iso: Copying file: Autounattend_Windows2016/autounattend.xml
12    vsphere-iso: Copying file: scripts/winrm.ps1
13    vsphere-iso: Copying file: scripts/vmtools.ps1
14    vsphere-iso: Done copying files from floppy_files
15    vsphere-iso: Collecting paths from floppy_dirs
16    vsphere-iso: Resulting paths from floppy_dirs : []
17    vsphere-iso: Done copying paths from floppy_dirs
18==> vsphere-iso: Uploading created floppy image
19==> vsphere-iso: Adding generated Floppy...
20==> vsphere-iso: Set boot order temporary...
21==> vsphere-iso: Power on VM...
22==> vsphere-iso: Waiting for IP...
23==> vsphere-iso: IP address: 192.168.1.88
24==> vsphere-iso: Using winrm communicator to connect: 192.168.1.88
25==> vsphere-iso: Waiting for WinRM to become available...
26    vsphere-iso: WinRM connected.
27==> vsphere-iso: Connected to WinRM!
28==> vsphere-iso: Shutting down VM...
29==> vsphere-iso: Deleting Floppy drives...
30==> vsphere-iso: Deleting Floppy image...
31==> vsphere-iso: Eject CD-ROM drives...
32==> vsphere-iso: Convert VM into template...
33==> vsphere-iso: Clear boot order...
34Build 'vsphere-iso' finished after 18 minutes 28 seconds.
35
36==> Wait completed after 18 minutes 28 seconds
37
38==> Builds finished. The artifacts of successful builds are:
39--> vsphere-iso: Windows2016-Template

Variables inside Windows answer file

You are probably wonder if variables can be used inside answer file to re-use autounattend.xml file with different Windows versions? Autounattend.xml does not support variables natively, but with help of Azure DevOps third party tools we can bypass variables into autounattend.xml as well. This will be demonstrated in later posts once we will start to create our images using Azure DevOps.

In next blog we will tweak our image using provisioners and post-processors.

Thanks for reading!