Author Archives: Stephan

Auto Connecting Devices to a VMware Workstation VM

As part of my latest homelab setup, I run my vCenter Server VM and Domain Controller VM on VMware Workstation Pro 16 and not on an ESXi host. This is mainly so I don’t have to fire up a noisy rack mount server when I only need my vCenter Server for something.

This did leave me with the problem of a lack of physical ports in my Windows PC to be able to bridge my VM’s with, so out came the trusty USB Ethernet adapter!

By default, this USB device is connected to the host machine. As I would always forget to connect this to my domain controller VM(which is the VM I bridged with this interface), I would end up having to reboot it once I had connected it as I would get all sorts of problems, including DNS issues.

To address this, there is a line that can be added to the vmx file of the VM to auto connect a specific device to a VM. It took me a while to find the information that was pertinent to specifically version 16 of VMware Workstation so I thought I would write a quick article on it.

Firstly, you need to grab the VID and PID values from the registry by looking through the devices in the following path, or searching if you have an idea on what its friendly name is:

Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB

Once you have these values, open up the VMX file of the VM you want to auto connect the device to in a text editor, making sure the VM is in a powered off state first.

Add the following line to the file, replacing the VID and PID values with the ones you located in the registry earlier.

usb_xhci.autoconnect.device0 = "vid:0bda pid:8152"

Now, power on the VM and you will see the device is automatically connected to the VM and not the Host!

I hope this has been useful. I aim to get some more blog posts up on my lab setup this year, so keep an eye out if you are looking for idea’s for your own home lab.

Thanks for reading!

Failed to Deploy OVF Package – vSphere Content Libraries

I recently came across an issue with creating subscribed VMware Content Libraries, and deploying templates from a Content Library.

An error similar to the one below, would be received when attempting to deploy a VM template or OVF from a Content Library, or an error related to connection issues when setting up a subscribed Content Library.

Failed to deploy OVF Package.  ThrowablePrxy.cause A general system error occurred: Transfer failed.

After some investigation, I came to see that vCenter was attempting to communicate with linked vCenter’s and hosts via the web proxy that was configured in the VAMI, when attempting to deploy an OVF from a Content Library or when trying to synchronise a library.

As I didn’t want this traffic going via the proxy as it is internal traffic, a support ticket was logged. It was advised to add proxy exceptions, or bypasses, to the proxy file located here on a vCenter Server Appliance:

/etc/sysconfig/proxy

As this information isn’t something I managed to find documented publicly and support couldn’t provide me with anything as they were using internal documentation to assist, I thought I would write a quick post on it to help anyone facing the same issue!

Note: Always test in a non production environment and contact official support channels!

To begin reviewing and editing this file, you will need to SSH to the VCSA using the below command using your SSH tooling of choice.

ssh root@vm-vcsa-01.smt-lab.local

Using the following cat command you can then view the file:

cat /etc/sysconfig/proxy

Here is what the default file looks like with the HTTP and HTTPS options set:

# Enable a generation of the proxy settings to the profile.
# This setting allows to turn the proxy on and off while
# preserving the particular proxy setup.
#
PROXY_ENABLED="no"

# Some programs (e.g. wget) support proxies, if set in
# the environment.
# Example: HTTP_PROXY="http://proxy.provider.de:3128/"
HTTP_PROXY="proxy.smt-lab.local"

# Example: HTTPS_PROXY="https://proxy.provider.de:3128/"
HTTPS_PROXY="proxy.smt-lab.local"

# Example: FTP_PROXY="http://proxy.provider.de:3128/"
FTP_PROXY=""

# Example: GOPHER_PROXY="http://proxy.provider.de:3128/"
GOPHER_PROXY=""

# Example: SOCKS_PROXY="socks://proxy.example.com:8080"
SOCKS_PROXY=""

# Example: SOCKS5_SERVER="office-proxy.example.com:8881"
SOCKS5_SERVER=""

# Example: NO_PROXY="www.me.de, do.main, localhost"
NO_PROXY="localhost, 127.0.0.1"

Take note of the section at the bottom, “NO_PROXY”. This is where we need to add the fqdn’s of any hosts and VCSA’s you wish to deploy to, or subscribe with. If however you don’t want to maintain this for each and every host, you can add a wild card:

.*.domain.name

Note the ‘.’ at the beginning!

For instance, in my lab I would add the following entry to the NO_PROXY list:

.*.smt-lab.local

To edit this we can use the VI editor (More info on using VI here.):

vi /etc/sysconfig/proxy

Edit the file to include the FQDN’s or a wildcard, based on your requirements.

# Example: NO_PROXY="www.me.de, do.main, localhost"
NO_PROXY="localhost, 127.0.0.1, .*.smt-lab.local"

I found a short period of time is needed for this to take effect. Or you can reboot the VCSA to speed things along.

Following this, you will then be able to deploy or subscribe without issue!

Hope this has been useful and thanks for reading!

Different Tools; Same Result – vSphere Distributed Port Groups

As technology moves forward, more and more ways to achieve your goal become available. Many people still rely on the good old trusty GUI to achieve their goal, I know I do at times. Is this because it’s quicker, more comfortable or familiar? Or perhaps because they don’t realise there are other options out there!?

This blog post will be one of many, where I highlight some of the options available for completing various technical tasks or configurations, in the hope it can provide additional options or tools for consideration.

To kick off, let’s take a look at a common example for a vSphere Administrator, creating Port Groups on a Distributed Switch.

vSphere Client

So let’s first look at the process via the GUI, in this case, the vSphere Client. I wont go into too much detail on the steps involved, as it is a well documented process, but the screenshots are below:

Repeat for the remaining Port Groups and you will be left with the finished article.

And there we have it, three Port Groups on a distributed Switch. Now, imagine doing this for 10’s or 100’s of Port Groups? It’s going to be slow and painful, so let’s look at some other options.

PowerShell

Firstly, PowerShell, specifically the VMware PowerCLI PowerShell module. Here is an example script that will create the same three Port Groups that we did using the GUI:

$vDSName = "vDS-Workload-Networks"
$Ports = "8"
$LoadBalancing = "LoadBalanceLoadBased" 
$ActiveUP = "Uplink 1", "Uplink 2"

$vdpgs = @(
    [pscustomobject]@{PG = 'dvPG-Guest-VM-1'; VLANID = '20'}
    [pscustomobject]@{PG = 'dvPG-Guest-VM-2'; VLANID = '21'}
    [pscustomobject]@{PG = 'dvPG-Secure-VM-1'; VLANID = '25'}
)

#Create Distributed Virtual Port Group.
ForEach ($vdpg in $vdpgs) {
    Get-VDSwitch -Name $vDSName | New-VDPortGroup -Name $VDPG.PG -VLanId $VDPG.VLANID -NumPorts $Ports
    #Set Load Balancing option
    Get-VDswitch -Name $vDSName | Get-VDPortgroup $VDPG.PG | Get-VDUplinkTeamingPolicy | Set-VDUplinkTeamingPolicy -LoadBalancingPolicy $LoadBalancing -ActiveUplinkPort $ActiveUP
}

