Tag Archives: vSphere

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!

Getting Started With Packer to Create vSphere Templates – Part 1

Virtual Machines Templates, why? Templates are a great way to achieve consistent, repeatable and fast virtual machine (VM) deployments, be it an on-premise vSphere environment or cloud based environment. Having up to date VM templates for each of the operating systems you use, is key to being able to deploy infrastructure quickly and easily across multiple platforms.

In this series of blog posts, I will be focusing on deploying virtual machine templates in vSphere, specifically vSphere 7, using a product called Packer by HashiCorp. Packer is an Infrastructure as Code tool specifically for template management.

There is so much that can be done with Packer. I aim to be able to give you enough information to be able to start your journey with Packer.

Throughout this series, I will reference two templates as examples. A Windows (Windows 2019 Core), and a Linux (CentOS 7) template to give you an idea of the differences, and will also give you a basis that you can apply to other operating systems. But to start we of course need to know how to install Packer and understand the components! Let’s get started!

Installing Packer

Firstly you are going to need to download the Packer executable from the Packer website, here. The latest version at the time of writing is 1.7.0. This is an exciting release for many reasons, but specifically that its moved over to HCL (Hashicorp Configuration Language) from JSON! This brings it closer inline with the other Hashicorp products such as Terraform which already use HCL.

You have a choice of downloads for multiple operating systems. Everything in this blog series will be done on a Windows 10 machine.

Now you need to unzip the download and copy ‘packer.exe’ to either an existing PATH directory or create a new one. For simplicity here, I have copied the executable to ‘C:\Windows\System32’.

Another option for installing on Windows is to use Chocolatey by running the following:

choco install packer

All installation options can be found here.

Once done, you can confirm its working by opening a PowerShell Terminal and running the Packer executable:

packer

The Command Line

Packer has a simple command line to build your templates, you will have seen the available options when you ran ‘packer’ to check your install.

Lets take a look at a few of them that we might see during this series:

  • build: Builds the template you have defined.
  • fmt: This is a nice command that will format your code. Anyone who likes their code to look tidy formatting wise, will like this one!
  • hcl2_upgrade: For anyone that has been using Packer with JSON configuration files, this is a great starting point to get your code converted to HCL. Be aware its not perfect in my experience and will need to be manually tweaked, but it gets you on your way.
  • validate: This checks whether your template is valid. It will check to make sure your syntax is correct and has all mandatory values set for any resources you make use of.
  • version: This is a quick easy way to check which version of Packer you are currently using.

As we saw above, you can find brief descriptions for the remaining options by running ‘packer’ from the command line.

Blocks

There are multiple blocks that can be used to build your virtual machine templates. You can find a complete list here, but lets take a look at some of the ones you will see throughout this series.

Source Blocks

There are two types of source blocks, top level that can be used and reused by multiple builder blocks, and then there are nested source blocks which can be used to inject build specific content.

Build Blocks

Build blocks are used to build your templates, in this case by referencing a source block. It can reference any top level source blocks you have referenced or source blocks nested within your build and merge them to produce a complete configuration.

Provisioner blocks and Post-Processor blocks are also referenced in the build block. More on what they do below…

snippet of a build block referencing a top level source block

Provisioner Blocks

Provisioners are how we interface with your template once the operating system is installed. They will use either SSH or WinRM to communicate with the operating system.

We will be focusing on three provisioners throughout the series; Shell for Linux, PowerShell for Windows, and a community managed provisioner called ‘Windows Update Provisioner’.

Both PowerShell and Shell can be used to run scripts, commands, copy files (you can also use the File Provisioner to do this), install software, basically anything you want. The Windows Update provisioner, is exactly what it sounds like. It’s a way of installing the latest Windows patches. More on that later.

There are multiple HashiCorp supported provisioners available which you can find here.

Post-Processor Blocks

Finally, Post-Processors, these run once the build is complete, but its not mandatory to use them. I haven’t really used them yet apart from producing a manifest file which you will see included later in the series.

Information on the available Post-Processors can be found here.

Folder Structure, Configuration Files and Scripts

There are many ways to set out your configuration files for your templates in which ever directory structure you wish. This is the way I have found logical for me; by separating the configuration out into multiple files (mainly the 3 highlighted in Bold below), it makes it easier to reuse your code.

Folder Structure

--> OperatingSystemName
 -->config
     -->answerfile.filename
  -->output
  -->scripts
     -->drivers
         -->driver.file
         -->driver1.file
     -->configurationscript1.ps1
     -->configurationscript2.sh
     -->install_VM_Tools.cmd
  -->variables.pkr.hcl
  -->build.pkr.hcl
  -->userdefinedvars.pkrvar.hcl

You can have a set of folders per operating system .

Configuration Files, Scripts and Drivers

All Packer configuration files use the file extension; .pkr.hcl apart from your user defined variables file which uses the .pkrvar.hcl extension. Lets take a look at each file.

Variables Declaration file (example – variables.pkr.hcl): This file is where you declare all the variables you want to reference in your source, build or provisioner blocks. This includes user defined variables and environmental variables.

User Defined Variables file (example – win2019.pkrvar.hcl ): This file is where you will define your user variable values. This could include values for Template Name, CPU, RAM and disk size for instance. These variable values are in plain text, therefore you don’t want to be keeping sensitive values such as passwords in this file in any scenario outside of a lab. These can be handled by environment variables which we will see in later parts of the series.

