Securing DSC resources for VMware

28/08/2019

Recently DSC Resources for VMware 2.0 was released. This new version comes with a lot of new resources and other features, like availability in the PowerShell Gallery. If DSC Resources for VMware is completely new,
I recommended reading the “Getting started” blog post, but do not follow the installation instructions. Instead install directly from the PowerShell Gallery, use something like this:

PS> Find-Module *VMware.vSphereDSC* | Install-Module

So after exploring “Vester”, the other DSC solution, it is now time to have a look at the DSC Resources for VMware 2.0.

Disclaimer: Windows PowerShell Desired State Configuration (from now on “DSC”) is often used for configuration management of Windows systems and as such is new to me. This post focuses on the use of DSC in a vSphere environment.

My setup;  I used an old Windows Server 2012R2 as a LCM. The vSphere environment is a VCSA version 6.5 and two ESXi hosts.
This post contains links to some script. All files mentioned in this post can be downloaded from this location. Then on the LCM, create a new folder named C:\VMwareDSC and place all the files in this folder.

One of my first goals was to understand how to create a good configuration. Luckily, the VMware DSC module contains an example folder, and I selected the VMHost_Config.ps1 configuration, an sample script for configuring an ESXi host.

File VMHost_Config1.ps1 is the first attempt to configure some services on both ESXi hosts. Besides the Configuration section starting on line 22, there is also a Configuration data section, starting on line 1, that needs to be populated.

$script:configurationData = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword = $true
            VMHosts = @(
                @{
                    Server = 'esx01.virtual.local'
                    User = 'root'
                    Password = 'VMware1!'
                },
                @{
                    Server = 'esx02.virtual.local'
                    User = 'root'
                    Password = 'VMware1!'
                }
            )
        }
    )
}

Configuration VMHostSettings_Config1 {
    Import-DscResource -ModuleName VMware.vSphereDSC

    Node $AllNodes.NodeName {
        foreach ($vmHost in $AllNodes.VMHosts) {
            $Server = $vmHost.Server
            $User = $vmHost.User
            $Password = $vmHost.Password | ConvertTo-SecureString -asPlainText -Force
            $Credential = New-Object System.Management.Automation.PSCredential($User, $Password)

            VMHostNtpSettings "VMHostNtpSettings_$($Server)" {
                Name = $Server
                Server = $Server
                Credential = $Credential
                NtpServer = @('0.bg.pool.ntp.org', '1.bg.pool.ntp.org', '2.bg.pool.ntp.org')
                NtpServicePolicy = 'automatic'
            }

Code fragment VMHost_Config1.ps1, for the complete script use this link.

In this example, credentials must be provided for each ESXi host. If you want to see this code in action, the following lines will create the .mof file, apply the configuration on the two ESXi hosts and will finally retrieve and test the configuration.

cd C:\VMwareDSC

# Create MOF file
VMHostSettings_Config1 -ConfigurationData $script:configurationData

# Start configuration
$paramHash = @{
    ComputerName = "localhost"
    Path = "C:\VMwareDSC\VMHostSettings_Config1"
    Wait = $true
    Verbose = $True
    Force = $True
}

Start-DscConfiguration  @paramHash

# View configuration 
Get-DscConfiguration -CimSession localhost

# Validate Configuration
$cs = New-CimSession -ComputerName localhost
Test-DscConfiguration -CimSession $cs 

Code fragment VMHost_Config1.ps1, for the complete script use this link.

As an alternative, the vCenter Server credentials can also be used.

All ESXi host resources, like “VMHostNtpSettings” have properties, which can be requested with the Get-DscResource cmdlet. Besides the Property Server, there is also Name.

PS> Get-DscResource VMHostNtpSettings | 
    Select-Object -ExpandProperty Properties | ft -AutoSize

 Name                 PropertyType   IsMandatory Values                     
 ----                 ------------   ----------- ------                     
 Credential           [PSCredential]        True {}                         
 Name                 [string]              True {}                         
 Server               [string]              True {}                         
 DependsOn            [string[]]           False {}                         
 NtpServer            [string[]]           False {}                         
 NtpServicePolicy     [string]             False {Automatic, Off, On, Unset}
 PsDscRunAsCredential [PSCredential]       False {}     

Server is the vCenter Server used for authentication and Name represents the ESXi hosts.
After some adjustments to the Configuration and the Configuration data, the result is VMHost_Config3.ps1.

$script:configurationData = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword = $true
            PSDscAllowDomainUser = $true

            Server = 'vc06.virtual.local'
            User = 'administrator@vsphere.local'
            Password = 'VMware1!'

            VMHosts = @(
                @{
                    Name = 'esx01.virtual.local'
                },
                @{
                    Name = 'esx02.virtual.local'
                }
            )
        }
    )
}