So lets break down this code. Firstly we are defining some variables;

  • $vDSName – This is the name of an existing virtual distributed switch in which you will be creating your Port Groups.
  • $Ports – This defines the number of ports the Port Group will be initially configured with. (By default 128 ports are created, there is nothing wrong with using the default, see the note further down as to why I have specified 8.)
  • $LoadBalancing – This is the load balancing policy I wish to set for the Port Group. Available options are:LoadBalanceLoadBased, LoadBalanceIP, LoadBalanceSrcMac, LoadBalanceSrcId, ExplicitFailover. This can be adjusted as required.
  • $ActiveUP – This variable defines the uplinks you wish to set as active for the Port Group. (If you want to add standby uplinks, you could add this parameter in too)
  • $VDPGS – Finally, this is an array containing both the name and VLAN ID for each Port Group.

Now we have our input information in variables, we move onto the next two lines of code. These are within a ‘ForEach Loop’. This will take each entry within an array and run a block of code against it. In this case, each Port Group we wish to create.

So for each entry in the array, ‘Get-VDswitch -Name $vDSName‘ gets the existing Virtual Distributed Switch based on the variable and then pipes (‘|’) this into the command (New-VDPortGroup -Name $VDPG.PG -VLanId $VDPG.VLANID -NumPorts $Ports) to create the Port Group on the Distributed Switch, using the properties set for each line of the array.

Secondly, we get the Port Group we just created (Get-VDswitch -Name $vDSName | Get-VDPortgroup $VDPG.PG) and then ‘Get & Set’ the Teaming and Loadbalancing options (Get-VDUplinkTeamingPolicy | Set-VDUplinkTeamingPolicy -LoadBalancingPolicy $LoadBalancing -ActiveUplinkPort $ActiveUP), again ‘piping’ the results into the next command.

Below is the output from PowerShell after running the script above:

Name                           NumPorts PortBinding
----                           -------- -----------
dvPG-Guest-VM-1                8        Static

VDPortgroup                      : dvPG-Guest-VM-1
NotifySwitches                   : True
NotifySwitchesInherited          : True
LoadBalancingPolicy              : LoadBalanceLoadBased
LoadBalancingPolicyInherited     : False
FailoverDetectionPolicy          : LinkStatus
ActiveUplinkPort                 : {Uplink 1, Uplink 2}
StandbyUplinkPort                : {}
UplinkPortOrderInherited         : False
Failback                         : False
EnableFailback                   : True
FailbackInherited                : True
UnusedUplinkPort                 : {}
FailoverDetectionPolicyInherited : True
Uid                              : /VIServer=vsphere.local\administrator@vm-vcsa-01.smt-lab.local:443/VDPortgroupUplinkTeamingPolicy=cec49f0b7f124d0c9f37814392494a31/

dvPG-Guest-VM-2                8        Static

VDPortgroup                      : dvPG-Guest-VM-2
NotifySwitches                   : True
NotifySwitchesInherited          : True
LoadBalancingPolicy              : LoadBalanceLoadBased
LoadBalancingPolicyInherited     : False
FailoverDetectionPolicy          : LinkStatus
ActiveUplinkPort                 : {Uplink 1, Uplink 2}
StandbyUplinkPort                : {}
UplinkPortOrderInherited         : False
Failback                         : False
EnableFailback                   : True
FailbackInherited                : True
UnusedUplinkPort                 : {}
FailoverDetectionPolicyInherited : True
Uid                              : /VIServer=vsphere.local\administrator@vm-vcsa-01.smt-lab.local:443/VDPortgroupUplinkTeamingPolicy=e126093ed67f45a3b7c42874c5affc20/

dvPG-Secure-VM-1               8        Static

VDPortgroup                      : dvPG-Secure-VM-1
NotifySwitches                   : True
NotifySwitchesInherited          : True
LoadBalancingPolicy              : LoadBalanceLoadBased
LoadBalancingPolicyInherited     : False
FailoverDetectionPolicy          : LinkStatus
ActiveUplinkPort                 : {Uplink 1, Uplink 2}
StandbyUplinkPort                : {}
UplinkPortOrderInherited         : False
Failback                         : False
EnableFailback                   : True
FailbackInherited                : True
UnusedUplinkPort                 : {}
FailoverDetectionPolicyInherited : True
Uid                              : /VIServer=vsphere.local\administrator@vm-vcsa-01.smt-lab.local:443/VDPortgroupUplinkTeamingPolicy=b5f1889461584b1daf314379cd935f50/

Terraform

Now let’s take a look at using Terraform to achieve the same result. Terraform is an infrastructure and code tool used to manage infrastructure in the form of configuration files and state:

provider "vsphere" {
  vsphere_server = "vCenter Server FQDN"
  user           = "Domain\\Username"
  password       = "Password"
}
data "vsphere_datacenter" "datacenter" {
  name = "dc-smt-01"
}
data "vsphere_distributed_virtual_switch" "vds" {
  name          = "vDS-Workload-Networks"
  datacenter_id = data.vsphere_datacenter.datacenter.id
}
resource "vsphere_distributed_port_group" "pg20" {
  name                            = "dvPG-Guest-VM-1"
  distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.vds.id
  number_of_ports                 = 8
  vlan_id                         = 20
}
resource "vsphere_distributed_port_group" "pg21" {
  name                            = "dvPG-Guest-VM-2"
  distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.vds.id
  number_of_ports                 = 8
  vlan_id                         = 21
}
resource "vsphere_distributed_port_group" "pg25" {
  name                            = "dvPG-Secure-VM-1"
  distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.vds.id
  number_of_ports                 = 8
  vlan_id                         = 25
}

Lets break this down.

First we are specifying which terraform provider we want to use, this will be the vSphere provider in this case. We are then providing some parameters for Terraform to connect to your vCenter instance; VCSA FQDN and credentials.

We then have two ‘data’ blocks. These are used to get information about an existing resource, such as the Distributed Switch and the Datacenter it resides in. You could loosely consider this similar to populating variables in the PowerShell example.

Next we have three ‘resource’ blocks. Each block represents one of the three Port Groups we want to configure. It provides parameters for Name, number of ports and vlan ID for each, along with a reference to the Distributed Switch from the ‘data’ block.

Now when you run ‘terraform apply’ to apply for code, here is the output:

