Click to Rate and Give Feedback
Tips
Windows 7 beta 1 includes new keyboard shortcuts that allow you to navigate and manage the Windows workspace more efficiently. Here are 10 shortcuts you should know. ...

Read more!

Windows Vista includes an assortment of utilities for diagnosing, monitoring, and repairing network connections. Here’s an overview of the key tools and what they’re used for. ...

Read more!

The easiest way to create a copy of a table is to use Transact-SQL. Find out how. ...

Read more!

You can use T-SQL commands to manage access and roles in SQL Server. Here's a summary of the commands you'll use. ...

Read more!

You can pin files and resources to the Taskbar for easy access to the documents you use most often. Find out how. ...

Read more!

Related Articles

Don Jones demonstrates a Windows PowerShell-based inventory tool and guides you through the process that goes into building such a solution.

Don Jones

TechNet Magazine November 2008

...

Read more!

Despite its object oriented nature, Windows PowerShell is also able to parse complicated strings. Don Jones demonstrates how you can do this in Windows PowerShell using Select-String.

Don Jones

TechNet Magazine September 2008

...

Read more!

File compression is even more useful when you can automate the process via scripting. Here are some Windows PowerShell scripts you can use to create and expand cab files.

The Microsoft Scripting Guys

TechNet Magazine April 2009

...

Read more!

Don Jones continues his look at how you can use Windows PowerShell to automate user provisioning. In this installment, Don details the function that will create a mail-enabled user in Active Directory.

Don Jones

TechNet Magazine April 2009

...

Read more!

This installment clears up some confusion over Active Directory Browser, ADSI Scriptomatic, and browsing Active Directory. And in doing so, the Scripting Guy creates a Windows PowerShell script that will allow you to browse the Active Directory schema.

The Microsoft Scripting Guys

TechNet Magazine February 2009

...

Read more!

Also by this Author

When designing Windows PowerShell, a lot of thought was put into security. But, as with any software, Windows PowerShell can still be exposed to malicious code. Here are some recommendations for choosing an Execution Policy that will keep your systems more secure.

Don Jones

TechNet Magazine January 2008

...

Read more!

Don Jones shares his top six common suggestions to improve Windows PowerShell scripts.

Don Jones

TechNet Magazine July 2009

...

Read more!

Don Jones continues his four-part series that explains how you can create an automated provisioning function in Windows PowerShell

Don Jones

TechNet Magazine May 2009

...

Read more!

Permissions in Windows are, unfortunately, innately complicated. But Windows PowerShell can help. Get a quick overview of how permissions in Windows work and find out how you can simplify the task of working with them.

Don Jones

TechNet Magazine February 2008

...

Read more!

Despite its object oriented nature, Windows PowerShell is also able to parse complicated strings. Don Jones demonstrates how you can do this in Windows PowerShell using Select-String.

Don Jones

TechNet Magazine September 2008

...

Read more!

Popular Articles

Security principals underlie so much of Windows security that it is essential for any administrator to have at least a basic understanding of how the various types of Security principals work and how they are used. Here's what you need to know.

Jesper M. Johansson

TechNet Magazine January 2009

...

Read more!

Consolidating servers onto fewer physical machines has many advantages, but it is extremely important that you plan for your systems to be highly available. Here’s a guide to using Windows Server 2008 Failover Clustering to bring high availability to your Hyper-V virtual machines.

Steven Ekren

TechNet Magazine October 2008

...

Read more!

Project Server 2007 delivers significant enhancements, not only to the features and functionality for users but also for administrators. Alan Maddison explores some of the most significant new features and walks you through the installation and configuration of Microsoft Office Project Server 2007.

Alan Maddison

TechNet Magazine January 2009

...

Read more!

Take a close look at SharePoint Security Accounts to see how a weak configuration can give an attacker full control over all site collections and sites.

Pav Cherny

TechNet Magazine January 2009

...

Read more!

Drivers fail, files get corrupted, disks crash--there are numerous uncontrollable reasons why Windows might fail. But all is not lost. Wes Miller explores the kinds of things that can go wrong in a Windows system, and explains how you can troubleshoot them to get your system working again.

Wes Miller

TechNet Magazine January 2009

...

Read more!

Our Blog

NAP monitors the health of specified computers when they attempt to connect to a network and includes a number of mechanisms to enforce health requirements. In this article, Geek of All Trades Greg Shields gives readers an overview of these enforcement mechanisms and, as an example, takes a closer look at setting ...

Read more!

Use Windows PowerShell to Manage Virtual Machines Here are a few examples of how you can use Windows PowerShell scripts to manage virtual machines running on a Server Core installation. Note that these scripts are presented as samples and may need to be customized to work in your environment.

Create a New ...

Read more!

Disabling an Unused Part of Group Policy Objects One way to disable a policy is to disable an unused part of the GPO. By disabling part of a policy that isn’t used, the application of GPOs and security will be faster.

Administer Windows Server 2008 Server Core from the Command Prompt ...

Read more!

In the August 2008 issue of TechNet Magazine, Paul Randal wrote an article Top Tips for Effective Database Maintenance.  It was geared toward "involuntary  DBAs" (IT pros who inadvertently wind up responsible for a SQL Server instance).  The article had a great response from our readers so Paul has written another ...