Configuration VMHostSettings_Config3 {
    Import-DscResource -ModuleName VMware.vSphereDSC

    Node $AllNodes.NodeName {
        $Server = $AllNodes.Server

        $User = $AllNodes.User
        $Password = $AllNodes.Password | ConvertTo-SecureString -asPlainText -Force
        $Credential = New-Object System.Management.Automation.PSCredential($User, $Password)

        foreach ($vmHost in $AllNodes.VMHosts) {
            $Name = $vmHost.Name
            VMHostNtpSettings "VMHostNtpSettings_$($Name)" {
                Name = $Name
                Server = $Server
                Credential = $Credential
                NtpServer = @('0.bg.pool.ntp.org', '1.bg.pool.ntp.org', '2.bg.pool.ntp.org')
                NtpServicePolicy = 'automatic'
            }


Code fragment VMHost_Config3.ps1, for the complete script use this link.

However, when you take a close look at these configuration scripts and the compiled configurations (the localhost.mof files in the folders VMHost_Config1 and VMHost_Config3), you will notice that these files contain passwords in clear text!

 
/* 
@TargetNode='localhost' 
@GeneratedBy=Administrator 
@GenerationDate=08/21/2019 20:56:38 
@GenerationHost=WINJUMP1 
*/ 

instance of MSFT_Credential as $MSFT_Credential1ref 
{ 
Password = "VMware1!"; 
UserName = "administrator@vsphere.local"; 
}; 

instance of VMHostNtpSettings as $VMHostNtpSettings1ref 

Fragment localhost.mof with clear text passwords

A commonly used, more secure method is to encrypt the passwords in the compiled configuration files using a certificate. This can be achieved in three steps:

1. Generate a self-signed certificate

2. Configure the LCM with certifcates

3. Edit the configuration script

You can generate a self-signd certificate with PowerShell. Windows 10 and Windows Server 2016 have the New-SelfSignedCertificate cmdlet.
You can also use the Self-signed certificate generator, in case of older Windows versions. After downloading, put the script in the folder C:\VMwareDSC.

The certificate needs to be of type “Document Encryption”.
The following lines will generate and export the certificate.


#load function

. "C:\VMwareDSC\New-SelfSignedCertificateEx.ps1"

# generate Cert
New-SelfSignedCertificateEx -Subject 'CN=Cert' -EnhancedKeyUsage 'Document Encryption' -StoreLocation LocalMachine -FriendlyName 'SelfSigned'

# Check Cert
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq 'SelfSigned'}
$cert

# Cert can be exported
Export-Certificate -Cert $cert -FilePath C:\VMwareDSC\Cert.cer -Force

The second step is to configure the LCM to work with certificates.
This can be done using the function DSCLocalConfigurationmanger.
Just like a DSC configuration, it has a set of parameters.
The CertificateID is the thumbprint of the certificate which have been generated.
Line 32 creates the config file, which is applied on line 33.
The final line retrieves the new configuration.

[DSCLocalConfigurationManager()]
Configuration LCM_SelfSigned
{
    param
        (
            [Parameter(Mandatory=$true)]
            [string[]]$ComputerName,

            [Parameter(Mandatory=$true)]
            [string]$guid,

            [Parameter(Mandatory=$true)]
            [string]$thumbprint

        )
	Node $ComputerName {
		Settings {
		AllowModuleOverwrite = $True
		ConfigurationMode = 'ApplyAndMonitor'
		RefreshMode = 'Push'
		ConfigurationID = $guid
		CertificateID = $thumbprint
		}
	}
}

$ComputerName = 'localhost'
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq 'SelfSigned'}
$cim = New-CimSession -ComputerName $ComputerName
$guid=[guid]::NewGuid()

LCM_SelfSigned -ComputerName $ComputerName -Guid $guid -Thumbprint $Cert.Thumbprint -OutputPath C:\VMwareDSC\S2
Set-DscLocalConfigurationManager -CimSession $cim -Path C:\VMwareDSC\S2 -Verbose
Get-DscLocalConfigurationManager -CimSession $cim

The final step is to edit the Configuration script for certificate usage, see VMHost_Config_Secure.ps1.

$script:configurationData = @{
    AllNodes = @(
        @{
            PSDscAllowDomainUser = $true
            NodeName = 'localhost'
            CertificateFile = 'C:\VMwareDSC\Cert.cer'
            Server = 'vc06.virtual.local'
            Credential = (Get-Credential -UserName 'administrator@vsphere.local' -Message 'Enter Password')

            VMHosts = @(
                @{
                    Name = 'esx01.virtual.local'
                },
                @{
                    Name = 'esx02.virtual.local'
                }
            )
        }
    )
}

Configuration VMHostSettings_Config_Secure {
    Import-DscResource -ModuleName VMware.vSphereDSC

    Node $AllNodes.NodeName {
        $Server = $AllNodes.Server
        $Credential = $AllNodes.Credential

        foreach ($vmHost in $AllNodes.VMHosts) {
            $Name = $vmHost.Name
            VMHostNtpSettings "VMHostNtpSettings_$($Name)" {
                Name = $Name
                Server = $Server
                Credential = $Credential
                NtpServer = @('0.bg.pool.ntp.org', '1.bg.pool.ntp.org', '2.bg.pool.ntp.org')
                NtpServicePolicy = 'automatic'
            }

Code fragment VMHost_Config_Secure

The Configuration Data now has a reference to the certificate, the password will be provided while running the configuration.
As with the other provided scripts, it will apply and test the configration.
The resulting .mof file now shows the password in an encrypted form instead of plain text.

@GeneratedBy=Administrator
@GenerationDate=08/21/2019 20:59:47
@GenerationHost=WINJUMP1
*/

instance of MSFT_Credential as $MSFT_Credential1ref
{
Password = "-----BEGIN CMS-----\nMIIBlwYJKoZIhvcNAQcDoIIBiDCCAYQCAQAxggE/
...
qX0sp0DA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCRMnG9mU356LTd8mNAoBHagBAwv5TR\n4zJ/b4E2knfRnPA6\n-----END CMS-----";
 UserName = "administrator@vsphere.local";

Fragment localhost.mof with encrypted passwords

In later posts, I will dive deeper into the DSC Resources for VMware. As allways, I thank you for reading.

Sources:

New Release – DSC Resources for VMware 2.0

Getting Started with Desired State Configuration Resources for VMware

Pluralsight, Practical Desired State Configuration (DSC) by Josh Duffney.

Credential encryption and security in MOF files

This is the sixth part of a series about configuration drift, Vester and DSC.
Overview of all posts in this series:

About Configuration Drift, Pester and Vester

Tips for writing Vester test files, part 1

Tips for writing Vester test files, part 2

Creating Dashboards for Vester

Vester Test file generator

 


Vester Test file generator

07/07/2019

In previous posts (see below), I presented some tips for creating new Vester Test files. As you may know, ESXi hosts have a large number of so called “Advanced System Settings” Some of these settings are already present as Vester test files. These Advanced System Settings can be handled with the Get-AdvancedSetting and Set-AdvancedSetting cmdlets. With this knowledge and some PowerShell code, it is not to difficult to create a complete set (>1.100) of Vester Test files.

The New-VesterHostAdvanced.ps1 script can be found here.

A brief description how it works. After connecting to a vCenter Server, one of the available ESXi hosts needs to be selected. The selected host will be used to create an overview of all available Advanced System Settings.

Key in creating the scripts is the concept of Here documents, in PowerShell known as Here-String. See for a brief overview. Key in Here-Strings is the usage of single or double quotes with variables. A Here-String with double quotes allows the usage of variables. Run the following code to see the difference.

$var = 'MyValue'
$formatText1 = @"
Here-String with double quotes
The variable $var
Variable replacement

"@
$formatText1

$formatText2 = @'
Here-String with single quotes
The variable $var
Test as-is
'@
$formatText2

Read the rest of this entry »


PsConf.eu 2019

10/06/2019

Some time ago, I was invited to visit the PowerShell Conference Europe, in short PSCONF.EU 2019. This conference took place between 4 and 7 June 2019 in the Hannover Congress Center in Germany. To get started a few numbers of Europe’s largest PowerShell event which is held annually since 2016; 350 delegates, 40 speakers and 1 dog from almost all European countries and the United States of America will present and attend over 75 presentations during these four days.

Fig.1 – Opening Ceremony

Read the rest of this entry »


VMware Learning Zone

05/05/2019

Introduction

On January 17th, I completed my VMware recertification. Just a few days later, VMware announced it’s new recertification policy, leaving out the mandatory two years recertification requirement. On March 1st, I received the following message from VMware; “As it’s been communicated with our recent changes to the VMware recertification policies, we have identified you as completing your Certification requirements by completing the Expired Recertification Path within the last six months. As a token of appreciation for the extra time and effort it involved, we are providing you a free one-year premium license to the VMware Learning Zone.”

For some reason that also reminded me of the past, after successfully passing a VMware VCP exam, you received an envelope with the certificate and a one-year license for VMware Workstation by mail.

Fig. 2

So time to redeem my free one-year premium license and share my first experiences.

Read the rest of this entry »


Creating Dashboards for Vester

03/04/2019

Introduction

In my first post about Vester, I ended the post with a number of items that needs further investigation. On top of my list is some kind of reporting function. After submitting an Invoke-Vester command lots of information scrolls over the screen.

Figure 1. – Output Invoke-Vester

Most administrators will not agree with an unseen remediation of the errors found and desire some kind of overview. It would also be nice to have some kind of overview while running Invoke-Vester as a scheduled job. Fortunately, one of my colleagues (Thank you Alex!) gave me the idea to create a dashboard. As there are many monitoring and dashboards product available like Grafana and Graphite there is also the PowerShell Universal Dashboard module. The PowerShell Universal Dashboard comes in a licensed Enterprise Edition and a free Community edition, documentation can be found here.

Installation is done by installing the module:


Install-Module UniversalDashboard.Community -AcceptLicense

To test UD, run the following code


$MyDashboard = New-UDDashboard -Title "Hello, World" -Content {

New-UDCard -Title "Hello, my first universal dashboard!"

}

Start-UDDashboard -Port 10000 -Dashboard $MyDashboard -Name 'HelloDashboard'

Start a browser and enter URL: http://localhost:10000, this should show this:

Figure 2.

For a nice introduction in Universal Dashboard, please read this post by Nicolas Prigent.

Read the rest of this entry »


Tips for writing Vester test files, part 2

20/02/2019

This post is the second part in a series about writing effective Vester test files. The previous part can be found here.

When there is no easy Get and Set

An example, we want to create a test to check the Cluster DPM settings. The Get-Cluster cmdlet can show many properties, however the options of the corresponding Set-Cluster cmdlet are limited. You can see for yourself running the following command:


PS> help Set-Cluster -Parameter *

Commands like Get-Cluster, Get-VMHost, Get-Datacenter are practical, easy to use but have some limitations, like not showing all info and are not blazing fast.

Time to meet the Get-View cmdlet, a bit less user-friendly, but much quicker and very useful. The equivalent for the Get-Cluster cmdlet is:


PS> Get-View -ViewType ClusterComputeResource

To select a specific Cluster, use the -Filter parameter, like:


PS> Get-View -ViewType ClusterComputeResource -Filter @{"NAME"="Cluster01"}

Another way is:


PS> Get-Cluster -Name Cluster01 | Get-View

Time to create the first DPM test. To test if DPM is enabled, execute the following commands:


PS> $Cluster = Get-Cluster -Name Cluster01 | Get-View

And run this:


PS> $Cluster

You can see all properties, note there is “Configuration” and “ConfigurationEx”. Run both:


PS> $Cluster.Configuration
PS> $Cluster.ConfigurationEx

And note the difference, $Cluster.ConfigurationEx has a “DpmConfigInfo” section. The following line will show the current DPM configuration for Cluster “Cluster01”


PS> $Cluster.ConfigurationEx.DpmConfigInfo.Enabled

Enabled DefaultDpmBehavior HostPowerActionRate Option
------- ------------------ ------------------- ------
True automated 4

We can now write the first part for the DPM enabled test.


$Title = 'DRS Power Management enabled'
$Description = 'Enable Power Management DPM'
$Desired = $cfg.cluster.drsDpmEnable
$Type = 'bool'

# The command(s) to pull the actual value for comparison
# $Object will scope to the folder this test is in (Cluster, Host, etc.)
[ScriptBlock]$Actual = {
($Object | Get-View).Configurationex.DpmConfigInfo.Enabled
}

# The command(s) to match the environment to the config
# Use $Object to help filter, and $Desired to set the correct value
[ScriptBlock]$Fix = {
????
}

Read the rest of this entry »


Tips for writing Vester test files, part 1

10/02/2019

Over the last couple of weeks, I took a look at the Desired State Configuration Resources for VMware (later more on that…).

But above all, I spent quite some time exploring Vester. Vester can be really useful, and it is relatively easy to create additional test files and get more configuration settings under Vester control. While working on new test files, I gathered some lessons learned that can be useful for others.

Naming Test file and the components

Choose a descriptive name for a new test file. Although test files are organized in folders, when the number of test files is increasing descriptive names can be helpful.
What makes a good name? Refer to something that is known and unique.
E.g. For vCenter Clusters, most settings are related to DRS or HA settings, the output of the following command can be helpful:

> Get-Cluster -Name Cluster01 | select *
VsanEnabled               : False
VsanDiskClaimMode         : Manual
HATotalSlots              : 
HAUsedSlots               : 
HAAvailableSlots          : 
HASlotCpuMHz              : 
HASlotMemoryMb            : 
HASlotMemoryGB            : 
HASlotNumVCpus            : 
ParentId                  : Folder-group-h23
ParentFolder              : host
HAEnabled                 : True
HAAdmissionControlEnabled : True
HAFailoverLevel           : 1
HARestartPriority         : Low
HAIsolationResponse       : PowerOff

E.g. Creating a test file for the HA Failover Level, name the test file: “HA-FailoverLevel.Vester.ps1”.
While working on a test file the following variables also play an important role.
The variable $Title is shown during each run of Invoke-Vester and can be used to provide more information then the title of the test file.

Fig. 1

Read the rest of this entry »