terraform apply  


Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # vsphere_distributed_port_group.pg20 will be created
  + resource "vsphere_distributed_port_group" "pg20" {
      + active_uplinks                    = (known after apply)
      + allow_forged_transmits            = (known after apply)
      + allow_mac_changes                 = (known after apply)
      + allow_promiscuous                 = (known after apply)
      + auto_expand                       = true
      + block_all_ports                   = (known after apply)
      + check_beacon                      = (known after apply)
      + config_version                    = (known after apply)
      + directpath_gen2_allowed           = (known after apply)
      + distributed_virtual_switch_uuid   = "50 33 5e 01 05 1e 32 66-ea f7 7c 42 ce fa f1 96"
      + egress_shaping_average_bandwidth  = (known after apply)
      + egress_shaping_burst_size         = (known after apply)
      + egress_shaping_enabled            = (known after apply)
      + egress_shaping_peak_bandwidth     = (known after apply)
      + failback                          = (known after apply)
      + id                                = (known after apply)
      + ingress_shaping_average_bandwidth = (known after apply)
      + ingress_shaping_burst_size        = (known after apply)
      + ingress_shaping_enabled           = (known after apply)
      + ingress_shaping_peak_bandwidth    = (known after apply)
      + key                               = (known after apply)
      + lacp_enabled                      = (known after apply)
      + lacp_mode                         = (known after apply)
      + name                              = "dvPG-Guest-VM-1"
      + netflow_enabled                   = (known after apply)
      + network_resource_pool_key         = "-1"
      + notify_switches                   = (known after apply)
      + number_of_ports                   = 8
      + port_private_secondary_vlan_id    = (known after apply)
      + standby_uplinks                   = (known after apply)
      + teaming_policy                    = (known after apply)
      + tx_uplink                         = (known after apply)
      + type                              = "earlyBinding"
      + vlan_id                           = 20

      + vlan_range {
          + max_vlan = (known after apply)
          + min_vlan = (known after apply)
        }
    }

  # vsphere_distributed_port_group.pg21 will be created
  + resource "vsphere_distributed_port_group" "pg21" {
      + active_uplinks                    = (known after apply)
      + allow_forged_transmits            = (known after apply)
      + allow_mac_changes                 = (known after apply)
      + allow_promiscuous                 = (known after apply)
      + auto_expand                       = true
      + block_all_ports                   = (known after apply)
      + check_beacon                      = (known after apply)
      + config_version                    = (known after apply)
      + directpath_gen2_allowed           = (known after apply)
      + distributed_virtual_switch_uuid   = "50 33 5e 01 05 1e 32 66-ea f7 7c 42 ce fa f1 96"
      + egress_shaping_average_bandwidth  = (known after apply)
      + egress_shaping_burst_size         = (known after apply)
      + egress_shaping_enabled            = (known after apply)
      + egress_shaping_peak_bandwidth     = (known after apply)
      + failback                          = (known after apply)
      + id                                = (known after apply)
      + ingress_shaping_average_bandwidth = (known after apply)
      + ingress_shaping_burst_size        = (known after apply)
      + ingress_shaping_enabled           = (known after apply)
      + ingress_shaping_peak_bandwidth    = (known after apply)
      + key                               = (known after apply)
      + lacp_enabled                      = (known after apply)
      + lacp_mode                         = (known after apply)
      + name                              = "dvPG-Guest-VM-2"
      + netflow_enabled                   = (known after apply)
      + network_resource_pool_key         = "-1"
      + notify_switches                   = (known after apply)
      + number_of_ports                   = 8
      + port_private_secondary_vlan_id    = (known after apply)
      + standby_uplinks                   = (known after apply)
      + teaming_policy                    = (known after apply)
      + tx_uplink                         = (known after apply)
      + type                              = "earlyBinding"
      + vlan_id                           = 21

      + vlan_range {
          + max_vlan = (known after apply)
          + min_vlan = (known after apply)
        }
    }

  # vsphere_distributed_port_group.pg25 will be created
  + resource "vsphere_distributed_port_group" "pg25" {
      + active_uplinks                    = (known after apply)
      + allow_forged_transmits            = (known after apply)
      + allow_mac_changes                 = (known after apply)
      + allow_promiscuous                 = (known after apply)
      + auto_expand                       = true
      + block_all_ports                   = (known after apply)
      + check_beacon                      = (known after apply)
      + config_version                    = (known after apply)
      + directpath_gen2_allowed           = (known after apply)
      + distributed_virtual_switch_uuid   = "50 33 5e 01 05 1e 32 66-ea f7 7c 42 ce fa f1 96"
      + egress_shaping_average_bandwidth  = (known after apply)
      + egress_shaping_burst_size         = (known after apply)
      + egress_shaping_enabled            = (known after apply)
      + egress_shaping_peak_bandwidth     = (known after apply)
      + failback                          = (known after apply)
      + id                                = (known after apply)
      + ingress_shaping_average_bandwidth = (known after apply)
      + ingress_shaping_burst_size        = (known after apply)
      + ingress_shaping_enabled           = (known after apply)
      + ingress_shaping_peak_bandwidth    = (known after apply)
      + key                               = (known after apply)
      + lacp_enabled                      = (known after apply)
      + lacp_mode                         = (known after apply)
      + name                              = "dvPG-Secure-VM-1"
      + netflow_enabled                   = (known after apply)
      + network_resource_pool_key         = "-1"
      + notify_switches                   = (known after apply)
      + number_of_ports                   = 8
      + port_private_secondary_vlan_id    = (known after apply)
      + standby_uplinks                   = (known after apply)
      + teaming_policy                    = (known after apply)
      + tx_uplink                         = (known after apply)
      + type                              = "earlyBinding"
      + vlan_id                           = 25

      + vlan_range {
          + max_vlan = (known after apply)
          + min_vlan = (known after apply)
        }
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

vsphere_distributed_port_group.pg20: Creating...
vsphere_distributed_port_group.pg21: Creating...
vsphere_distributed_port_group.pg25: Creating...
vsphere_distributed_port_group.pg25: Creation complete after 0s [id=dvportgroup-2669728]
vsphere_distributed_port_group.pg21: Creation complete after 0s [id=dvportgroup-2669730]
vsphere_distributed_port_group.pg20: Creation complete after 0s [id=dvportgroup-2669729]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

For more information on the vSphere provider from Terraform, check out this link.

You will have noticed that I have explicitly defined the number of ports in both the PowerShell and Terraform examples. This is purely to match up with the default value that is set when using the vSphere Client; 8. By default the port allocation automatically expands as required, so this is for consistency rather than anything else.

If you are someone who relies heavily on a GUI as part of your work, I hope this have given you some idea’s on how you can perhaps leverage other options, especially when looking to build or configure in bulk.

Thanks for reading!

NSX-T Manager Certificate Replacement

I decided it was time to add VMware NSX-T to my HomeLab. I had been putting it off for a while but I couldn’t avoid it any longer!

Once I had fired up my NSX Manager Nodes and Cluster (I am using version 3.1), I looked to installing certificates. I choose to use a single certificate for all 3 of the NSX managers and the cluster using Subject Alternative Names (SAN’s) to simplify the process and this means I don’t need to renew 4 certificates each time.

As this is a different process to other VMware products I have put together a quick run through on how to achieve this.

Firstly, we need to generate the CSR from one of the NSX Manager nodes using openssl. SSH to one of your nodes and run the following command to create a new file called ‘ssl.conf’:

vim ssl.conf

Then populate this file with the below text, changing the values to suit your environment. I have left my values in to help with reading the file. If you are using a single NSX manager in your lab, you can remove the lines for DNS.3, DNS.4, IP.3 and IP.4.

[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no

[ req_distinguished_name ]
countryName = GB
stateOrProvinceName = Labshire
localityName = Lab City
organizationName = SMT-Lab
organizationalUnitName = SMT-Lab
commonName = vm-nsx-00.smt-lab.local

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1 = vm-nsx-00.smt-lab.local
DNS.2 = vm-nsx-01.smt-lab.local
DNS.3 = vm-nsx-02.smt-lab.local
DNS.4 = vm-nsx-03.smt-lab.local
IP.1 = 10.200.15.34
IP.2 = 10.200.15.35
IP.3 = 10.200.15.36
IP.4 = 10.200.15.37

Now to generate the CSR, run the following, but replacing the files names to suit:

openssl req -out vm-nsx-00.smt-lab.local.csr -newkey rsa:2048 -nodes -keyout vm-nsx-00.smt-lab.local.key -config ssl.conf -sha256

This will generate 2 files in the current working directory. You will have your CSR and the private key. Using something like WinSCP, copy the files off the NSX manager to a location of your choice.

Head off to your CA and issue the certificate using the CSR.

Now you need to copy the root and issuing (if you have an issuing CA) certificate to certificate you just created. This will complete the chain. Also have the private key handy as you are going to need it.

We are now ready to import import the certificate. Head to System > Certificates > Import and select Import Certificate.

Give it a name, browse to the certificate file that now includes the certificate chain, followed by browsing the the private key file. Be sure to change the ‘Service Certificate’ slider to ‘No’ and then click Import.

Once imported you can select it and see that it includes the certificates in the chain.

Now to assign them! Firstly, click on the identifier next to the name and copy the value. This is what will be used to target the certificate in the next steps.

To validate and replace the certificates in NSX we need to use API’s. Using a tool like Postman, validate then replace the certificate on the NSX Manager Cluster by running the following as a GET request. Note you need to provide credentials for the NSX managers on the Authorization tab.

https://vm-nsx-00.smt-lab.local/api/v1/trust-management/certificates/82c80092-3571-40cd-8960-3189594ec0f1?action=validate 

The result of ‘”status” : “ok”‘ is what we are looking for here.

Now its confirmed valid, lets replace the certificate by running the following POST request:

https://vm-nsx-00.smt-lab.local/api/v1/cluster/api-certificate?action=set_cluster_certificate&certificate_id=82c80092-3571-40cd-8960-3189594ec0f1

Then its time to apply to all nodes by running each line below:

https://vm-nsx-01.smt-lab.local/api/v1/node/services/http?action=apply_certificate&certificate_id=a94b3600-696b-43bf-a2df-c1e8e2180c3a

https://vm-nsx-02.smt-lab.local/api/v1/node/services/http?action=apply_certificate&certificate_id=a94b3600-696b-43bf-a2df-c1e8e2180c3a

https://vm-nsx-03.smt-lab.local/api/v1/node/services/http?action=apply_certificate&certificate_id=a94b3600-696b-43bf-a2df-c1e8e2180c3a

And that completes the replacement. If you browse to either your cluster address or individual nodes, you will see your new certificate in place.

You can find the full VMWare documentation on this here.

Thanks for reading!

Sessions I’ll Be Attending at VMworld 2021

VMworld 2021 is right around the corner! This year we are again unable to attend in person due to COVID-19, however this still gives everyone the chance to attend as its an online event!

It is running from the 5th to the 7th October this year –

Like many, I have been marking various sessions as favourites in the catalog ready for when they can be booked. Here are a few that are on my list this year.

Firstly, Frank’s session on NUMA has been on my list for all 3 years I have been attending. For anyone who uses vSphere, this is a must.

60 Minutes of Non-Uniform Memory Access (NUMA) 3rd Edition [MCL1853]

Pass Type: General and Tech+ Passes
Session by: Frank Denneman
Description:

“Although we enrich the stack with multiple layers of abstraction, obtaining consistent performance boils down to understanding the fundamentals. This requires the admin and the architect to focus on individual host components again. In this session, we dive into the impact the Multi-chip Module (MCM) has on scheduler behavior and workload sizing. Learn the underlying configuration of a virtual machine and discover the connection between the General-Purpose Graphics Processing Unit (GPGPU) and the NUMA node. Determine how the cores-per-socket impact a virtual NUMA configuration. We will look at the impact of heterogeneous clusters on workload performance and how you can detect faux-wide virtual machine configurations. You will understand how your knowledge of NUMA concepts in your cluster can help the developer by aligning the Kubernetes nodes to the physical infrastructure with the help of VM Service.”


The next two are sessions are focused on performance. I’m personally always looking for ways to improve performance via configuration and tuning, but also ways to identify performance issues.

Extreme Performance Series: vSphere Advanced Performance Boot Camp [MCL2033]

Pass Type: Tech+ Pass Only
Session by: Mark Achtemichuk & Valentin Bondzio
Description:

“The VMware vSphere Advanced Performance Boot Camp provides the most advanced technical performance-oriented training available about vSphere performance design, tuning and troubleshooting. Hosted by VMware Certified Design Expert Mark Achtemichuk, we will cover a broad range of topics on all resource dimensions, including the VMware ESXi scheduler, memory management, storage and network optimization. The student will become empowered to identify the location of performance issues, diagnose their root cause, and remediate a wide variety of performance conundrums using the many techniques practiced by the most seasoned vSphere veterans and VMware internal experts. Armed with the knowledge provided in the class will allow you to confidently approach virtual performance and manage it successfully.”

Not got the Tech+ Pass? Here is an alternative session available on the General Pass – Extreme Performance Series: Performance Best Practices [MCL1635].

Deep Dive: VM Performance and Best Practices [VI2158]

Pass Type: Tech+ Pass Only
Session by: Jimmy Arias
Description:

“This session will provide a very detailed and technical explanation of the utilization of resources by VM, how to evaluate the performance indicators using ESXtop, and how to better architect and create solutions for performance issues.”


Having watched a couple of sessions this year by David Klee on performance tuning SQL on vSphere, this session was one of the first on my favourite list. Having supported SQL in some fashion my entire career, I am always looking to learn how to get the best performance possible.

Meet the Experts: Virtualizing Microsoft SQL Server on vSphere – Stories from the Trenches [MCL1318]

Pass Type: Tech+ Pass Only
Session by: Deji Akomolafe & David Klee
Description:

“Virtualizing Microsoft SQL Server (the most virtualized mission-critical application) on VMware vSphere has become the standard for SQL Server deployments around the world. As vSphere continues to be the target platform for most SQL Server workloads and, with vSphere now being available in all major public cloud infrastructures, it is a given that you will virtualize your SQL Server workloads. The degree to which you will achieve bare-metal performance is up to how well you align SQL Server with the underlying infrastructure. This session (based on more than two decades of field experience) presents the common pitfalls you need to avoid and those you need to embrace as you run (or plan) your SQL Server instances on premises or in one of the various hybrid cloud options based on vSphere available from AWS, Microsoft, Google, and more.”


Another topic I have really enjoyed getting involved in this year has been Infrastructure as Code (IaC), Packer & Terraform specifically. So this year, I was for sure going to have this session on my list.

Automation Showdown: Imperative vs Declarative [CODE2786]

Pass Type: General and Tech+ Passes
Session by: Luc Dekens & Kyle Ruddy
Description:

“The automation landscape has always been a source of rapid innovation. Historically, the languages, whether it’s Perl, Python, vRealize Orchestrator JavaScript, or PowerCLI, may have changed, but the imperative, step-by-step workflows you’ve learned and know have not. However, a new challenger has appeared. Declarative workflows upended the usual processes and even the languages all in the name of infrastructure as code. Human readable, plain text files can be interpreted by products like HashiCorp Terraform and RedHat Ansible to do the heavy lifting of the imperative process. The key is knowing when, how, and where to use each method within your VMware environment. Join Luc and Kyle for this session where they will discuss these different styles of automation, complete with practical examples that you can use in your own environment!”

If like me you like to see things in action, Kyle also has a Live Coding session – Live Coding: Terraforming Your vSphere Environment [CODE2755].

Both of theses session are available on the General Pass so no excuses to miss out on them!


Finally, a session on Azure VMware Solution (AVS). As I am currently studying for my Microsoft AZ-104 exam, I wanted to start exploring and learning about this offering. Perhaps you are already using Azure or O365 and want to begin looking into the options for extending your vSphere solution to the cloud? If so, this session is definitely worth looking at!

Azure VMware Solution: Deployment Deep Dive [MCL2036]

Pass Type: Tech+ Pass Only
Session by: Jeremiah Megie & Steve Pantol
Description:

“In this session, we will discuss planning and deployment of Azure VMware Solution beyond the quick start. We will cover planning for network addressing, connectivity, integrating into an existing Azure hub and spoke or virtual WAN deployment, configuring monitoring and management, and establishing governance controls.”


If you haven’t already, head over to the VMworld website and register for the event! All content can be found in the Content Catalog, so get browsing!

As always, thanks for reading!

Enabling VM Rightsizing in vRealize Operations Manager (vROPS)

One of the many great features of vRealize Operations Manager (vROPS) is the ability to identify and address over or under sized virtual machines.

I was asked a short while ago why the option to resize a VM was unavailable or ‘greyed out’ as you can see below.

This feature is something that you need to a enable for a connection or ‘Cloud Account’. In this instance, this is my connection with vCenter.

You can check this by heading to Administration, Cloud Accounts and then select the three ‘dots’ next to the connection you want to check, or enable it for.

When reviewing the connection configuration you can see that the enable ‘Operational Actions’ is not selected.

Go ahead and select it.

Now if you head back to the rightsizing section, you will see that you have the option to resize the VM’s (for the connection or Cloud account you have enabled it for). One thing to note, the account you have used for the credentials on this connection require the appropriate privileges to modify the VM’s!

Once you click resize, you can then confirm the suggested resizing and continue.

Hope you found this useful. Once again thank you for reading!

Getting Started With Packer to Create vSphere Templates – Part 5 – Bringing it Together!

Here we are, Part 5! If you have stuck with me through this series, thank you for taking the time. If not, you can catchup with Parts 1-4 by searching my blog!

I wanted to end this series with something different to just text, code and images. So I am going to show you the end to end template deployment process with video’s using user defined variables but with a few environment variables in the Linux example.

Lets start with a Windows example – Windows 2019 Core

To give some context to the files being referenced for this build, here is the folder structure I will be working with, all of which is available on the link above.

From the root directory of your configuration, run the following:

packer build --var-file=./win2019core.pkrvar.hcl .

The trailing ‘.’ is important as this tells Packer that it needs to reference all of the .hcl files in its current directory.

And here is the finished article in the content library!

Now lets look at a Linux example that uses a HTTP server to acquire its kickstart configuration file from, rather than it being loaded as removal media. – Centos 8

This example also makes use of environment and user defined variables!

And again, the finished article.

If you have followed this series throughout, thank you. I hope you have found it useful and its inspired you to begin your Packer journey! Feel free to reach out via my socials if you have any questions or just want to chat about Packer!

Thanks!

Getting Started With Packer to Create vSphere Templates – Part 4 – Blocks

Welcome to Part 4 of the Packer Series! In this part we will look at putting together all the block and files we need to deploy a template!

As we have touched upon in earlier parts, we have multiple blocks and files available to us that can be used to make up a complete configuration. We will walk through a complete Source and Build Block here using user defined variables to complete the build. In the final part of this series, I will use a combination of user and environment variables to give you an idea of how you may use this outside of a lab.

Lets start by breaking down a Source Block for a Windows 2019 Core template.

source "vsphere-iso" "win-2019-std-core" {
  CPUs            = var.CPUs
  RAM             = var.RAM
  RAM_reserve_all = var.ram_reserve_all
  boot_command    = var.boot_command
  boot_order      = var.boot_order
  boot_wait       = var.boot_wait
  cluster         = var.vsphere_compute_cluster
  content_library_destination {
    destroy = var.library_vm_destroy
    library = var.content_library_destination
    name    = var.template_library_Name
    ovf     = var.ovf
  }
  datacenter           = var.vsphere_datacenter
  datastore            = var.vsphere_datastore
  disk_controller_type = var.disk_controller_type
  firmware             = var.firmware
  floppy_files         = var.config_files
  folder               = var.vsphere_folder
  guest_os_type        = var.guest_os_type
  insecure_connection  = var.insecure_connection
  iso_paths = [var.os_iso_path,var.vmtools_iso_path]
  network_adapters {
    network      = var.vsphere_portgroup_name
    network_card = var.network_card
  }
  notes        = var.notes
  password     = var.vsphere_password
  communicator = var.communicator
  winrm_password = var.winrm_password
  winrm_timeout  = var.winrm_timeout
  winrm_username = var.winrm_user
  storage {
    disk_size             = var.disk_size
    disk_thin_provisioned = var.disk_thin_provisioned
  }
  username       = var.vsphere_user
  vcenter_server = var.vsphere_server
  vm_name        = var.vm_name
  vm_version     = var.vm_version
}

All values are passed in via variables in this example. You can see this by the ‘var.<variable_name>’ entry against every configuration line. All variables in this example are user defined variables in a pkrvar.hcl file.

We have configuration for CPU, Memory and disk sizes for instance, then we also have the WinRM username, password and timeout values used for connecting to the operating system after it’s been installed, for use with provisioners.

You can deploy your template as just a ‘normal’ VM Template in the VM and Templates Inventory by using this line:

convert_to_template        = true

Or a using a variable:

convert_to_template             = var.convert_to_template

Or you can deploy to Content Libraries by either removing the “convert_to_template” option or setting it to false, and replacing it with this:

  content_library_destination {

    library = var.content_library_destination
    name    = var.template_library_Name
  }

If you already use Content Libraries, then you are likely going to want to continue to do so.  Or, if you have multiple vCenter’s, you may want to make use of subscribed libraries so you only have to deploy the template once.

To go further you can automatically destroy the original VM after its been uploaded to the Content Library by adding:

destroy = var.library_vm_destroy

And to take it even further, you can add the following to convert the template to an OVF.  OVF’s can be updated in the content library and therefore will be overwritten when you deploy your template again.  This can’t be done with a standard VM template.

ovf     = var.ovf

To bring that all together it looks like this:

  content_library_destination {
    destroy = var.library_vm_destroy
    library = var.content_library_destination
    name    = var.template_library_Name
    ovf     = var.ovf
  }

A key line to point out in this windows example configuration above, is the ‘floppy_files’ option. This option is used to mount a floppy disk with any configuration files or media that you need to reference during the operating system installation. This includes your unattended.xml file, any scripts and any media or drivers such as VMware Paravirtual drivers for the SCSI controller. Checkout Part 2 for more info.

If we were looking at a Linux build, we would see the WinRM options replaced by SSH, like so:

  ssh_password = var.ssh_password
  ssh_timeout  = var.ssh_timeout
  ssh_username = var.ssh_username

A full list of the different configuration options available can be found here.

Now we have defined our source, we now want to deploy it using a build block.

build {
  name    = "win-2019-std-core"
  sources = ["source.vsphere-iso.win-2019-std-core"]

  provisioner "powershell" {
    scripts           = var.script_files
  }
  provisioner "windows-update" {
            search_criteria = "IsInstalled=0"
            filters = [
                      "exclude:$_.Title -like '*Preview*'",
                      "include:$true"
            ]
            update_limit = 25
  }
  post-processor "manifest" {
    output = "output/out-win-2019-std-core.json"
    strip_path = false
  }
}

What’s happening in this block, is that we are referencing the source block that contains our configuration based on the name of the source block that we defined earlier, in this case ‘ source.vsphere-iso.win-2019-std-core’.

In this example we also have two provisioners being used once the operating system has been installed. Firstly, the Windows-Update-Provisioner which installs the latest Windows updates based on any filters you include. In this example, its configured to exclude any updates with ‘Preview’ in the title and also to limit it to install up to 25 updates.

Secondly, we are making use of the Manifest post-processor. This produces an output that includes information such as build time each time it is run.

      "name": "win-2019-std-core",
      "builder_type": "vsphere-iso",
      "build_time": 1617185954,
      "files": null,
      "artifact_id": "windows-2019-std-core",
      "packer_run_uuid": "865be1fd-0dec-1688-8c89-9252e48d0818",
      "custom_data": null
    }
  ],
  "last_run_uuid": "865be1fd-0dec-1688-8c89-9252e48d0818"

