So you bring up a new Windows Server machine, you're done installing and configuring it how you'd like it to look, perhaps deployed it into your test environment and run a few PowerShell scripts against it. You're done, right? Not so fast. Are you sure all of your standard checklist of things have been applied correctly? Validate that Server 2016 configuration with the testing framework, Pester!

Pester is a testing framework built-in PowerShell and comes natively installed with Windows Server 2016 although I recommend getting the latest version by running Install-Module Pester.

Pester's goal is to confirm your expected configuration ensuring your code executes the way you think it should and makes changes to your environment the way you think it should (unit and infrastructure tests).

In this article, we're going to apply the Pester testing framework to Window Server 2016 in particular and show you how it can be leveraged to ensure your Windows Server 2016 configuration set exactly how you intend it to be.

Defining configuration items to validate

Prior to actually creating a test, we need to ensure what to test in the first place so I'll make a list of a couple components I'd like to test after my server is provisioned. Here I'm only testing two items but you can add as many Windows Server 2016 configuration items as you'd prefer.

  • Ensure your VM (or physical server) is running Windows Server 2016
  • Ensure the Containers Windows feature is enabled

Building a Pester test scaffold script

Now that we know what to test, I'll start by creating a single Pester test script called WinSrv16.Tests.ps1 that contains three individual tests in a single describe block called WinSrv2016 Tests.

describe 'WinSrv2016 Tests' {
    it 'is Server Windows Server 2016' {
    
    }
	
    it 'has the Containers Windows feature enabled' {

    }
}

If I run this test now using Invoke-Pester, you can see that Pester does recognize the tests but does not show any results. This is because we don't have any actual test code in our it blocks yet.

Scaffolding Pester tests

Build code to test and to confirm expectations

Let's first add some code to determine the OS. During my investigation, I discovered that I could use the Win32_OperatingSystem WMI class to find this. If the Caption property is equal to Microsoft Windows Server 2016 then it has the OS I'm expecting. I'll add this check in my first test.

describe 'WinSrv2016 Tests' {
    it 'is Server Windows Server 2016' {
    	(Get-CimInstance -Class Win32_OperatingSystem -Property Caption).Caption | should be ' Microsoft Windows Server 2016'
    }
	
    it 'has the Containers Windows feature enabled' {

    }
}

You can now see that test passes.

Server is running Windows Server 2016

Now you need to figure out how to test for the Containers Windows feature as well. Note you could also test server roles like this too. When you run Get-WindowsFeature -Name Containers you'll see that it returns an object with a property of Installed and a value of True. You need to write a test for that condition.

When you run (Get-WindowsFeature -Name Containers).Installed, you'll then get back a boolean True or False value. This value will give you a value to key off of when checking it against the expected output of True.

it 'has the Containers Windows feature enabled' {
    (Get-WindowsFeature -Name Containers).Installed | should be $true
}

Run your tests again and can now see that both tests have passed!

All tests pass

Your end result test script should now look like the below example:

describe 'WinSrv2016 Tests' {
    it 'is Server Windows Server 2016' {
    	(Get-CimInstance -Class Win32_OperatingSystem -Property Caption).Caption | should be ' Microsoft Windows Server 2016'
    }
	
    it 'has the Containers Windows feature enabled' {
        (Get-WindowsFeature -Name Containers).Installed | should be $true
    }
}

Using Context blocks

Another great way to build infrastructure tests is with Pester's context blocks. Context blocks allows you to separate tests within a describe block. For the example you're working with, perhaps you have many different Windows features you need to test along with files that need to be on the server, registry keys that need to be set and so on.

You should build context blocks grouping these tests together to ensure you keep your tests organized.

Your tests could end up looking something like below. Notice also that I introduced a way you can provide many different features names and test them all without having to replicate the it block. Since Pester is PowerShell, you can do a lot with it.

describe 'WinSrv2016 Tests' {

    context 'Windows Features' {
        $features = 'Containers', 'Web-Server'
        foreach ($feature in $features) {
            it "has the $feature Windows feature enabled" {
                (Get-WindowsFeature -Name $feature).Installed | should be $true
            }
        }
    }
    
    context 'Registry settings' {
        it 'has registry key foo in place' {

        }
    }

    context 'Misc settings' {
        it 'is Server Windows Server 2016' {
            (Get-CimInstance -Class Win32_OperatingSystem -Property Caption).Caption | should be ' Microsoft Windows Server 2016'
        }
    }
}

Learn more about Pester

If you'd like to learn more about Pester, be sure to check out my 200+ page book Pester Book to learn the ins and outs of how to use Pester to test all the things!

You can check out a full two chapters from the book for free at my blog post Write PowerShell Tests with Pester: Getting Started.

Summary

These examples are most likely only a small subset of the number of items that you need to change when setting up a Windows Server 2016 configuration. However, it should give you the foundation to think through each component and to build a fully-featured Pester test that will cover a wide range of configuration values for your new server.

Take what you've learned here and add more tests to your Server 2016 configuration or any other server configuration for that matter. The techniques you've learned here will apply across many different operating systems and contexts.

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!