Read more!

Microsoft Forefront is designed to deliver an integrated security solution that makes it much easier to deploy and manage security across an organization’s IT infrastructure. In this, our annual security issue, we feature two articles that describe how Forefront Security protects instant messaging and e-mail.

Protect ...

Read more!

Windows PowerShell Building a Better Inventory Tool
Don Jones


In the previous installment of this column, I created a function that could retrieve service pack inventory information from multiple remote computers. While this is a very practical tool that can be quite handy, I was also concerned with the process I used to develop the tool. In this installment, I want to demonstrate the process I use so you will be better equipped to build your own functions.
The function I created in the previous installment looks like this:
Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_OperatingSystem  
      –comp $_ | 
    Select CSName,BuildNumber,
      ServicePackMajorVersion
    Write-Output $wmi
  }
}
And here is how you can use it:
Get-Content c:\computernames.txt | 
Get-SPInventory
This works well, if the text file includes one computer name per line.
One particular weakness of this function is that it is limited to returning data from a single WMI (Windows Management Instrumentation) class. So what if I also want to return the BIOS serial number?

Video
Following up on the previous installment of his Windows PowerShell column, Don Jones shows you how to make an even better inventorying tool. And, in doing so, he demonstrates a sound process for building your own custom Windows PowerShell functions.


Objects Are Always the Same
The problem is that my function is retrieving the Win32_OperatingSystem class from WMI and simply outputting that object. The Win32_OperatingSystem class, like all objects, is fixed in the data it contains—it doesn't include a BIOS serial number and never can. As long as I am merely outputting Win32_OperatingSystem objects, my output can never include a serial number.
No matter how much you rummage around in WMI, you will not find a single object class that includes both BIOS serial numbers and service pack information. As a result, my function cannot simply output a WMI class. Instead, I need it to output a custom object that I make up on the fly—one that contains all of the data I need.
To create a new, blank object with no properties, I just run this:
$obj = New-Object PSObject
The new object is stored in the variable $obj, and I can add whatever data I want to it. Once I have added all my data, it will then become the output of my function.
Inside my function, I've retrieved the Win32_OperatingSystem information and stored it in the variable $wmi. Using that information is as simple as referring to properties of $wmi. For example, to get the BuildNumber property, I would use this:
$wmi.BuildNumber
To add a property to my custom object, I have to pipe the object (which is in the variable $obj) to Add-Member. I tell Add-Member what type of property I want to add (always a NoteProperty), the name of the property I want to add, and the value I want the property to have, like so:
$obj | Add-Member NoteProperty BuildNumber 
($wmi.BuildNumber)
I can do this for the computer system name and service pack version as well:
$obj | Add-Member NotePropertyCSName 
  ($wmi.CSName)
$obj | Add-Member NotePropertySPVersion 
  ($wmi.ServicePackMajorVersion)
Wrapping that up yields a new function (see Figure 1). Notice that I've changed the Write-Output line to output the custom object, rather than the original $wmi object.
Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_       OperatingSystem –comp $_ | 
       Select CSName,BuildNumber,ServicePack         MajorVersion
    $obj = New-Object PSObject
    $obj | Add-Member NoteProperty 
      BuildNumber ($wmi.BuildNumber)
    $obj | Add-Member NoteProperty CSName      ($wmi.CSName) 
    $obj | Add-Member NoteProperty SPVersion 
      ($wmi.ServicePackMajorVersion)
    Write-Output $obj
  }
}
Now I need to get that BIOS serial number. Some searching turns up the Win32_BIOS class, which has a Serial­Number property (Figure 2 shows a portion of the online documentation page). So I simply have to query the Win32_BIOS class, add the Serial­Number property to my custom object, and I'm done. You can see the revised function in Figure 3.
Figure 2 Win32_BIOS class documentation page (Click the image for a larger view)
Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_      OperatingSystem –comp $_ |    
      Select CSName,BuildNumber,ServicePack        MajorVersion
    $obj = New-Object PSObject
    $obj | Add-Member NoteProperty 
      BuildNumber ($wmi.BuildNumber)
    $obj | Add-Member NoteProperty CSName      ($wmi.CSName)
    $obj | Add-Member NoteProperty SPVersion 
      ($wmi.ServicePackMajorVersion)

    $wmi = Get-WmiObject Win32_BIOS –comp $_ | 
      Select SerialNumber
    $obj | Add-Member NoteProperty BIOSSerial 
      ($wmi.SerialNumber)
    Write-Output $obj
  }
}

Flexible Output
By using a custom object for the output, I've created a variety of potential uses for this function. For example, to include only Windows Server 2008 computers, I can use this:
Gc c:\computernames.txt | Get-SPInventory | 
Where { $_.BuildNumber –eq 6001 }
Or, to create an HTML file listing all of the Windows Vista computers that don't have Service Pack 1 installed, I can use this:
Gc c:\computernames.txt | Get-SPInventory | 
Where { $_.BuildNumber –eq 6000 } | 
ConvertTo-HTML | Out-File c:\VistaInventory.html
The possibilities are endless—XML, CSV, tables, lists, HTML, sorted, filtered, grouped, and so on. The built-in cmdlets in Windows PowerShell are designed to work with objects. By creating objects for the output, you can take advantage of everything Windows PowerShell is capable of doing—with no extra work!