All of the above makes up a complete build file that can be deployed with any media or variables you have referenced. The full set of files for this example can be found here.

To give you an example of a non-windows Provisioner, here is a Shell Provisioner for a Linux template:

provisioner "shell" {
    execute_command = "echo '${"var.ssh_password"}' | sudo -S -E bash '{{ .Path }}'"
    scripts         = var.script_files
  }

This executes all scripts that are referenced in the script.files variables.

Now using environment variables, nothing really changes. Your build file will look the same, the only differences will be you won’t provide a value for your declared variable in your pkrvar.hcl file, instead adding the variable to your terminal session. Check out Part 3 for more info. In the final part of this series, I will show an example of using both user defined and environment variables.

That concludes a short run through of the different files in the examples you can find on my GitHub. By no means have I covered everything in those examples or everything you can do with Packer, but this series along with the examples should help you on your way with discovering Packer! There is so much more that can be done using this product to create templates on vSphere as well as multiple other platforms so do head over to http://packer.io to discover more.

In the final part of this series, I am going to try a different content type, video’s! In these, we will run through two end to end template deployments using default values for variables, user defined and environment variables to show how you could use this as part of a workflow.

If you have gotten this far, thanks for sticking with me and I hope you have enjoyed it and found it useful!

Cheers!

Getting Started With Packer to Create vSphere Templates – Part 3 – Variables

