Windows PowerShellWorking Without a Script

Don Jones

Whenever I'm talking to folks about Windows PowerShell—whether at a conference, on the public Windows PowerShell newsgroup, or even on my site—I always meet administrators who tell me they're "going to wait" to learn Windows PowerShell. Why? The most common reason they give is that they

"just don't have time to learn how to script right now."

It's a common sentiment. We administrators are a busy group, and we really don't have much free time to learn a new technology. But we have to keep moving forward, learning new software such as Windows Vista®, Windows Server® 2008, and Exchange Server 2007, so we can stay on top of the technologies that are critical to our jobs. And Windows PowerShell™ is a big part of these technologies.

Another reason I often hear administrators cite for putting off learning to script in Windows PowerShell is that they have a "fear of becoming a programmer," as one fellow administrator put it. Well, that is something I can help you with. You may be surprised to learn that you can do some truly amazing things using Windows PowerShell, all without writing a lick of code.

Getting Ready to Pipeline

I've written before about the extremely flexible and powerful object-oriented pipeline that Windows PowerShell uses. (Even though the words "object-oriented" might make you suspect that I'm heading for programmer-land, stick with me.) One of my favorite things about the pipeline is how it can be used interactively. It can give you instant results and lets you easily refine those results so you can get exactly what you want. For example, suppose I want to get a free disk space inventory for several remote computers. Now say I have those computers' names listed in a text file called C:\Computers.txt. It's just a plain text file with one computer name per line—nothing as complicated as a database.

To start, I'll see if I can retrieve this information from my local computer. After all, if I can figure it out on one computer, performing the task on multiple computers should be pretty easy. And remember, there is to be no scripting involved here! I'm going to do everything interactively in the shell, getting results as soon as I press the Enter key.

Here's a hint about this sort of task. Any time you want to inventory management information from remote computers, you will probably use Windows® Management Instrumentation (WMI). Now assume that, for the purposes of this example, I know nothing more than that I need to use WMI. So I hop on Live Search and enter "windows wmi free disk space" as my search term. Several results come up that list the phrase "Win32_LogicalDisk" in their excerpts. That looks to me like a WMI class, and it may be the one I'm after. I don't even bother to click on any of those search results. Instead, I enter "Win32_LogicalDisk" as a new search term and the top result is for a Win32_LogicalDisk documentation page on the MSDN® site (msdn2.microsoft.com/aa394173.aspx). So I hop on over to that page and see that one of the properties for this class is FreeSpace, which sounds very promising.

Finding a Cmdlet

For the next step, I need to find a cmdlet that will retrieve a WMI class's properties for me. One of the notable aspects of Windows PowerShell is that it has fantastic built-in self-discovery features—that is, the shell can help you find out about its own capabilities. So I enter Help *wmi* to find out what the shell knows about working with WMI. The results, shown in Figure 1, are the Gwmi alias and the cmdlet it points to, Get-WMIObject. Basically, there are two ways to access the same functionality, which means I don't have a very tough decision about which one to use. Since they do the same thing and Gwmi is shorter to type, I'll use that:

Figure 1 What the shell knows about working with WMI

Figure 1** What the shell knows about working with WMI **(Click the image for a larger view)

PS C:\> gwmi win32_logicaldisk

Note that Windows PowerShell isn't case-sensitive—it knows we administrators don't have time for fussy details like pressing the Shift key.

The results of this command, shown in Figure 2, include all five of my local drives and their FreeSpace properties, which appear to be listed as bytes. Also listed is a DriveType property for each one of these drives, and I'm seeing values of 2, 3, and 5 on my computer.

Figure 2 All of my local drives and their FreeSpace properties

Figure 2** All of my local drives and their FreeSpace properties **(Click the image for a larger view)

Cmdlet of the Month

You may have used Get-Content to read the contents of a text file. It treats each line of the file as a [string] object, passing those objects into the pipeline for other cmdlets to work with. But Get-Content has a number of options that make it more flexible: the –readCount parameter, for example, allows you to specify how many [string] objects are passed into the pipeline at once (the default is to send them all), while the –totalCount parameter controls the total number of lines that are read from the file. Both of these parameters are useful for working with really large files where you might not want to process the entire file at once for performance reasons.

Here's another you'll find handy. The –encoding parameter allows a large variety of file encoding types to be read correctly, including Unicode, ASCII, UTF7, UTF8, and many others. To see a complete list of supported encoding types, run help gc (gc is an alias for Get-Content).

Refining the Data

