Let’s Talk Some Powershell

It has been roughly 2 weeks since I began the journey into PowerShell.  In that short amount of time, I have learned a ton.  I have to say, if you are a system admin and you don’t already know PowerShell, get on it.  It is such a time saver.  Let’s take one example in particular.  Just think about for a moment one of the most time consuming tasks you will ever do.  Server maintenance.  With PowerShell and Pester, no more will you painstakingly remote into every server to verify that the correct services are running.  No more will you open a web browser to verify that the site loads.  With a little bit of up front work, you will run a single command and check every server you have in minutes.  Yes, I said minutes.  I’ll give you a moment to let that sink in.

A little back story.  In my endless search of PowerShell information, I came across something called Pester.  What is Pester you ask?  Pester was designed to give PowerShell developers a way to do test driven development.  What it does is evaluate the result of a command or set of commands.  The evaluation will either pass or fail and give a visual representation of that pass or fail as either green, or red.  Very interesting.  In doing some more searching on Pester itself, I came across a blog post from last week showing how they were using Pester to do operational validation on their SQL server.  Even more interesting.  Seeing as how I just did server maintenance the weekend before that and it takes roughly an hour+ to verify everything is good after rebooting all the servers, the pain was still fresh in my memory.

First thing was first, get Pester installed.  Fortunately, I’m running Windows 10 now on my work laptop and Pester is preinstalled.  It was a sign.  Opened up PowerShell, created a new directory for my validation test and created my first Pester fixture.

New-Fixture -Name server

This will create two new files:
server.ps1
server.Tests.ps1

If you open up server.ps1, you’ll notice that all that is there is an empty function named “server”.  This file must remain in the same directory as the server.Tests.ps1 file otherwise when you go to run your pester tests, it will throw an error.  Found this out the hard way.  Best thing to do is try to ignore the fact that there is an extra file.  It isn’t needed for our purpose.

Opening up the server.Tests.ps1 file, you will see the following.

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
. "$here\$sut"

Describe "server" {
It "does something useful" {
$true | Should Be $false
}
}

Always leave those first four lines alone.  We are concerned with the rest of it to configure our tests.  Just to see what happens, from within the directory containing our test, let’s test it.

Invoke-Pester

You will immediately see a block of the dreaded PowerShell red text.  Let’s take a look at what it actually is telling us compared to what the file had in it line by line.

The first thing Pester tells us when we run our test is “Describing server”.

Describe "server" {

Ok, so now we know where that comes from in the test file.

The next thing Pester tells us is “does something useful”.

It "does something useful" {

So far so good.  The rest of what Pester is telling us is that it was expecting false but got true.  This is explained here:

$true | Should Be $false

With me so far?  This is where the fun part begins.  Let’s just say that we want to verify that BITS is running on our server.  This is what we would change in our test file.

It "The BITS service should be running." {
(Invoke-Command -ComputerName server {Get-Service -Name bits}).status |
Should Be 'Running'
}

Now, when you “Invoke-Pester”, it will show a pretty green line of success.  A couple of things to take note of.  I placed the entire command in () because we needed to pipe the status of the object returned by the command(which in this case was a service object, we just as easily could have checked the .name of a get-aduser object).  The last line reads just like it says.  The status of the bits service “Should Be ‘Running'”.  We are telling Pester what to verify.

I have created all of my base tests for services as well as port connection for my servers.  A good example of a port connection test is for a file server.  You know that it will need to answer requests via SMB.  So add another test to the test file for that server.  Make sure you add the -InformationalLeve Quiet so it only returns $true or $false.

It "Should accept connections on port 445(SMB)." {
Test-NetConnection -ComputerName server -Port 445 -InformationalLevel Quiet |
Should Be $true
}

I think you get the idea now.  Any kind of information you can grab with PowerShell, you can verify.  For example, this could be a basic test file for a SQL server.


$ComputerName = 'SQLServer'
$Session = New-PSSession -ComputerName $ComputerName

Describe "SQLServer" {
It "Should be listening on port 1433(SQL Server)" {
Test-NetConnection -ComputerName $ComputerName -Port 1433 -InformationLevel Quiet |
Should be $true
}
It "Is SQL Server running?" {
(Invoke-Command -Session $Session {get-service -name MSSQLSERVER}).status |
Should be 'Running'
}
It "Is SQL Server Agent Service running?" {
(Invoke-Command -Session $Session {get-service -name SQLSERVERAGENT}).status |
Should be 'Running'
}
}

Remove-PSSession -Session $Session

One thing I’ve learned in the very short time learning/using PowerShell.  Create a variable for anything you will use more than once in a script.  Also, if you need to run multiple commands on a single remote machine, create a PSSession for it.  The commands will run faster.  It doesn’t seem like much of a gain on these small scripts, but if you have a very large test file with tons of commands being sent to the remote server, the speed gains add up.  Especially if you are going to run tests on 10s to 100s of servers(see below).  Also, create a separate Pester fixture for each server.  This way you can test one server if you need to.

Invoke-Pester -TestName server

This will run Pester on just that one test file.  If you “Invoke-Pester” without any arguments, it will run every test file in the current directory.

The last little thing I will leave you is this.  Let’s say that you want to add many services to test with Pester.  I wrote a small script that will basically generate the test code for Pester for services.  It dumps it to a test.ps1 file which you can then manually copy and paste into your actual test file.  This could probably be better to be more automated, but as I haven’t been using this very long, it was a good start and did save me a ton of time.

$Services = get-service -DisplayName sql* -ComputerName server | where status -eq 'running'

Remove-Item -Path "C:\Scripts\test.ps1" -Force

foreach ($S in $Services){

$DisplayName = $S.DisplayName
$Name = $S.Name

$test = "It `"Is the $DisplayName service running?`" {
(Invoke-Command -Session `$Session {Get-Service -Name $Name}).status |
Should Be 'Running'
}"

$test | Out-File -FilePath "C:\Scripts\test.ps1" -Append

}

As always, any tips, comments, feedback, or questions are welcome.  Thanks for dropping by and I’ll see you soon.

Advertisements
Let’s Talk Some Powershell

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s