Welcome back to part 3 of my Creating vSphere Templates using Packer series, if you missed part 1 or 2, you can find them here and here. In part 3 we will explore variables!

Why would we use variables? Variables allow you to specify customisations to your templating code without having to edit your actual build files. This can be useful when you are reusing code for multiple templates.

There are multiple types of variables that can be used, but we will talk about 2 types of input variables in this blog. They are what I will refer to as; User defined variables and Environment variables. We will talk about both during the blog post and the use cases for each.

Regardless of whether we use a user defined variable or an environment variable, we still need to declare them. This is done in a variable declaration file, so lets start with that!

Variable Declaration

Following the release of Packer version 1.7 the Hashicorp Configuration Language (HCL) format is now the preferred language over JSON. Everything you will see will be in HCL.

The variable declaration file is a pkr.hcl file used to declare any variables you will be using as part of your configuration, be it user defined or environment variables.

Lets take a look at a few of the variable types you can make use of as well as some of the options you can also set.

Variable Type

Here is a few common variable types, you don’t have to define a type at all, but you could then pass the wrong type of data into your config.

  • String – E.g. The templates name or the name of a datastore.
  • Boolean – E.g. A true or false value for whether you are using thin or thick provisioned disks.
  • List – E.g. A list of DNS server IP addresses.