Flexible Input
Unfortunately, my function isn't as flexible in terms of input. This is partially a limitation of how functions are designed in version 1 of Windows PowerShell—version 2 will introduce script cmdlets, which offer a great deal more flexibility.
My function expects its pipeline input to be simple string objects. The function wouldn't work if I were to replace the Get-Content command with, say, a cmdlet to retrieve all the computer names from Active Directory, like this:
Get-QADComputer | Get-SPInventory
In this case, the Get-QADComputer command (part of a free set of Active Directory management cmdlets you can get at quest.com/powershell) is returning an object that has a Name property, rather than returning simple string objects. To make that command work, I would have to tweak the function, shown in Figure 4; it's important for you to note that the changes are highlighted in red.
Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_OperatingSystem –comp 
    $_.Name | Select 
      CSName,BuildNumber,
        ServicePackMajorVersion
    $obj = New-Object PSObject
    $obj | Add-Member NoteProperty 
      BuildNumber ($wmi.BuildNumber)
    $obj | Add-Member NoteProperty CSName 
      ($wmi.CSName)
    $obj | Add-Member NoteProperty SPVersion  
      ($wmi.ServicePackMajorVersion)

    $wmi = Get-WmiObject Win32_BIOS –comp $_.Name | 
      Select SerialNumber
    $obj | Add-Member NoteProperty BIOSSerial 
      ($wmi.SerialNumber)
    Write-Output $obj
  }
}
Rather than passing the entire pipeline object to the –computerName parameter, my function is now acting on the pipeline object's Name property. This minor change is quite worthwhile in terms of flexibility—by using Get-QADComputer, I am able to limit my input to computers in a specific organizational unit, for example, or to only those computers that are matching other criteria based on Active Directory attributes.

What about Errors?
Inevitably, this function will eventually come across a computer that it can't connect to, it doesn't have permission to connect to, or so on. The function, as it is currently written, will display an error message in red text within the Windows PowerShell console window, and then continue on to the next computer.
That, of course, might be just what you want it to do. Or you might prefer the function to suppress those error messages. This is easy to do simply by adding this one line to the start of the function's PROCESS script block:
$ErrorActionPreference = "SilentlyContinue"
Then again, you might want to take a more complex approach and create an error log that lists the names of the computers that weren't reachable. Windows PowerShell can do that, but you'll have to wait until next month to see how to do this!
Cmdlet of the Month: Export-Alias And Import-Alias
Here's a great way to share custom aliases that you've created or to easily load your custom aliases each time the shell starts. After you create all the aliases you want, export them to a file, like this:
Export-Alias c:\aliases.xml
Then, to load those aliases back into the shell, run this command:
Import-Alias c:\aliases.xml
You can place that second command in your Windows PowerShell profile script to have it run each time the shell starts. Also, you can place the file on a network share in order to have it readily available to the other administrators you work with.

Making This Really Useful
So far, you may have been typing this function into a .ps1 file and running the script. That's all well and good, but it does have a few usability problems. One issue is that anytime you want to use the function you would have to open up that script file, modify the line that calls the function (to add whatever output, sorting, or other commands you need at that time), save the script, and run it. It would be much easier if the function itself were available right within the shell's console window, almost like a cmdlet.
There are two options for going about doing this. The first is to simply copy the function into a Windows PowerShell profile script, one of the four script files that the shell will automatically run (if they exist) each time it starts. The Quick Start Guide that is installed with Windows PowerShell lists the four locations.
I usually use the file that is named profile.ps1, which has to be located in the folder named Windows­PowerShell (no spaces) inside your Documents folder (or My Documents on Windows XP and Windows Server 2003 computers). Once the function is in your profile, it'll be available right from the command prompt anytime you're in the shell.
The second option is to include the function—just the function, with no other code—in a script and dot source that script into the shell. What typically happens when you run a script is Windows PowerShell creates a new scope for the script. Anything that goes on in the script takes place within that scope, such as the definition of functions. When the script finishes running, the scope is discarded and anything that went on within that scope is lost.
As a result of this, remember that if you have a script file that contains only the Get-SP­Inventory function, running that script will create a new scope, define the function, and discard the scope, and then the function will go away. Obviously, that's not very useful to you.
Dot sourcing, on the other hand, runs a script without creating a new scope. If I've put Get-SPInventory into a file named MyFunction.ps1, I can dot source it like this:
PS C:\> . c:\functions\myfunction
Note the lone period, followed by a space, that precedes the script's path and file name. That tells the shell to run the script in the current scope, meaning everything that happens in the script will stick around after the script finishes running. The result is that the Get-SPInventory script is now defined in the shell and you can use it right from the command line, almost as if it were a cmdlet.

Don Jones is a cofounder of Concentrated Technology and author of numerous IT books. Read his weekly Windows PowerShell tips or contact him at ConcentratedTech.com.

Page view tracker