‘Build’ file (example – win2019.pkr.hcl): This is where you define your template using a Source Block that we mentioned earlier, and build it using a Build Block. In this case we are going to be using the ‘vSphere-ISO’ Source.

Operating System Answer File (example autounattended.xml: This is the answer file needed to complete the installation of your operating system. For Windows this would be an autounattend.xml file and for CentOS, a kickstart.cfg file.

Scripts and Drivers: Finally you will need any scripts, drivers or media ready to reference in either the answer files or for use by a provisioner. The output file is not a prerequisite, as this is generated by the post processor at the end of the build.

In a later part of this series I will break down each of the components and blocks, and explain the content of a Windows and Linux template build in further detail.

So what next? In part 2, we will take a closer look at the operating system answer files and some example scripts & drivers that can be used or are required.

Thanks for reading!

Enabling Native KMS in vSphere 7.0 Update 2

Some time back I wrote about setting up and enabling a HyTrust Key Management setup for vSphere to make use of VM and vSAN encryption. Following the release of vSphere 7.0 Update 2, VMware have introduced native key management capabilities! This is a great feature as you no longer require a potentially expensive separate key management solution to make use of vSphere’s encryption offerings.

Lets take a look at this new capability by heading over to the Key Providers menu on your vCenter object, and selecting ‘Add Native Key Provider’:

Give your provider a name:

It then needs backing up! There is an option to do this next to the ‘Add’ option, or in the flow graphic at the bottom:

It is recommended to protect this with a password, make sure you keep this safe along with the key itself, after it downloads when you hit ‘Back Up Key Provider’. You won’t be able to restore the provider without it should you have a need to. Without the provider, any VM’s or data encrypted with it will be lost.

Once its backed up and safely stored you will have an active KMS! You can choose to set it to default if you have more than one key provider if you wish. Any VM’s that are encrypted from the point of changing the default, will be with the new provider, any already encrypted VM’s will continue to be encrypted with the original key.

If you head over to vSAN services, you will now have your native key provider available and can enable Data-At-Rest encryption as well as Data-In-Transit encryption:

Likewise, if you edit the settings of a VM via the VM Options tab you will be able to enable VM encryption:

There you have it, a native Key Management capability, in built with vSphere 7.0 Update 2.

Thanks for reading!

Get, Set and Remove Perennial Reservations Using PowerShell Functions

Having recently had to do some work with RDM perennial reservations I looked into ways to make this less of a manual headache. There are plenty of examples out there for doing this, which I took as a basis to make a PowerShell function. If anything it was a great way to refresh my PowerShell skills and an opportunity to learn some new skills.

Note: Although this has been tested in my environment, please make sure you test it appropriately before running against a production environment!

Lets take a look…

Get-PerennialReservation

This function targets a vSphere cluster, gets all RDM disks that are connected to VM’s and then queries each host in the cluster to check if the disk/storage device is perennially reserved or not.

You can find the code here.

There are multiple ways to use it, whether that is by specifying the target cluster using the -Cluster parameter or by piping it from Get-Cluster. You can also specify a specific canonical name or a comma separated string of them, if you just want the status of a single/select disk(s) using the -CanonicalName parameter. There is also an Export flag to export the results to CSV, if you wish to make use of the data outside of PowerShell. You can get the full usage information by running the following command once you have loaded the function into your PowerShell session:

Get-Help Get-PerennialReservation -full

Here it is in action:

Get-PerennialReservation -Cluster smt-lab-cl-mn-01
Get-Cluster | Get-PerennialReservation
Get-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adcacba077cf38ccc60
Get-PerennialReservation -Cluster smt-lab-cl-mn-01 -ExportPath C:\temp

Set-PerennialReservation

This function again targets a vSphere cluster, gets all RDM disks that are connected to VM’s and sets the IsPerenniallyReserved flag too ‘True’ on all hosts.

You can find the code here.

There are multiple ways to use it like the Get function; specifying the target cluster using the -Cluster paramater or by piping it from Get-Cluster. You can still specify a specific canonical name or a comma separated string of them, if you just want to set the flag of a single/select disk(s) using the -CanonicalName parameter. There is still an Export function that will provide you an output to CSV. You can get the full usage information by running the following command once you have loaded the function into your PowerShell session:

Get-Help Set-PerennialReservation -full

Here it is in action:

Set-PerennialReservation -Cluster smt-lab-cl-mn-01
Get-Cluster | Set-PerennialReservation
Set-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adc87371e49e5b78222
Set-PerennialReservation -Cluster smt-lab-cl-mn-01 -ExportPath C:\Temp\

Remove-PerennialReservation

To complete the set there is a Remove function. This function again targets a vSphere cluster, but this time you need to pass in the canonical name you wish to set the IsPerenniallyReserved flag too ‘False’ for.

You can find the code here.

To use this one, you need to specify the target cluster using the -Cluster paramater and specify a specific canonical name or a comma separated string of them, using the -CanonicalName parameter. There is still an Export function that will provide you an output to CSV. You can get the full usage information by running the following command once you have loaded the function into your PowerShell session:

Get-Help Remove-PerennialReservation -full

Here it is in action:

Remove-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adc87371e49e5b78222
Remove-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adcacba077cf38ccc60, naa.60003ff44dc75adcadc3f2be374bf90a
Remove-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adc87371e49e5b78222, naa.60003ff44dc75adcacba077cf38ccc60, naa.60003ff44dc75adcadc3f2be374bf90a -Exportpath C:\Temp\

I hope this is of use to folks out there. There may be some updates/improvements added in the future so keep an eye on my GitHub for any updates!

I aim to bundle these, and other functions into a module in the near future!

As always, thanks for reading!

Administering ESXi Hosts With ESXCLI using PowerCLI

There are times as a vSphere admin, you are going to want to run ESXCLI commands against multiple ESXi Hosts from a central location. This could be for configuration / administration, reporting, patching or a number of other things.

Recently I have been testing different values in the /DataMover/MaxHWTransferSize advanced setting. To make life easier, I wanted a way to change multiple hosts quickly and easily. To do this, I customised a script that Luc Dekens posted as a solution to a problem someone was having that can be used to send ESXCLI commands to multiple hosts using PowerCLI and plink.exe. This slightly modified version uses a CSV file as a source containing my hosts FQDN and the username and password I will be connecting with.

Plink, which is part of the PuTTy suite, can can be found here.

When using this script, you need to either run the script from a directory containing the plink executable, copy it to where you want to run the script, or adjust the script to include the path to the plink executable… whichever takes your fancy.

Disclaimer: Always complete your own testing in an appropriate environment and refer to the vendors official documentation!

$Hosts = Import-Csv C:\ESXiHosts.csv
$Commad = 'esxcfg-advcfg -s 16384 /DataMover/MaxHWTransferSize'

Foreach ($H in $Hosts) {
    #Starting the SSH Service if not already started
    $SSHService = Get-VMHostService -VMHost $H.HostName | where {$_.Key -eq 'TSM-SSH'}
    if ($SSHService.Running -eq 'True') {
        Write-Host "****************************" -ForegroundColor Blue
        Write-Host "WARNING: SSH already enabled, this will be stopped on completion of this script" -ForegroundColor Yellow
    }
        Else {

            Write-Host "Starting SSH Service on Host $($H.HostName)" -ForegroundColor Green
            Start-VMHostService -HostService $SSHService -Confirm:$false > $null
        }
    #Running the defined ESXCLI Command(s)
    Write-host "Running remote SSH commands on $($H.HostName)." -ForegroundColor Green
    Echo Y | ./plink.exe $H.HostName -pw $H.Password -l $H.UserName $Commad
    
    #Stopping the SSH Service
    $SSHService = Get-VMHostService -VMHost $H.HostName | where {$_.Key -eq 'TSM-SSH'}
    if ($SSHService.Running) {
        Write-Host "Stopping SSH Service on Host $($H.HostName)" -ForegroundColor Green
        Stop-VMHostService -HostService $SSHService -Confirm:$false > $null
        Write-Host "****************************" -ForegroundColor Blue
    }
}
Write-Host "Complete $(Get-Date)" -ForegroundColor Green

You can run as many commands as you need by declaring another ‘Command’ variable at the beginning of the script and adding another line to the ‘Running the defined ESXCLI Command(s)’ section.

When run, it will then cycle through each of the ESXi hosts from your CSV file, enable SSH (if its not already enabled), accept the host key, run the commands you have specified and finally turn the SSH service off.

Here you can see it has set the MaxHWTransferSize to 16384 on each host.

You will see the Recent Task pane show the SSH Service starts and stops.

The commands passed in can be anything you need. All you need to do is change the commands that are defined in the variables section. For example, restarting the management agents –

$commad = 'etc/init.d/hostd restart'
$commad2 = 'etc/init.d/vpxa restart'

I hope this has been of use for anyone needing a centralised, quick way to administer multiple hosts via ESXCLI.

Thanks for reading!

Deploying vCenter 7.0 via the CLI

Recently I decided it was time to add a second vCenter 7.0 Appliance to my main lab environment after the lab containing my SRM and vSphere Replication installation ceased to exist…

I thought I would take the CLI route as its been a while, and thought I’d share!

To begin, you need to decide what you are deploying. There are four deployment options available to you, which you can see listed below. To see the options, mount the vCenter ISO image, browse to vcsa-cli-installer\templates\install, and you will find 4 templates;

  • Embedded on ESXi
  • Embedded on VC
  • Embedded replication on ESXi
  • Embedded replication on VC.

Note there is not a distributed option here anymore as this is depreciated in 7.0.

For my lab I will be using the 3rd option; ‘Embedded replication on ESXi’. Firstly because I’m deploying to a standalone host and not to an existing vCenter. Secondly as I already have an existing VCSA and SSO Domain. This new VCSA will be added, or linked to the existing VCSA for my ‘Recovery’ site, in my Site Recovery Manager (SRM) setup.

If you are looking to deploy your first VCSA, onto a standalone host, you will want to use the ‘Embedded on ESXi’ template.

Once you have decided on the template that suits your scenario, you are going to add some details to this template, such as the ESXi host information you are deploying to, networking information, NTP and in my case SSO details as I will be adding it to an existing SSO Domain. One important value is the deployment size (deployment_option in the example below).

A useful command that can be run to help you decide what size appliance is suitable for your needs is:

vcsa-deploy --supported-deployment-sizes

This outputs the vCenter sizing to assist you. It shows you the resource requirements as well as the amount of hosts and VM’s each can support.

Information 
Options 
tiny 
——supported—deployment—sizes 
about deployment sizes: 
vCPUs Memory CGB) Storage CGB) HostsCup to) VMsCup to) 
tiny—Istorage 
tiny—xlstorage 
small 
small—Istorage 
small—xlstorage 
medium 
medium—Istorage 
medium—xlstorage 
large 
large—Istorage 
large—xlstorage 
xlarge 
xlarge—lstorage 
xlarge—xlstorage 
2 
2 
2 
8 
8 
8 
16 
16 
16 
24 
24 
24 
12 
12 
12 
19 
19 
19 
28 
28 
28 
37 
37 
37 
56 
56 
56 
463 
1538 
3293 
528 
1583 
3343 
748 
1748 
3568 
1113 
1813 
3573 
1853 
1953 
3713 
16 
16 
16 
lee 
lee 
uee 
uee 
leee 
leee 
leee 
2eee 
2eee 
lee 
lee 
lee 
leee 
leee 
leee 
leeee 
leeee 
leeee 
35eee 
35eee