We will see examples of these later on.

Default Value

You can set default values for variables. These values will be used if no other variable value is found in either your pkrvar.hcl file or as an environment variable. Using default values can help reduce the amount of repeat configuration if you use a shared variable definition file.

Description

Another useful option is to be able to provide a description to a variable. This can be useful if you need to add any additional information about the variable or why a particular default has to be set.

Sensitive

You can also mark variables as sensitive for values such as keys, password or usernames etc, however you can mark any variable as sensitive if you have a need to. When a variable is marked as sensitive, it will not be displayed in any of Packers output.

User Defined Variables

Lets take a look at a few examples of declared variables in the variables.pkr.hcl file as well as any values then set for those variables in the user variables file. You will see a couple of examples of variables that have default, type and sensitive options set to give you an idea of some of the use cases.

Lets start with a basic user defined variable:

Variable Declaration – variables.pkr.hclVariable Defination – template.pkrvar.hcl
variable “vsphere_datastore” {}vsphere_datastore = “ds-vsan-01”
variable “vsphere_portgroup_name” {}vsphere_portgroup_name = “dvPG_Demo_DHCP_149”

So in this example, we are declaring that we are going to use variables called ‘vsphere_datastore’ and ‘vsphere_portgroup_name’. We then have values defined for these variables in our pkrvar.hcl file. This can be any data type for the value, as no type has been defined.

Variable Declaration – variables.pkr.hcl Variable Defination – template.pkrvar.hcl
variable “content_library_destination” {
  type    = string
  default = “Images”
}
Nothing defined = Default value would be used
content_library_destination = “ISOs”

In this example we have declared a variable with the type ‘String’, and also provided a default value. The configuration will use this default if no other value is defined either via a user variable or environment variable, but will be overridden should a variable value be set.

Variable Declaration – variables.pkr.hcl Variable Defination – template.pkrvar.hcl
variable “vsphere_server” {
  type    = string
  default = “vm-vcsa-01.smt-lab.local”
  description = “vCenter Server FQDN”
}
Nothing defined = Default value would be used
vsphere_server = “vcsa-02.smt-lab.local”

