Windows PowerShellDigging Deeper

Don Jones

Windows PowerShell is packed full of features that often get overlooked by many administrators. If you dig deep, you'll find some amazing capabilities. In fact, I often find myself discovering new features associated with a particular cmdlet that I thought I already knew inside out.

In the Windows PowerShellTM classes that I teach, I recommend that administrators make a cmdlet-of-the-day calendar for themselves—similar to a word-of-the-day calendar you might purchase. Then, each weekday morning (I let them take the weekend off), they can spend a few minutes familiarizing themselves with the complete capabilities of one cmdlet. Windows PowerShell ships with about 130 cmdlets, which means you can learn just about everything Windows PowerShell is capable of in just about six months if you stick to this schedule. At that point, you'll be ready for all of the Exchange Server 2007 cmdlets.

In this month's column, I want to give you some examples of what you might discover and what you can achieve if you simply take a few minutes to explore the deeper capabilities of various cmdlets.

Prettier HTML Reports

One cmdlet that's quite underutilized is ConvertTo-HTML. This clever cmdlet can accept a collection of input objects—services, processes, Windows® Management Instrumentation (WMI) objects, or whatever—and turn them into an HTML table. You can then pipe the HTML to Out-File and you've got an HTML page suitable for posting on an intranet Web server. For instance, you might schedule this simple line to run every morning:

Gwmi Win32_Service | Where { $_.StartMode
–eq "Auto" –and $_.State –ne "Running" } |
ConvertTo-HTML | Out-File C:\ServiceAlert.html

This will create an HTML report, like the one shown in Figure 1, of services that should be started automatically but aren't running.

Figure 1 An HTML report of services that aren't running

Figure 1** An HTML report of services that aren't running **(Click the image for a larger view)

Of course, you might want to produce a report that is a bit more attractive. Fortunately, the ConvertTo-HTML cmdlet produces clean HTML, meaning it doesn't embed any formatting into the HTML code that it creates. According to the rules of HTML, you should never put formatting into the HTML (well, you should put as little as possible). Instead, you should put formatting in an external cascading style sheet (CSS) and link the CSS to the HTML. And you can create that link with ConvertTo-HTML.

CSS links into the <HEAD> section of an HTML file. Looking at the help for ConvertTo-HTML, you'll see that its syntax includes some parameters that you may have overlooked:

ConvertTo-Html [[-property] <Object[]>] 
[-inputObject <psobject>] [-body <string[]>] 
[-head <string[]>] [-title <string>] [<CommonParameters>]

The –head parameter allows you to specify additional HTML code to be inserted into the <HEAD> section of the script. I can now easily modify my one-liner to link to an existing CSS file that contains the formatting I want to apply to the HTML table:

Gwmi Win32_Service | Where { $_.StartMode
–eq "Auto" –and $_.State –ne "Running" } |
ConvertTo-HTML -title "Services" -head "<link
rel='stylesheet' href='styles.css' type='text/
css' />" | Out-File C:\ServiceAlert.html

Here, I used the –head parameter to insert a link to a CSS file that is located in the same folder as the output HTML file. I also used the –title parameter to set the title of the Web page. The result is shown in Figure 2. The text of the Style.CSS file I used looks like this:

Figure 2 An HTML report that is formatted with a CSS file

Figure 2** An HTML report that is formatted with a CSS file **(Click the image for a larger view)