For my lab, ‘tiny’ covers my needs.

Here is the json file I used for the deployment in my lab.  I have excluded the passwords for obvious reason, but it can be ran like this, and will prompt you for the passwords in the terminal.

{
    "__version": "2.13.0",
    "__comments": "Sample template to deploy a vCenter Server Appliance with an embedded Platform Services Controller as a replication partner to another embedded vCenter Server Appliance, on an ESXi host.",
    "new_vcsa": {
        "esxi": {
            "hostname": "smt-lab-esx-04.smt-lab.local",
            "username": "root",
            "password": "",
            "deployment_network": "vSS_PG_Management",
            "datastore": "smt-lab-vmfs-02a"
        },
        "appliance": {
            "__comments": [
                "You must provide the 'deployment_option' key with a value, which will affect the VCSA's configuration parameters, such as the VCSA's number of vCPUs, the memory size, the storage size, and the maximum numbers of ESXi hosts and VMs which can be managed. For a list of acceptable values, run the supported deployment sizes help, i.e. vcsa-deploy --supported-deployment-sizes"
            ],
            "thin_disk_mode": true,
            "deployment_option": "tiny",
            "name": "smt-lab-vcsa-02"
        },
        "network": {
            "ip_family": "ipv4",
            "mode": "static",
            "system_name": "smt-lab-vcsa-02.smt-lab.local",
            "ip": "10.200.15.249",
            "prefix": "24",
            "gateway": "10.200.15.254",
            "dns_servers": [
                "10.200.15.10"
            ]
        },
        "os": {
            "password": "",
            "ntp_servers": "0.uk.pool.ntp.org",
            "ssh_enable": true
        },
        "sso": {
            "password": "",
            "domain_name": "vsphere.local",
            "first_instance": false,
            "replication_partner_hostname": "smt-lab-vcsa-01.smt-lab.local",
            "sso_port": 443
        }
    },
    "ceip": {
        "description": {
            "__comments": [
                "++++VMware Customer Experience Improvement Program (CEIP)++++",
                "VMware's Customer Experience Improvement Program (CEIP) ",
                "provides VMware with information that enables VMware to ",
                "improve its products and services, to fix problems, ",
                "and to advise you on how best to deploy and use our ",
                "products. As part of CEIP, VMware collects technical ",
                "information about your organization's use of VMware ",
                "products and services on a regular basis in association ",
                "with your organization's VMware license key(s). This ",
                "information does not personally identify any individual. ",
                "",
                "Additional information regarding the data collected ",
                "through CEIP and the purposes for which it is used by ",
                "VMware is set forth in the Trust & Assurance Center at ",
                "http://www.vmware.com/trustvmware/ceip.html . If you ",
                "prefer not to participate in VMware's CEIP for this ",
                "product, you should disable CEIP by setting ",
                "'ceip_enabled': false. You may join or leave VMware's ",
                "CEIP for this product at any time. Please confirm your ",
                "acknowledgement by passing in the parameter ",
                "--acknowledge-ceip in the command line.",
                "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
            ]
        },
        "settings": {
            "ceip_enabled": true
        }
    }
}


