The power of PowerShell: Essential tips Windows admins will love

Make the most of Microsoft’s command line by mastering the nuances of the PowerShell language

The power of PowerShell: Essential tips Windows admins will love
Credit: RyanMcGuire via Pixabay

PowerShell can be a daunting tool to master for Windows admins used to working with the GUI. But as I discussed in our hands-on PowerShell intros for Windows Server and Exchange admins, adding even a little PowerShell into your daily mix can save a ton of effort.

Here, I’m going to concentrate on some of the fundamentals of PowerShell as a language to help you take your PowerShell skills to the next level. I won't cover technology-specific topics like how to manage Active Directory, Exchange, or IIS, nor will I cover specific PowerShell cmdlets. Instead, I will drill into the semantics of the language to show a few key techniques that you can put into your scripts today. Follow along and let's make your PowerShell scripts the best they can be.

Splatting

PowerShell code is executed using commands such as cmdlets or functions. To increase their reuse value, commands typically employ parameters. Rather than requiring you to overspecify a custom function like Get-ContentFromFileXYZ.TXT and Get-ContentFromFileABC.TXT, PowerShell offers a Path parameter on its  Get-Content cmdlet. Parameters, like Path, are typically passed to functions and cmdlets with a dash, followed by the parameter name, a space, and the parameter argument or value:

Get-Content –Path C:\FileABC.txt

But cmdlets and functions like Get-Content often support several parameters, and before long, you may be typing commands like this:

Get-Content -Path C:\MyText.txt -ReadCount 1 -TotalCount 3 -Force -Delimiter "," -Filter '*' -Include * -Exclude 'a'

Here, eight parameters are being passed to Get-Content, and it’s clearly getting hard to read, to the point where confusion can arise in discerning the parameter from the value you're passing into the argument. Any time you use multiple parameters, it's wise to opt for another method of passing parameters to commands with the funny name of splatting.

Splatting, introduced in PowerShell Version 2, allows you to define all of your parameters up front in a much easier-to-read format.

Let's take that long, complicated set of parameters and see how all of them can be passed to Get-Content using splatting.

$getContentParameters = @{
   'Path'       = 'C:\MyText.txt'
   'ReadCount'  = 1
   'TotalCount' = 3
   'Force'      = $true
   'Delimiter'  = ','
   'Filter'     = '*'
   'Include'    = '*'
   'Exclude'    = 'a'
}
Get-Content @getContentParameters

Though I was forced to use more lines it's plain to see this method is much more readable than jamming them all on a single line. I also chose to line up the "=" statements by using tabs to have all of the parameter arguments lined up.

The $getContentParameters variable defined in the first line is a hashtable. You can see this by the @{}. Inside, each key represents the parameter, and each value represents the parameter argument or value. One item to note in this example is the Force parameter. You'll notice when Force was used on one line it wasn't technically equal to anything. This is because it is a type of switch. A switch parameter has no value and is simply used as a flag. Since hashtables require a value for each key, you must make any switch parameter used equal to a Boolean $true value.

Once you've defined the hashtable you then pass the entire variable to the command using the ampersand to represent a splatting variable. Get-Content will then process the commands exactly as if you'd passed them the traditional way with dashes.

This is a much cleaner way to pass arguments to commands, and I encourage its use whenever multiple parameters are in play.

Calculated properties

One of the biggest advantages of using PowerShell is that everything is an object. Objects have properties, but sometimes you may not want to get only the default property names on an object. Maybe you'd like to add another property, add text to an existing string property, or perform arithmetic on an integer property. You could send that property to a variable and make your necessary changes, but that requires another line and, depending on the complexity, can become unreadable real quick.

For example, let's say I want to test the network connectivity of a computer and get some common properties. To do this, I'll use the Test-NetConnection cmdlet.

Test-Connection -ComputerName localhost -Count 1 |
   Select-Object IPv4Address,ResponseTime,TimeToLive

Test-Connection -ComputerName localhost -Count 1 |  Select-Object IPv4Address,ResponseTime,TimeToLiv

This is great, but maybe I want to put this into a CSV report. As part of the CSV report, I'm reading server names from a text file. I'd like to include the server name as well as the properties I've shown above. I start out by reading the text file, but I don't have the server names that were in the text file.

CSV Report

I need to add another property to this to correlate the IP with the server name. This is a job for calculated properties. Since this requires additional code, I will now move into a script rather than show you examples directly from the console.

Adding ServerName

The output will now include our new custom property called ServerName.

ServerName Output

