How to Validate Infrastructure Changes Using PowerShell Pester Tests

April 18, 2019 Security and Compliance, MOVEit

Changes can make infrastructure better or can bring it to its knees. Change management is a crucial part of managing infrastructure in an IT pro’s world. When you do make changes, are you sure you changed the right setting? Are you sure that change you made didn’t affect something else you’re unaware of?

Perhaps, a coworker is making changes at the same time and they conflict. How can you be sure what changed is what you expected? One way is to use the PowerShell testing framework, Pester.

Pester was originally designed to build unit tests around PowerShell scripts but since it was built in PowerShell, many system administrators have adapted it to performing integration or infrastructure tests in a variety of different circumstances such as routine checks, part of larger automation scripts, or part of build and release pipelines to ensure any changes made to environments needed by software deployments happened as expected.

 

 

Infrastructure Tests

An infrastructure test is a generic term for any kind of 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 right user account?
  • Does that file server have the expected folder hierarchy?

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

Pester Tests

A Pester test can be broken down into a few main components; a describe block and an it block. This isn’t an article covering how to use Pester. There are plenty of other resources out there for that.

Let’s create a simple infrastructure test with Pester.

First of all, we need to figure out what we’re going to test. This can be anything that you can check the state of with PowerShell and that’s a lot of stuff. Let’s say that I have a PowerShell script that just starts a Windows service. After this PowerShell script is complete, I expect that service to be started. Below is an example of the PowerShell script starting the service on a remote server SRV1.

As you can see, the script is quite simple. We’re changing the state of a single item, the wuauserv Windows server from stopped (if stopped) to started. Easy enough. I’ll save this as a script called StartSRV1Service.ps1

Get-Service -ComputerName SRV1 -Name wuauserv | Start-Service

I’d like to test the results of this small script with Pester. To do that, I’ll first need to define all of the states the environment can be in before running the script. For this example, the wuauserv service can either be stopped or started. Let’s first ensure the service is stopped. Once we do that, we can then build a simple Pester test using a describe block and an it block which contains code to check the service’s status. Once that’s done, I perform the check to ensure it’s in the expected state.

describe 'The wuauserv service on SRV1' {

    $serverName = 'SRV1'

    context 'When the service is in a stopped state' {

        BeforeAll {
            ## Force the service to stop
            Get-Service -ComputerName $serverName -Name wuauserv | Stop-Service
        }

        ## Execute the script that makes the change
        & 'C:\StartSRV1Service.ps1'

        ## Check the state of the service after the script is done
        $serviceState = Get-Service -ComputerName $serverName -Name wuauserv

        it 'the wuauserv service should be started' {
            $serviceState.Status | should -Be 'Running'
        }
    }
}

When executed, if the script didn’t work, you’ll see something like this:

Describing The wuauserv service on SRV1

  Context When the service is in a stopped state
    [-] the wuauserv service should be started 8ms
      Expected 'Running', but got Stopped.
      19:             $serviceState.Status | should -Be 'Running'

At this point, we can add another context block in there to represented an initial started state to ensure our script doesn’t inadvertently stop it for some reason.

describe 'The wuauserv service on SRV1' {

    $serverName = 'SRV1'

    context 'When the service is in a stopped state' {

        BeforeAll {
            ## Force the service to stop
            Get-Service -ComputerName $serverName -Name wuauserv | Stop-Service
        }

        ## Execute the script that makes the change
        & 'C:\StartSRV1Service.ps1'

        $serviceState = Get-Service -ComputerName $serverName -Name wuauserv

        it 'the wuauserv service should be started' {
            $serviceState.Status | should -Be 'Running'
        }
    }

    context 'When the service is in a started state' {

        BeforeAll {
            ## Force the service to start
            Get-Service -ComputerName $serverName -Name wuauserv | Start-Service
        }

        ## Execute the script that makes the change
        & 'C:\StartSRV1Service.ps1'

        $serviceState = Get-Service -ComputerName $serverName -Name wuauserv

        it 'the wuauserv service should be started' {
            $serviceState.Status | should -Be 'Running'
        }
    }
}

Describing The wuauserv service on SRV1

  Context When the service is in a stopped state
    [-] the wuauserv service should be started 11ms
      Expected 'Running', but got Stopped.
      18:             $serviceState.Status | should -Be 'Running'

  Context When the service is in a started state
    [+] the wuauserv service should be started 10ms

Now you can see above that the script isn’t doing much at all! It’s clear from the tests that if I confirm the service is stopped, run the script then test the state, nothing changes. However, if I purposefully start the service beforehand, run the script and test the state, the service is still started! The script didn’t change anything at all.

This is a great advantage of building Pester tests. These tests can be expanded to a nearly infinite number of situations and test just about every state change you can think of. All it takes is some thinking up front to decide what to test, figuring out the code required to test it then building the Pesters tests to execute and compare states.

Adam Bertram

Adam Bertram is a 25+ year IT veteran and an experienced online business professional. He’s a successful blogger, consultant, 6x Microsoft MVP, trainer, published author and freelance writer for dozens of publications. For how-to tech tutorials, catch up with Adam at adamtheautomator.com, connect on LinkedIn or follow him on X at @adbertram.

Read next How to Search Windows Event Logs Across Hundreds of Servers