Once you have prepared your file, there are a couple of commands you can run from a PowerShell prompt to validate your configuration before deploying, saving you some time should mistakes have been made. The first being:

.\vcsa-deploy.exe install --accept-eula --acknowledge-ceip --verify-template-only <Path to json File>

This completes some basic checks to ensure your json file is correct, here is a successful output:

[START] Start executing Task: To validate CLI options at 
Command line arguments verfied . 
[SUCCEEDED] Successfully executed Task 'CLIOptionsVaIidationTask: Executing CLI 
optionsvalidation task' in TaskFIow 'template_validation' at 
[START] Start executing Task: To validate the syntax of the template. at 
Template syntax validation for template 
'M: . json' succeeded . 
Syntax validation for all templates succeeded . 
[SUCCEEDED] Successfully executed Task 'SyntaxVaIidationTask: Executing 
Template Syntax Validation task' in TaskFIow 'template_validation' at 
[START] Start executing Task: To check the version of each template, and for 
each older template that supports CEIP, convert it to the latest template 
format, and save it to the Template Blackboard at 
Template version processing for template 
'M: . json' succeeded . 
Version processing for all templates succeeded . 
[SUCCEEDED] Successfully executed Task 'VersionprocessingTask: Executing 
Template Version processing task' in TaskFIow 'template_validation' at 
[START] Start executing Task: To validate the template structure against the 
rules specified by a corresponding template schema. at 
Template structure validation for template 
'M: . json' succeeded . 
Structure validation for all templates succeeded . 
[SUCCEEDED] Successfully executed Task 'StructureVaIidationTask: Executing 
Template Structure Validation task' in TaskFIow 'template_validation' at 
[START] Start executing Task: To create a dependency graph for the provided 
templates, with an edge pairing two templates that are dependent on each other. 
Such graph relationships will affect whether certain templates can be deployed 
in parallel, or must be deployed sequentially. at 
Dependency processing for all templates succeeded . 
[SUCCEEDED] Successfully executed Task 'DependencyprocessingTask: Executing 
Template Dependency processing task' in TaskFIow 'template_validation' at 
Template verification completed . 
----------------------------------= 12 : l€:eu