body { background-color:#EEEEEE; }
body,table,td,th { font-family:Tahoma; color:Black; Font-Size:10pt }
th { font-weight:bold; background-color:#CCCCCC; }
td { background-color:white; }

Easier Filtering

Cmdlet of the Month

This month I am looking at a cmdlet pair: Start-Transcript and Stop-Transcript. Both are used to control Windows PowerShell transcript logging—that is, having everything that appears in the console window written to a text file that you have specified. It's quite simple. Run Start-Transcript and give it a file name to kick things off. Then run Stop-Transcript to end logging and close the file. This provides a great way to go from ad hoc shell use to formal scripting—once you get individual command lines working in the shell, you can just copy and paste them from the transcript file that you've created. Or, of course, you can edit down the transcript to just the actual script. Fellow MVP Jeffery Hicks even wrote a script that parses transcripts and turns them into Windows PowerShell PS1 files. You can find this script at

Now let's look at the Get-WMIObject cmdlet. Its syntax, as listed by the Windows PowerShell built-in help, hints at untapped capabilities:

Get-WmiObject [-class] <string> [[-property]
<string[]>] [-namespace <string>] 
[-computerName <string[]>] [-filter <string>] 
[-credential <PSCredential>]

One common mistake new Windows PowerShell users will make is to issue a WMI command like this:

Gwmi Win32_NTLogEvent –comp Server2

This command will retrieve the event log entries from Server2—all of the event log entries. That task will take a while for Server2 to process and transmit, and Windows PowerShell will take quite a while to do anything useful with such a large collection.

A better approach is to have Server2 find and send the events you really care about. You could certainly do that with a WMI Query Language (WQL) query. Some folks find that query syntax difficult, but Windows PowerShell lets you just specify the filtering portion of a WQL query, using the –filter parameter:

Gwmi Win32_NTLogEvent –comp Server2 –filter "EventIdentifier=1024"

This will retrieve all events—from any log—with the event ID 1024. Notice that the filter criteria uses = as a comparison operator rather than the Windows PowerShell –eq operator. This is because the filter is just being passed to the remote computer's WMI service for processing, so the criteria needs to be in the WMI syntax, not the Windows PowerShell syntax.

There is actually a –filter parameter on other cmdlets such as Get-ChildItem, the cmdlet behind aliases such as Dir and Ls. In most cases, the –filter parameter passes your filter criteria directly to the underlying technology, resulting in what I like to call source filtering, which is often much faster than bringing over all of the objects and then running them through the Where-Object cmdlet to filter out the ones you don't want.

Overlooked Cmdlets

Sure, this one-a-day approach to studying cmdlets will help you better understand cmdlets that you thought you already knew. But another good reason to have a cmdlet-of-the-day calendar is to avoid entirely overlooking cmdlets that you might not otherwise discover at all. One of my favorites that many people fail to use is Resolve-Path. Given a wildcard path, this will return a collection of file and folder names that match the path. It's similar to the Get-ChildItem cmdlet (that is, the familiar Dir or Ls aliases), but instead of returning entire file and folder objects, it returns simple strings that can be piped to other cmdlets for further filtering or processing. Using it is straightforward:

Resolve-Path C:\P*

This simple line will return such paths as C:\Program Files, C:\Processes.txt, and so forth. Figure 3 shows two examples of this cmdlet in use.

Figure 3 Using the handy, though overlooked, Resolve-Path cmdlet

Figure 3** Using the handy, though overlooked, Resolve-Path cmdlet **(Click the image for a larger view)

Start at the Very Beginning

When you're ready to start your cmdlet-of-the-day experience, run Gcm, an alias for Get-Command. You'll get a list of all cmdlets that Windows PowerShell knows about, including those you've added through snap-ins, such as the Exchange Server 2007 Management Shell, the PowerShell Community Extensions, and so forth. Just pick the first one on the list—mine was Add-Content—and read its help:

Help Add-Content –full

You'll want the full help—as opposed to the more concise default help—so you can get a complete description of what each parameter does, see some examples of the cmdlet's usage, and find out other detailed information. Take a moment or two to experiment with the cmdlet. (You may want to use a virtual machine so that you're not playing around with your production environment.) I recommend that you set aside 10 minutes each weekday—and at about the same time each day, so that it becomes a habit. In no time at all, you'll have a deep understanding of all the functionality and capabilities Windows PowerShell has to offer.

Don Jones is the Lead Scripting Guru for SAPIEN Technologies and coauthor of Windows PowerShell: TFM (SAPIEN Press, 2007). You can contact him at

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.