Here is an example again using a type and default values, but also providing a description to provide some additional information. Like the previous example, not providing a variable value either in the pkrvar.hcl file or in the terminal session as an environment variable, would result in the default value being used.

Variable Declaration – variables.pkr.hcl Variable Defination – template.pkrvar.hcl
variable “vsphere_user” {
  type      = string
  default   = “packer_build@smt-lab.local”
  sensitive = true
Nothing Defined

In this final example we are using the sensitive option. This will stop the value being displayed in any Packer output. Again, it’s using a default value, so you do not need to define a value in the pkrvar.hcl file unless you want to use a different value to this default.

Environmental Variables

Now let’s take a look at environment variables. These are especially useful if you want to use Packer as part of a workflow or automation pipeline, or to pass in secrets (passwords or keys) into the workflow from a secret management tool.

You still declare all your variables in your variables.pkr.hcl file as you would for user defined variables, but instead of providing a value in your pkrvar.hcl file, you create environment variables in your terminal session, in this case, PowerShell.

Packer will look for variables in the session with the prefix of PKR_VAR_. If Packer finds any variables with this prefix, it knows they are for its use.

You do not need to add this prefix anywhere in your configuration as Packer knows to ignore the prefix when matching the variable name.

For example lets set the vSphere connection password in the PowerShell session we are using. This can be done by running the following to set the variable:

$env:PKR_VAR_vsphere_password = "VMware123!"

This example will match up to the variable declaration:

variable "vsphere_password" {}

You do not need to provide a value in your pkrvar.hcl file as Packer will read the value from the ‘PKR_VAR_vsphere_password’ environment variable.

NOTE: If you also provide a user defined variable in pkrvar.hcl, this will take precedence over the environment variable.

You can find HashiCorps documentation on variables here, have a read to discover even more options.

Referencing a Variable from Build Blocks

Now we have taken a brief look at some of the ways to declare and define variables, lets now take a look at how you use them in your source block!

Here are some examples:

  username       = var.vsphere_user
  vcenter_server = var.vsphere_server
  vm_name        = var.vm_name
  vm_version     = var.vm_version

There are two components here. Firstly, ‘var.’ this defines that a variable is being referenced. Secondly, the name of the variable you wish to reference. Each variable referenced will need to exist in variables.pkr.hcl and either a default value specified or a user or environment variable set. It doesn’t matter whether you are using environment or user defined variables, this syntax is the same. Remember that you do not need to include ‘PKR_VAR_’ in the variable name in the source block when you are referencing an environment variable, it’s only needed as a prefix when actually setting the variable.

That concludes my brief overview of user defined and environment variables. Do checkout the link to HashiCorp’s official documentation above and you can also find an example of a variable declaration file here, and a pkrvar.hcl file here on my GitHub.

In Part 4 we will put all the blocks and files together to complete the configuration before moving onto the final part of the series, where we will deploy some templates!

Thanks for reading!

Getting Started With Packer to Create vSphere Templates – Part 2 – Answer Files and Scripts

Welcome to part 2 of my Getting Started with Packer series, if you missed part 1, you can find it here. In part 2, we will take a look through an important part of creating your vSphere templates; Answer Files and scripts.

Firstly, we will be looking at a couple of example scripts that can be used to configure your operating system before its turned into your template. We will then move on to answer files that allow an automated, non user prompting installation of your operating system. These answer files provide configuration details during the operating system installation.

Let’s get started!

Scripts, Drivers and Media

These can be referenced either during the installation of the operating system via the answer file, like VMTools is during the Windows example below. They can also be run by a Provisioner via PowerShell or Shell, after the operating system install has completed. If media is required during the installation of the operating system, such as disk controller drivers or VMware Tools, they need to be made available to the operating system during installation. This can be achieved in multiple ways, Floppy disks, CD_rom, via a HTTP server or a combination. Either way you are going to need them available, more on how to make them available later in the series, but for now lets look at a couple of examples.

Scripts

Disabling TLS (Windows)

Here is an example script for disabling TLS 1.0 &1.1 on Windows using a PowerShell script. This could be run during the installation via the answer file, or via the PowerShell Provisioner. If running during the installation of the OS, this must be mounted as media during the installation. If it’s being run via the Provisioner, this can be referencing directly from the working directory of the machine you are running Packer from.

#Disable TLS 1.0
new-item -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" -Name "TLS 1.0"
new-item -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0" -Name "Server"
new-item -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0" -Name "Client"
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client" -Name "Enabled" -Value 0
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client" -Name "DisabledByDefault" -Value 1
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Name "Enabled" -Value 0
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Name "DisabledByDefault" -Value 1
 
#Disable TLS 1.1
new-item -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" -Name "TLS 1.1"
new-item -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1" -Name "Server"
new-item -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1" -Name "Client"
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client" -Name "Enabled" -Value 0
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client" -Name "DisabledByDefault" -Value 1
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" -Name "Enabled" -Value 0
new-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" -Name "DisabledByDefault" -Value 1

This is a simple script to create the required registry entries to disable TLS versions 1.0 &1.1.

Updating Installed Packages (Linux)

Now let’s look at a CentOS example. Here is an example Shell script for updating all installed packages, that again can be ran via the answer file (kickstart.cfg) or via the Shell Provisioner.

# Update existing packages
sudo yum update -y

Drivers

Depending on the type of disk controller you plan on using for your templates and subsequent virtual machines, you may need to make drivers available during the operating system installation. An example of this are drivers for ParaVirtual SCSI (PVSCSI) disk controllers. These drivers aren’t available in during a Windows installation by default and need to be provided.

These can be mounted via floppy or another method. They just need to be available during the operating system installation. I stick to floppy currently as I don’t have to do anything other than reference the folder containing the drivers, along with my answer file and required scripts:

floppy_files         = var.config_files

This is the floppy_files config line referencing the variable ‘config_files’. That variable references the path and file name of each file I wish to make available to the VM.

Here is detail of that variable as an example. It is referencing files in two directories, config and scripts, within my template parent directory.

config_files            = ["config/autounattend.xml","scripts/pvscsi","scripts/install-vm-tools.cmd","scripts/enable-winrm.ps1"]

If you don’t provide drivers where needed, your operating system installation will fail.

Media

Depending on what you intend to install on your templates, you will need to make any install media or install scripts available. Like above, you can either mount any media to the VM using the floppy_files option and run the installs from the answer file, or via the Provisioner referencing your local working directory.

Examples of media or installations could be security products such as Antivirus or Data Loss Preventions agents, Management/Monitoring agents such as System Center Configuration Manager (SCCM) or System Center Operations Manager (SCOM).

There is no right or wrong answer as to what you should include in your templates, this is something you need to decide based upon your needs and environment. Although I would say, keep them as light as possible and use the right tool for the job. Consider using configuration management tools when its the right time too!

Answer Files

As we touched upon above, answer files are used to provide configuration details during the operating system install. In this blog, we will take a look at two types of answer files; A windows autounattended.xml & a CentOS kickstart.cfg.

Lets begin with the Windows answer file. You can create a Windows answer file using the Windows System Image Manager (Windows SIM) which you can find more information on here.

There are multiple sections within this file from the locale settings, disk partition configurations, the edition of Windows and even a section to stop the administrator account from expiring.

Here is a cut down example of a Windows answer file, you can find a complete example on my GitHub:

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="windowsPE">
        <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <SetupUILanguage>
                <UILanguage>en-US</UILanguage>
            </SetupUILanguage>
            <InputLocale>en-GB</InputLocale>
            <SystemLocale>en-GB</SystemLocale>
            <UILanguage>en-US</UILanguage>
            <UserLocale>en-US</UserLocale>
        </component>
...
        <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <DiskConfiguration>
                <Disk wcm:action="add">
                    <CreatePartitions>
                        <CreatePartition wcm:action="add">
                            <Order>1</Order>
                            <Size>350</Size>
                            <Type>Primary</Type>
                        </CreatePartition>
                        <CreatePartition wcm:action="add">
                            <Order>2</Order>
                            <Size>100</Size>
                            <Type>EFI</Type>
                        </CreatePartition>
...
                </Disk>
                <WillShowUI>OnError</WillShowUI>
            </DiskConfiguration>
...
            <UserData>
                <AcceptEula>true</AcceptEula>
            </UserData>
        </component>
    </settings>
    <settings pass="specialize">
...
        <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <RunSynchronous>
                <RunSynchronousCommand wcm:action="add">
                    <Order>1</Order>
                    <Description>Disable Network Discovery</Description>
                    <Path>cmd.exe /c a:\disable-network-discovery.cmd</Path>
                </RunSynchronousCommand>
            </RunSynchronous>
        </component>
    </settings>
    <settings pass="oobeSystem">
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <AutoLogon>
                <Password>
                    <Value>VgBNAHcAYQByAGUAMQAyADMAIQA=</Value>
                    <PlainText>false</PlainText>
                </Password>
                <LogonCount>2</LogonCount>
                <Username>Administrator</Username>
                <Enabled>true</Enabled>
            </AutoLogon>
                    <FirstLogonCommands>
...
                <SynchronousCommand wcm:action="add">
                    <CommandLine>cmd.exe /c wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE</CommandLine>
                    <Order>10</Order>
                    <Description>Disable password expiration for Administrator user</Description>
                </SynchronousCommand>
                <SynchronousCommand wcm:action="add">
                    <CommandLine>cmd.exe /c a:\install-vm-tools.cmd</CommandLine>
                    <Order>11</Order>
                    <Description>Install VMware Tools</Description>
                </SynchronousCommand>
                <SynchronousCommand wcm:action="add">
                    <CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\enable-winrm.ps1</CommandLine>
                    <Description>Enable WinRM</Description>
                    <Order>99</Order>
                </SynchronousCommand> 
            </FirstLogonCommands>
...
            <UserAccounts>
                <AdministratorPassword>
                    <Value>VgBNAHcAYQByAGUAMQAyADMAIQA=</Value>
                    <PlainText>false</PlainText>
                </AdministratorPassword>
            </UserAccounts>
        </component>
    </settings>
    <cpi:offlineImage cpi:source="wim:c:/wim/install.wim#Windows Server 2019 SERVERSTANDARDCORE" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>

Key parts of this file are the installation of VMTools and the enabling of WinRM:

<SynchronousCommand wcm:action="add">
                    <CommandLine>cmd.exe /c a:\install-vm-tools.cmd</CommandLine>
                    <Order>11</Order>
                    <Description>Install VMware Tools</Description>
                </SynchronousCommand>
<SynchronousCommand wcm:action="add">
                    <CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\enable-winrm.ps1</CommandLine>
                    <Description>Enable WinRM</Description>
                    <Order>99</Order>
                </SynchronousCommand>

VMTools is important to make sure that the correct drivers are installed, ensuring you can get a network connection etc, and you need WinRM and the appropriate firewall rules to be enabled, to allow Packer to continue any post OS install configurations to take place via PowerShell Provisioner block. If WinRM isn’t enabled and working, you wont be able to complete any post install configuration actions!

You will notice both these actions are achieved by running a script from a floppy drive. A:\<scriptname>. As touched upon earlier, these can be made available to the virtual machine as floppy disks (other options available) as it is built and subsequently removed when the build is complete.

One other setting to mention, the Administrator password in encrypted, you don’t want to be leaving this in plain text!

<Password>
                    <Value>VgBNAHcAYQByAGUAMQAyADMAIQA=</Value>
                    <PlainText>flase</PlainText>
                </Password>

Lets now take a look at a Linux Kickstart.cfg file, again cut down but a complete annotated example can be found here:

install
cdrom
lang en_GB
keyboard --vckeymap=uk --xlayouts='gb'
network --onboot yes --bootproto=dhcp --activate
rootpw --iscrypted $1$JlSBrxl.$ksXaF7TIE.70iV12//V4R0
firewall --disabled
authconfig –enableshadow –enablemd5
selinux --permissive
timezone --utc Europe/london --isUtc
bootloader --location=mbr --append="crashkernel=auto rhgb quiet" --password=$1$JlSBrxl.$ksXaF7TIE.70iV12//V4R0
autopart --type=lvm
clearpart --linux --initlabel
firstboot --disabled
eula --agreed
services --enabled=NetworkManager,sshd
user --name=linux_user --iscrypted --password=$1$JlSBrxl.$ksXaF7TIE.70iV12//V4R0 --groups=wheel
%packages --ignoremissing --excludedocs
@core
sudo
net-tools
ntp
ntpdate
vim
wget
curl
git
yum-utils
perl
-aic94xx-firmware
-alsa-*
-btrfs-progs*
-centos-logos
-dracut-config-rescue
-dracut-network
-microcode_ctl
-NetworkManager*
-ivtv-firmware
-iwl*firmware
-plymouth*
%end 
%post
chkconfig ntpd on
chkconfig sshd on
chkconfig ypbind on
chkconfig iptables off
chkconfig ip6tables off
chkconfig yum-updatesd off
chkconfig haldaemon off
chkconfig mcstrans off
chkconfig sysstat off
echo "linux_user        ALL=(ALL)       NOPASSWD: ALL" >> /etc/sudoers.d/linux_user
sed -i "s/^.*requiretty/#Defaults requiretty/" /etc/sudoers
%end
reboot --eject

Although this may look completely different it is still doing similar things as a Windows answerfile.

We are still detailing locale settings and encrypted passwords:

rootpw --iscrypted $1$JlSBrxl.$ksXaF7TIE.70iV12//V4R0

There is also a networking section. In this case I am specifying that I want the operating system to use DHCP:

network --onboot yes --bootproto=dhcp --activate

Also the packages section is quite useful. Here you can specify any packages you want to install during the operating system installation.

%packages --ignoremissing --excludedocs
@core
sudo
net-tools
ntp
ntpdate
vim
wget
curl
git
yum-utils
perl

In Part 3 we will dive into variables in more detail!

Thanks for reading!

« Older Entries