Secondly:

.\vcsa-deploy.exe install --accept-eula --acknowledge-ceip --precheck-only <Path to json File>

This will perform a more in depth validation, checking things like the credentials for your SSO domain, DNS or whether the IP or name you plan to use for your VCSA is in use already.

Note: Make sure you have your DNS setup correctly and is resolving the appliance FQDN!

It will also provide warnings if it thinks you might not be using an appropriate template. I originally specified a host what was already managed by vCenter, so it warned me like so:

DRS Warning: The target ESXi host ' smt—lab .10caI' is managed by 
of these hosts are in a cluster, and DRS 
vcenter server '1€.2€€.15.25€' . 
If any 
is enabled, vMotion can take effect and 
adversely impact the installation, 
upgrade, or migration processes. 
It is 
recommended that you use the 
*_on_vc.json template file for the target ESXi host if it is managed by a 
vCenter Server, and ensure the ESXi hosts you have specified are not members of 
clusters with DRS set to Fully Automated during the installation, upgrade, or 
migration processes.

You will get a similar output to the first command, should you pass all the tests. If not you will need to look at resolving them to ensure you get a successful deployment.

The Install!

Once you are confident you have everything in place, including DNS, and your configuration files are correct, you are ready to install:

.\vcsa-deploy.exe install --accept-eula --acknowledge-ceip --no-ssl-certificate-verification <Path to json File>

Here is a cut down version of the output you will see during the deployment:

 ====== [START] Start executing Task: To validate CLI options at 12:46:25 ======
 Command line arguments verfied.
  [SUCCEEDED] Successfully executed Task 'CLIOptionsValidationTask: Executing CLI
 optionsValidation task' in TaskFlow 'template_validation' at 12:46:26
  [START] Start executing Task: To validate the syntax of the template. at
 12:46:27
 Template syntax validation for template
 'M:\Software\VMware\vCenter\embedded_vCSA_replication_on_ESXi.json' succeeded.
 Syntax validation for all templates succeeded. 

 ====== [START] Start executing Task: Perform precheck tasks. at 12:46:39 ======
  [START] Start executing Task: Verify that the provided credentials for the
 target ESXi/VC are valid at 12:46:45
 The certificate of server 'smt-lab-esx-04.smt-lab.local' will not be verified
 because you have provided either the '--no-ssl-certificate-verification' or
 '--no-esx-ssl-verify' command parameter, which disables verification for all
 certificates. Remove this parameter from the command line if you want server
 certificates to be verified. 

 ================== [START] Start executing Task:  at 12:47:47 ==================
 = [SUCCEEDED] Successfully executed Task '' in TaskFlow 'install' at 12:47:47 =
  [START] Start executing Task: Check whether the datastore's free space
 accommodate the VCSA's deployment option at 12:47:51
  [SUCCEEDED] Successfully executed Task 'Running precheck: TargetDsFreespace' in
 TaskFlow 'install' at 12:47:51 

 ==========VCSA Deployment Progress Report==========         Task: Install
 required RPMs for the appliance.(RUNNING 5/100)   - Setting up storage
 VCSA Deployment is still running 

 ==========VCSA Deployment Progress Report==========         Task: Install
 required RPMs for the appliance.(SUCCEEDED 100/100)       - Task has completed
 successfully.         Task: Run firstboot scripts.(SUCCEEDED 100/100) - Task has
 completed successfully.
 Successfully completed VCSA deployment.  VCSA Deployment Start Time:
 2020-12-28T13:19:19.291Z VCSA Deployment End Time: 2020-12-28T14:18:27.103Z
  [SUCCEEDED] Successfully executed Task 'MonitorDeploymentTask: Monitoring
 Deployment' in TaskFlow 'embedded_vCSA_replication_on_ESXi' at 14:18:45
 Monitoring VCSA Deploy task completed
 The certificate of server 'smt-lab-vcsa-02.smt-lab.local' will not be verified
 because you have provided either the '--no-ssl-certificate-verification' or
 '--no-esx-ssl-verify' command parameter, which disables verification for all
 certificates. Remove this parameter from the command line if you want server
 certificates to be verified.
 == [START] Start executing Task: Join active domain if necessary at 14:18:59 ==
 Domain join task not applicable, skipping task
  [SUCCEEDED] Successfully executed Task 'Running deployment: Domain Join' in
 TaskFlow 'embedded_vCSA_replication_on_ESXi' at 14:18:59
  [START] Start executing Task: Provide the login information about new
 appliance. at 14:19:10
     Appliance Name: smt-lab-vcsa-02
     System Name: smt-lab-vcsa-02.smt-lab.local
     System IP: 10.200.15.249
     Log in as: Administrator@vsphere.local
  [SUCCEEDED] Successfully executed Task 'ApplianceLoginSummaryTask: Provide
 appliance login information.' in TaskFlow 'embedded_vCSA_replication_on_ESXi' at
 14:19:10
 =================================== 14:19:16 =================================== 

