PowerShell How-To

Building Infrastructure Tests with Pester, the PowerShell Testing Framework

Pester infrastructure tests can run as standalone tests or be integrated into a broader automation workflow. But first, what's an "infrastructure test" anyway?

Pester was built for unit testing. Unit testing, in a nutshell, refers to testing code, not the changes that code may do to an environment. However, Pester can be adopted to accomplish other testing scenarios such as testing IT infrastructure.

If you don't already have Pester installed, you can download it for free by running Install-Module Pester in your PowerShell console.

Sysadmins want to ensure their infrastructure environment is as expected. They need to ensure it meets individual specifications, and that all configuration items are set a specific way. Using Pester, they can build infrastructure tests to answer questions like:

  • Did that script change the right thing?
  • Is the server configured the same way today as it was yesterday?
  • What's the difference between a configuration last week versus today?

Pester infrastructure tests can run as standalone tests or be integrated into a broader automation workflow. But first, what is an "infrastructure test" anyway?

Infrastructure Tests
An infrastructure test is a generic term for any code that you write to test a current state against an expected state. Think:

  • Does that server have the right software installed on it?
  • When I run this PowerShell script, does it create the correct user account?
  • Does that file server have the expected folder hierarchy?

Infrastructure tests in Pester are PowerShell code that's executed by the Pester PowerShell module and built in a specific way, known as a domain-specific language (DSL). This DSL describes the desired state and has the code necessary to check that state and to compare the result.

Pester Tests
A Pester test can be broken down into a hierarchy of components such as a describe block, a context block and an it block. We won't cover how to write Pester tests in this article, but if you'd like to learn more, I encourage you to check out The Pester Book, written by me, which goes in-depth on Pester and how to build tests.

Before we can begin writing a test, we first need to figure out what it is we're going to test in the first place. Pester can test for anything PowerShell can read. To use an example, let's say you have a Windows service that you expect to be running on all of your servers. If the service is stopped, that's a problem, and you'd like to know about it.

The first task is building the code to test for this scenario. For this example, a code snippet like the one below would work fine. This code snippet queries a remote server's service and returns its status. The status is probably going to either be Running or Stopped.

 (Get-Service -ComputerName  -Name ).Status

Once we have the code to perform the tests, we need to start scaffolding out a Pester test to run the code and then compare the result against the expected outcome.

Let's say I want to ensure the wuauserv service is running on a server of mine. One way to build that test would be to define a describe block with the name of the service and the computer it's being tested on. Always be sure to set all labels descriptively -- otherwise, you'll find yourself sifting through hundreds of testing without being able to find the one you're looking for!

## ServiceTests.Tests.ps1
describe 'The wuauserv service on SRV1' {

    $serverName = 'SRV1'

    $status = (Get-Service -ComputerName $serverName -Name 'wuauserv').Status

    it 'should be running' {
        $status | should -Be 'Running'

    }
}

When executed, you will either get a success or failure. Both examples are shown below.

## Success
Describing The wuauserv service on SRV1
  [+] should be running 312ms

## Failure
Describing The wuauserv service on SRV1
  [-] should be running 130ms
    Expected 'Running', but got Stopped.
    8:         $status | should -Be 'Running'

The above example was a simple one. Nothing too complicated was going on there. With Pester, you can get as complex as you'd like by adding additional functionality.

For example, using a Pester context block, you can define specific states the server is in before running the test. The example above doesn't take into account situations for when the server is offline -- or perhaps even when the service doesn't exist on the server. There are a lot of scenarios to test.

Below is an example that accounts for when the server is offline or when the service doesn't exist. Notice that I'm setting the test to inconclusive. This means that I wasn't able to test to ensure the service is running because it didn't meet all of the prerequisites in place to perform the test in the first place.

## ServiceTests.Tests.ps1
describe 'The wuauserv service on SRV1' {

    $serverName = 'SRV1'
    $serviceName = 'wuauserv'

    if (-not (Test-Connection -ComputerName $serverName -Quiet -Count 1)) {
        context 'when the server is offline' {
            Set-ItResult -Inconclusive
        }
    } else { 
        $service = Get-Service -ComputerName $serverName -Name $serviceName -ErrorVariable err -ErrorAction SilentlyContinue
        if ($err -and $err.Exception.Message -like '*Cannot find any service with service name*') {
            context 'when the service does not exist' {
                Set-ItResult -Inconclusive
            }
        } else {
            context 'when the server is online, and the service exists' {

                $status = (Get-Service -ComputerName $serverName -Name $serviceName).Status

                it 'service should be running' {
                    $status | should -Be 'Running'
                }
            }
        }
    }
}

When this test is run and it doesn't meet the prerequisites, it will return an inconclusive result, as shown below.

Describing The wuauserv service on SRV1

  Context when the server is offline
    [-] Error occurred in Context block 0ms
      Exception: It result set to Inconclusive
      at Set-ItResult, C:\Program Files\WindowsPowerShell\Modules\Pester\4.7.3\Functions\Set-ItResult.ps1: line 70
      at DescribeImpl, C:\Program Files\WindowsPowerShell\Modules\Pester\4.7.3\Functions\Describe.ps1: line 199

Building infrastructure tests with Pester is an art rather than a science. Due to the flexibility Pester gives you, you can build infrastructure tests in many different ways. If you'd like to dive deeper into infrastructure tests and learn many more ways to make them, be sure to check out The Pester Book, which goes into a much deeper dive on infrastructure testing.

About the Author

Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.


comments powered by Disqus
Most   Popular