I have two problems at this point. First, I don't care about all those other properties, I just want the FreeSpace. Second, I don't want to know about free space for optical drives, removable drives, or network drives; I am only concerned with local hard disks. Maybe that DriveType property can help me distinguish between them. I flip back to my Web browser and see that the documentation contains a table explaining what the different DriveType values represent. I only want the ones with a DriveType of 3, which indicates that the drive is a local hard disk. Having never done this before, I decide to see if the Gwmi command can do some kind of filtering for me. Running Help Gwmi –full pulls up details about how the command works, including several examples. I find a parameter that looks useful, –filter, and so decide to try running this:

gwmi win32_logicaldisk -filter "drivetype = 3"

It worked! My drive list now contains only local fixed disks. That's the second problem solved; now I need to solve the first one—getting rid of all the properties I don't care about.

I run Get-Command to list all the Windows PowerShell cmdlets and I eventually come upon Select-Object. The description says this will "Select specified properties of an object or set of objects." So I try this:

gwmi win32_logicaldisk -filter "drivetype = 3" | select freespace

By piping the results of Gwmi to Select (the alias for Select-Object), I can get just the property I want. Oops. These results aren't so good, as all I have is a list of numbers—I no longer know which drive goes with which free space listing. So I jump back to the documentation and see that the DeviceID property contains the drive letter. I quickly revise my command and try again:

gwmi win32_logicaldisk -filter "drivetype = 3" | select deviceid,freespace

Perfect! And since only two properties are being listed, the shell is able to put the information into a nice-looking table. (Next month I'll explain when Windows PowerShell chooses to use lists and tables.)

Calculating

I've retrieved information about free space, but it's in bytes and that isn't really as useful as megabytes or gigabytes would be. Perhaps I don't want the FreeSpace property itself, but rather a value calculated from the FreeSpace property. The ForEach-Object cmdlet (or one of its many aliases, such as %) can help with that. This cmdlet lets me use a special variable, $_, to refer to the current logical disk and it lets me access each disk's properties individually and do some math. Here you can see my revised command:

gwmi win32_logicaldisk -filter "drivetype = 3" | % { $_.deviceid; $_.freespace/1GB }

All I've done here is removed Select and replace it with the alias for ForEach-Object (%). That cmdlet just wants me to tell it what to do with each Win32_LogicalDisk it gets and I've told it to get the DeviceID property and to divide the FreeSpace property by a gigabyte—Windows PowerShell "knows" what a KB, MB, and GB are—so that my output is given in gigabytes.

Many Computers

Now that I have this working for one computer, it's time to get it working for several systems. I know how to get the content of a text file. This is done in the same way as back in the MS-DOS® days—using the Type command, which in Windows PowerShell, it turns out, is an alias for Get-Content:

Type c:\computers.txt

What I want to do is take each of these computers, and for each one, perform the WMI command I already worked out. The phrase "for each one" should remind you of a cmdlet—specifically, ForEach-Object. So let's just cut to the big reveal, as they say on those home-design television shows, and look at my final command:

type c:\computers.txt | % { $_; 
gwmi –computername $_ win32_logicaldisk -filter "drivetype=3" | % { $_.deviceid; $_.freespace/1GB} }

Scary, isn't it? Thanks to my exclusive use of aliases rather than cmdlet names, it's tough to read. However, it's pretty easy to decipher if you walk through it one bit at a time:

  • I start by "typing" the contents of my text file.
  • I pipe those contents to ForEach-Object.
  • ForEach-Object outputs the current item, using the $_ variable. (That's the current computer name.)
  • ForEach-Object then executes my WMI command, which has another ForEach-Object call.

Expanding those alias names into cmdlet names may help. So here is the same command, but this time I've spelled out the cmdlets and broken the command apart into individual lines so that you are able to examine each section more easily:

Get-Content C:\Computers.txt | 
ForEach-Object { 
 $_; Get-WMIObject –computername $_ 
Win32_LogicalDisk -filter "DriveType=3" | 
 ForEach-Object { 
 $_.DeviceID; $_.FreeSpace/1GB
 }
}

The results—which aren't attractive, but are functional—are shown in Figure 3.

Figure 3 The final results

Figure 3** The final results **(Click the image for a larger view)

No Scripting Required

The point of this walk-through is to demonstrate that you can use Windows PowerShell without doing any scripting. And, with just a few examples like this one, plus a little exploring within Windows PowerShell itself, you can actually find some of the bits you need to accomplish whatever task you've set out to do.

The bottom line is that Windows PowerShell is here to stay. Why not start learning to use it now? You might even find yourself wondering how you ever lived without it—and you certainly shouldn't let the word scripting keep you from diving right in.

Don Jones is the Lead Scripting Guru for SAPIEN Technologies and an instructor for ScriptingTraining.com. Reach Don through his Web site, ScriptingAnswers.com.

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