Once complete, you will now have a second vCenter appliance deployed in Linked mode with the original.  Here it is once I had configured a datacenter and cluster with two hosts.

v @ smt-lab-vcsa-02_smt-lab.locaI 
smt-lab-dc02 
a:] smt-lab-cl-wl-02a 
(Maintenance Mode) 
smt-lab-esx-07_smt-lab_local (Maintenance Mode)

And there you have it, thanks for reading!

Using Tags To Automate The Assigning Of vCenter Object Permissions

Tags are a really useful component in VMware. They can be used for all manor of things, whether it’s for storage policies, backups, identifying a group of objects or in the case of this post, managing permissions.

Having a method of easily assigning permissions to singular or multiple objects in vCenter can be a great benefit to a vSphere Admin as it’s gives them greater control over the environment they manage.

Lets take a look at what is needed to get this setup:

  • Script
  • Tag Category & Tags for each support role.
  • AD Security Groups
  • AD Service Account
  • vCenter Roles (one for the service account, then one for each of the support roles)
  • PowerCLI VICredentials
  • Scheduled Task

In this example I will use 4 common support teams that could be used, DBA, EUC, Operations and Storage. These can be anything you have a requirement for.

Script

Here is the script that applies the permissions based on the assigned tags. It can also be found here on GitHub. Save this on your management server of choice, or wherever you intend to run the scheduled task as a .PS1 file. In this example it’s saved on a management server in C:\Scripts\VI_Permissions.ps1.

#Load PowerCLI Modules
Import-module VMware.PowerCLI

#Get the Credentials
$creds = Get-VICredentialStoreItem -file  C:\Scripts\VM_Tagging_Perms.creds
 
#Connect to vCenter
Connect-VIServer -Server $creds.host -User $creds.User -Password $creds.Password -Force

#Tags
$dbaT = "Support Team/DBA_Team"
$storT = "Support Team/Storage_Team"
$eucT = "Support Team/EUC_Team"
$operT = "Support Team/Operations_Team"

#Active Directory Groups
$dbaG = "smt-lab\dba_admins"
$storG ="smt-lab\storage_admins"
$eucG = "smt-lab\euc_admins"
$OperG = "smt-lab\operations_users"

#Roles
$dbaR = "DBA VM Administrator"
$storR = "Storage VM Administrator"
$eucR = "End User VM Administrator"
$OperR = "Operations Users"


$VMs = Get-VM

ForEach ($VM in $VMs) {

        $TAGS = Get-TagAssignment -Entity $VM | Select @{l='SupportTeam';e={('{0}/{1}' -f $_.tag.category, $_.tag.name)}}, Entity

                                If ($TAGS.SupportTeam -contains $dbaT)  {New-VIPermission -Principal $dbaG -Role $dbaR -Entity $vm.name} Else {Get-VIPermission -Entity $vm.Name -Principal $dbaG | Remove-VIPermission -Confirm:$false}
                                If ($TAGS.SupportTeam -contains $storT) {New-VIPermission -Principal $storG -Role $storR -Entity $vm.Name} Else {Get-VIPermission -Entity $vm.Name -Principal $storG | Remove-VIPermission -Confirm:$false}
                                If ($TAGS.SupportTeam -contains $eucT) {New-VIPermission -Principal $eucG -Role $eucR -Entity $vm.Name}  Else {Get-VIPermission -Entity $vm.Name -Principal $eucG | Remove-VIPermission -Confirm:$false}
                                If ($TAGS.SupportTeam -contains $operT) {New-VIPermission -Principal $OperG -Role $OperR -Entity $vm.Name}  Else {Get-VIPermission -Entity $vm.Name -Principal $OperG | Remove-VIPermission -Confirm:$false}
                        }

Tag Category & Tags

Now onto Tag Categories and Tags in vCenter. Create a Tag category called ‘Support _Teams’ (Or something of your choosing, just make sure you are consistent throughout):

Or using PowerShell – New-TagCategory -Name Support_Teams -Cardinality Multiple -EntityType All

You can select as many object types as you wish and you will also want to allow multiple tags per object.

Now create a tag for each of the support teams in the tag category you just created:

New-Tag -Name Storage_Team -Category "Support_Teams"
New-Tag -Name DBA_Team -Category "Support_Teams"
New-Tag -Name EUC_Team -Category "Support_Teams"
New-Tag -Name Operations_Team -Category "Support_Teams"
Storage_Team 
DBA—Team 
EUC_Team 
Operations _ Team 
Support Team 
Support Team 
Support Team 
Support Team

Create AD groups

Now for some corresponding AD Security Group for each role you wish to have:

Name 
dba admins 
operation 
storage_ad, 
Type 
Security Group 
Security Group... 
Security Group, 
Security Group

Service Account (AD User)