I had to change the code quite a bit so allow me to break it down. First, notice how I moved the original properties being passed to Select-Object (IPV4Address,ResponseTime,TimeToLive) above, then passed $selectProperties to Select-Object that way. This was simply to reduce the length of the line. It behaves exactly the same as if I simply passed them directly to Select-Object as I did in the console previously.

What functionality did change was the addition of another property in $selectProperties to pass to Select-Object. Notice it wasn't a string like the others but a hashtable with two elements inside: Name and Expression. This is called a calculated property. This is how using Select-Object you can essentially create properties on the fly. Every property you'd like to add has to be in a hashtable with a Name and an Expression as key names. The Name key's value is the name you'd like to call the object property. The Expression key's value always has to be a script block. Notice $serverName is enclosed in curly braces. This is how $serverName can be expanded to the actual server names as each server is tested in the text file.

This can be used not only to create new properties but to modify existing properties as well. Maybe I'd like to append a ms label to all the TimeToLive properties to signify the number is in milliseconds. Instead of specifying the name of the TimeToLive property I would instead create a hashtable and concatenate the foreach pipeline value of TimeToLive represented by $_.TimeToLive with ms to create a single string.

TimeToLive script
TimeToLive output

Using calculated properties with Select-Object are very convenient but beware: They come with a performance hit. I don't recommend using calculated properties if you're working with large data sets as it can drastically slow down your script. But if you have fairly small data sets with 100 or fewer elements the performance hit will be minimal.

Custom object creation

Objects abound in PowerShell, and it only makes sense for us to be able to create our own objects from scratch. Fortunately, PowerShell provides us with a few different ways to do that. In this tip, I'll cover three methods to create custom objects.

In PowerShell, an object is of a specific type. When creating custom objects, the most common type of object type is System.Management.Automation.PSCustomObject. This is the kind of object we'll create in this article. Also, an object has one or more properties of various types. In this article, we'll focus on NoteProperty types.

One of the oldest ways to create a custom object that works on all versions of PowerShell is via the New-Object cmdlet. To create a blank custom object of type System.Management.Automation.PSCustomObject with no properties using New-Object you'd simply call New-Object and specify the TypeName parameter of PSObject.

$object = New-Object –TypeName PSObject

$object = New-Object –TypeName PSObject

This doesn't do us any good, however, because it contains no properties. To add properties, we can use the Add-Member cmdlet. This cmdlet essentially binds a new member (or property) to an existing object similar to the one we created.

$object | Add-Member –MemberType NoteProperty –Name MyProperty –Value SomeValue

$object | Add-Member –MemberType NoteProperty –Name MyProperty –Value SomeValue

You can see that Add-Member has a MemberType parameter. As I mentioned earlier, when creating your own objects you will typically use the NoteProperty type here. At this point, you simply need to specify the name of the property and the value that the property will hold.

From here, you can repeat adding as many properties as you'd like using Add-Member.

Next, rather than using Add-Member you can specify all your properties up front in a hashtable and pass those properties to New-Object.

$properties = @{ 'MyProperty1' = 'Value1'; 'MyProperty2' = 'Value2' }
$object = New-Object –TypeName PSObject –Property $properties

$object = New-Object –TypeName PSObject –Property $properties

Finally, as of PowerShell v3, we can use the [pscustomobject] type accelerator. Occasionally, the PowerShell team will create what's called type accelerators. These are convenient shortcuts to create objects of particular types. Since creating custom types are so common in PowerShell, they decided to create one for creating them. Nowadays, this is the most common way to create custom objects.

To create custom objects with the [pscustomobject] type accelerator you'll first need to create a hashtable with property names as key names and their values as the hashtable values. Let's reuse the hashtable we created earlier.

$properties = @{ 'MyProperty1' = 'Value1'; 'MyProperty2' = 'Value2' }

Now, instead of using the New-Object cmdlet and specifying the type and properties we can simply cast that hashtable directly to a custom object simply by "declaring" the hashtable as a custom object type.

[pscustomobject]$properties

 [pscustomobject]$properties

You'll see it’s a much faster way to create a custom object. I recommend this approach when working with PowerShell v3 or later. It's, by far, the easiest to remember and is the most readable.

In this tips and tricks article, we were able to cover a few language-specific concepts in PowerShell. Replicate what I've done, tinker around, use the Get-Member cmdlet to explore the custom objects further. PowerShell has so much more to offer from a language perspective. If you're new to PowerShell I recommend checking out the book "PowerShell in a Month of Lunches." It addresses the topics we've discussed here but covers much more of PowerShell and takes a well-rounded approach to learning the language.

Related articles

From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.