Now to create an AD user account that will be used to apply the permissions within vCenter. This will be the account that will be used to run the scheduled task, connect to vCenter and will have the appropriate permissions to assign permissions for the support roles.

Name 
Type 
tag_permissions User

Support Team Roles

Now we need to create a suitable role for each team.  In this example I have copied the Virtual Machine Power User role, but these roles can contain which ever privilege’s you require.

Under ‘Administration > Roles’ you will see the options to either create a new Role or copy an existing.  From here you will be able to assign it a name and specify the privilege’s you require.

Roles 
Roles provider: 
Admlnlstrator 
Read-only 
No access 
AppdAppllenceUser 
AutoUpdateUser 
VSPHERE.LOCAL v 
DESCRIPTION 
Datastore 
USAGE 
PRIVILEGES 
Content llbrery edmlnlstretor (sample) 
Content Llbrery Reglsry admlnlstrator (sample) 
Datastore consumer (sample) 
DBA VM Admlnlstretor 
End User VM Admlnlstretor 
Network admlnlstretor (sample) 
No cryptography admlnlstrator 
No Trusted Infrastructure admlnlstretor 
NSX Admlnlstretor 
NSX Auditor 
NSX VI Admlnlstretor 
Operatlons Users 
Resource pool admlnlstretor (sample) 
SRM Admlnlstrator 
42 items 
Browse datastore 
Global 
Cancel task 
Scheduled task 
Create tasks 
• Modify task 
• Remove task 
• Run task 
Virtual machine 
Change Configuration 
Acquire disk lease 
Add existing disk 
Add new disk 
Add or remove device 
Advanced configuration 
Change CPU count 
Change Memory 
Change Settings 
Change resource 
o Modify device settings 
Remove disk

You will be referencing these Role names in the script so make sure you continue to match the names thought the process.

Permissioning Role

As mentioned in the service account section, the account (tag_permissions) running the scheduled task will need permissions in vCenter through a role.  The privileges this role will hold, needs to include all the privilege’s that are referenced in all of your Support Team Roles in order for it to have the right to assign the permissions. For example, if all your support roles are a copy of the ‘Virtual Machine power user’ role, your tagging permissions role will need to contain the same privileges.

Depending on how broad the scope of your support team roles, you may want to use the ‘Administrator’ or the ‘No cryptography administrator’ role.  This is entirely up to you and how you manage your estate.

For this example in my lab, I will use the predefined ‘Administrator’ role to grant the ‘tag_permissions’ AD account permissions at the Global Root, ensuring you have selected the ‘Propagate to children’ option.

Change Role 
Domain 
User/Group 
Role 
Global Permission Root 
SMT-LABLOCAL 
tag_permissions 
Administrator 
Propagate to children 
CANCEL

You could create a copy of the ‘Administrator’ role and name it something like ‘VI Permissions Service’ for instance, to give you flexibility to modify it in the future as well as making it easy to identify. With any high privileged account, ensure you secure it appropriately.

Create VI Credential Item

Now to create an encrypted credentials file that the service account running the scheduled task can import and then use to connect to vCenter without any intervention.

The AD account that is used to run the scheduled task, must be the account that also creates the credentials file as this is the only user that can use it.  It will require permissions to run PowerShell and have access to a folder location to store the credentials file on your chosen management server.

To begin, start a PowerShell session in the context of the service account:

Windows Security 
Run as different user 
Please enter credentials to use for 
Domain: smt-lab 
OK 
Cancel

Note: Ensure the server that you are running this scheduled task from has PowerCLI installed.  Installing PowerCLI.

Then run the following, entering your vCenter FQDN and the user and password that you created:

New-VICredentialStoreItem -host "smt-lab-vcsa-01.smt-lab.local" -user "smt-lab\tag_permissions" -password "VMware123!" -file C:\Scripts\VM_Tagging_Perms.creds
PS C: 
_Perms . creds 
New—VIC redenti al Storeltem 
C: \ Scri pts ng 
-host 
smt—lab. local" -user 
"smt—l ssion 
s" -password 
"VMware123! " 
Hos t 
User 
smt—1ab—vcsa—01 s 
smt—l . 
C: \ Scri ng_Perms . creds

Ensure you are storing the file somewhere with appropriate access to allow this but, also to restrict any unnecessary access.  The credentials file can have the password read if the user account that created it is compromised and gains access to the file using those windows credentials.

Scheduled Task

Now for the last component, the scheduled task. On a management server or a server of your choosing, create a scheduled task:

Vl_permissions Properties (Local Computer) 
General Triggers Actions Conditions Settings 
History 
Location: 
Author: 
VI Permissions 
SMT- LA8\Administrator 
Description: 
Security options 
When running the task, use the following user account: 
SMT- LA rmissio ns 
C) Run only when user is logged on 
@ Run whether user is logged on or not 
Change User or Group... 
[3 Do not store password. The task will only have access to local computer resources. 
Run with highest privileges 
Hidden 
Configure for: 
Windows Vista" , Windows Server" 2008 
Cancel

Assign an appropriate schedule that suits the level of change and size of your environment:

Vl_permissions Properties (Local Computer) 
General Triggers Actions Conditions Settings 
History 
Location: 
Author: 
VI Permissions 
SMT- LA8\Administrator 
Description: 
Security options 
When running the task, use the following user account: 
SMT- LA rmissio ns 
C) Run only when user is logged on 
@ Run whether user is logged on or not 
Change User or Group... 
[3 Do not store password. The task will only have access to local computer resources. 
Run with highest privileges 
Hidden 
Configure for: 
Windows Vista" , Windows Server" 2008 
Cancel

Now configure the trigger to execute the script:

(9 
Create Task 
General Triggers Actions Conditions Settings 
When you create a task, you must specify the action that will occur when your task starts. 
Action 
Start a program 
New... 
Details 
powershell -File 
Edit... 
Delete

Now thats everything you need to set this up, so lets give it a run though!

Assigning Tags and Permissions

Lets take a look at my demo VM permissions before we begin assigning permissions:

Lets check the VM permissions before having any tags assigned:

Get-TagAssignment -Entity $VM
$VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "*smt-lab*"} | Select Principal, Role}

Note that the tag_permissions account has been propagated from the root permissions you set earlier.

—TagAssignment 
-Entity 
ps Get 
ForEach—Object {Get—VIPermission 
principal 
Role 
SMT-LAB\tag_ 
permissions Admin 
—Entity 
Where 
principal 
—like 
Select principal, 
Role} 
SMT-LAB\Stephan 
Admin

Now assign a tag or two from the ones you created earlier using ‘New-TagAssignment’:

New-TagAssignment -Tag DBA_Team -Entity $VM
New-TagAssignment -Tag Operations_Team -Entity $VM
DBA_Team 
Tag 
Support 
Tag 
Support 
New—TagAssignment —Tag 
Team/DBA_Team 
New—TagAssignment —Tag 
Team/Operations_Team 
-Entity $VM 
Entity 
tfdemol 
Operations_Team —Entity $VM 
Entity 
tfdemol

Now you can either manually run the scheduled task or wait until its next scheduled run time.  Once the job has run, you can now check the tags match the permissions assigned by running the following:

Get-TagAssignment -Entity $VM
$VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "smt-lab"} | Select Principal, Role}
—Entity 
Tag 
Support 
Support 
Get—TagAssignment 
Team/DBA_Team 
Team/Operations_Team 
ForEach—Object {Get—VIPermission 
Entity 
tfdemol 
tfdemol 
—Entity 
Where 
principal 
—like 
Select principal, 
Role} 
principal 
SMT 
SMT 
SMT 
SMT 
—LAB\dba_admins 
—LAB\operations_users 
—LAB\tag_permissions 
—LAB\Stephan 
Role 
DBA VM Administrator 
Operations Users 
Admin 
Admin

You will see that the two tags assigned align with the the two AD groups being granted the corresponding role.

Now let do this for multiple VM’s:

Here I have multiple VM’s in the variable ‘$VM’ and I am assigning two tags to each of them.

$VM | ForEach-Object {New-TagAssignment -Tag DBA_Team -Entity $_}
$VM | ForEach-Object {New-TagAssignment -Tag Storage_Team -Entity $_}
DBA_Team 
Tag 
Support 
Support 
Support 
Support 
Support 
Tag 
Support 
Support 
Support 
Support 
Support 
—Object 
ForEach 
Team/DBA_Team 
Team/DBA_Team 
Team/DBA_Team 
Team/DBA_Team 
Team/DBA_Team 
—Object 
ForEach 
Team/Storage_Team 
Team/Storage_Team 
Team/Storage_Team 
Team/Storage_Team 
Team/Storage_Team 
{New—TagAssignment —Tag 
Entity 
Demopho€€6 
Demophoe€5 
Demopho€€2 
Demopho€€u 
Demophoe€3 
—Entity $_} 
{New—TagAssignment —Tag 
Storage_Team 
Entity 
Demopho€€6 
Demopho€€5 
Demopho€€2 
Demopho€€u 
Demopho€€3 
—Entity $_}

You can now see the tags assigned:

$VM | ForEach-Object {Get-TagAssignment -Entity $_}
{Get—TagAssignment 
Entity 
Tag 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
ForEach—Object 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
—Entity $_} 
Demophoe€6 
Demopho€€6 
Demopho€€5 
Demophoe€5 
Demopho€€2 
Demopho€€2 
Demophoeeu 
Demopho€€u 
Demophoe€3 
Demophoe€3

Following the script / job being run:

 $VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "*smt-lab*"} | Select Principal, Role} 
ForEach 
principal 
—Object {Get—VIPermission 
Role 
DBA VM Administrator 
—Entity 
Where 
principal 
—like 
Select principal, 
Role} 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin

As in the singular example, you will see that the two tags assigned, align with the the two AD groups being granted the corresponding role.

Removing Tags and Permissions

Now lets look at removing permissions, in this case, the Operations Team permissions from a VM:

Get-TagAssignment -Entity $VM | Where {$_.Tag -like "*Operations*"} | Remove-TagAssignment -Confirm:$false

Leaving it with just the ‘DBA_Team’ Tag assigned:

Once the script has run:

$VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "*smt-lab*"} | Select Principal, Role}

Reviewing Permissions

Finally, if you want to know which objects are supported by a specific team and have access you can check this by running:

Get-TagAssignment | Where {$_.Tag -like "Support_Teams/DBA_Team"}

You now have a way of assigning and removing permissions from vCenter objects using Tags. In this example I have used virtual machine object, but depending on your requirements, and the scope you set on the tag category, you could use this for other vCenter objects.

Thanks for reading!

« Older Entries