The ability to use Windows PowerShell effectively relies on an understanding of how objects are utilized to move and manage data as it passes through the PowerShell pipeline. The pipeline provides a structure for creating complex scripts that are broken down into one or more simple commands, each performing a discrete action against the data as it passes through. Objects make it possible to hand off that data from one command to the next by bundling it into individual packages of related information.
PowerShell objects provide a consistent structure for working with different types of data, regardless of that data's source. In other words, the ways in which you manage one object's data are similar to those used to manage another object's data. Because of PowerShell's object-oriented nature, you're able to not only take advantage of the objects inherently generated by PowerShell's built-in cmdlets but also build your own objects based on classes in the Microsoft .NET Framework. Indeed, PowerShell's use of objects—and the flexibility they provide—is what makes PowerShell such an effective and formidable tool.
Understanding PowerShell's Object-Oriented Structure
The .NET Framework is a software-based structure that includes a large library of different types of classes. These classes serve as the foundation on which .NET objects are built and provide access to a variety of system, network, directory, and storage resources. PowerShell is built on specialized .NET classes that make it possible to access the entire .NET class library from within the PowerShell environment. You could say that objects provide the foundation on which PowerShell is built, which accounts for PowerShell's flexible and powerful nature.
Built into the PowerShell environment is a collection of cmdlets. Each cmdlet carries out a specific operation, such as retrieving a list of files in a folder or managing a service running on a computer. To carry out such an operation, the cmdlet generates an object or set of objects based on the specialized PowerShell classes. Objects provide the vehicles by which data is passed down the pipeline, where it can be used by other commands.
You can think of each object as a package of related information. For example, an object might contain the data necessary to describe a file: its name, size, location, and other attributes. To work with an object's data, you call its members, which are components that let you access and manipulate that information. A PowerShell object supports several types of members, but the two most common are properties and methods. A property is a named data value that describes the "thing" being represented by the object, such as the size of a file or the date it was created. Methods are actions that you can take related to the object's data, such as deleting or moving a file.
Working with PowerShell Objects
To better understand how PowerShell objects work, let's look at an example. One of the cmdlets that PowerShell supports, Get-Service, returns a list of services installed on a computer. When specified without any parameters, the cmdlet provides details about the services on the local computer.
Each service returned by the Get-Service cmdlet is an object based on the .NET class System.ServiceProcess.ServiceController. Like most objects, ServiceController contains numerous properties and methods that you can use to access the data contained within that object or run operations against the data. For example, the ServiceController object includes the Name and DisplayName properties. The data value associated with the Name property provides the service's actual name. The data value associated with the DisplayName property provides the display name used for that service. The ServiceController object also includes a number of methods. For instance, you can use the Start method to launch the service represented by the object or use the Stop method to stop that service.
You're not expected to know every property, method, or other member type associated with an object. PowerShell provides the Get-Member cmdlet, a handy tool for accessing details about each member supported by an object. You can use this cmdlet to retrieve information about all the members or certain member types. For details about all the members, you first specify the cmdlet you want to learn about and then specify the Get-Member cmdlet. You need to separate the two cmdlets with a vertical bar (pipe), like this:
In this case, the command is piping the objects returned by the Get-Service cmdlet to the Get-Member cmdlet. When you run the command, you receive the results shown in Figure 1.
As you can see, the list contains each member's name, member type, and definition, which might or might not make sense, depending on your programming background. In any case, you can see that the ServiceController object supports a number of members, mostly methods and properties. (You can find details about the different member types in the MSDN article "PSMemberTypes Enumeration.")
Notice that the results returned by the Get-Member cmdlet begin by listing the type name of the class on which the object is based—in this case, System.ServiceProcess.ServiceController. Having this information can be useful if you're familiar with .NET classes and want to better understand the operations you're trying to perform. You can also use it to make sure that you're working with the type of object with which you want to be working. This leads to another aspect of the information returned by the Get-Member cmdlet. As you might have noticed, the results include details about only a single object, even though the Get-Service cmdlet returns an object for each service. When the Get-Member cmdlet sees that multiple objects of the same type are being returned, it includes only one instance of those objects to avoid redundancy. Another way to look at this is to say that it returns only the class on which all these objects are based.
However, if a command returns more than one type of object, Get-Member returns information on each of those types. For example, if you run the Get-ChildItem cmdlet against a directory that contains both files and folders, Get-Member will return details about the System.IO.DirectoryInfo class and the System.IO.FileInfo class. However, if the directory contains only files, Get-Member returns details about System.IO.FileInfo only. So, when you use Get-Member, you should make sure that you're viewing the object types you want to be viewing.
The Get-Member cmdlet also lets you view a list made up of a specific member type, such as properties or methods. To do so, you need to include the -MemberType parameter, followed by the type name. For example, the following command specifies that only the object's properties be returned:
Being able to retrieve the details about an object's members—whether all members or a specific type of member—makes it easier to access specific information within that object. For example, you now know that the ServiceController class supports the Status and ServiceType properties. You can use that information within your commands to refine your operations. For instance, the following command uses these properties with the Where-Object cmdlet to filter the results:
-and $_.ServiceType -eq "Win32OwnProcess"}
In this command, the Get-Service results (i.e., the set of ServiceController objects) are piped to the Where-Object cmdlet. For each object passed to the Where-Object cmdlet, you can access that object's properties in order to create the filters. To do so, you first specify the $_ symbol—a system variable that points to the current object in the pipeline—followed by a period and the name of the property. You can then use those properties to return specific data by defining Boolean expressions that evaluate to either true or false. In this case, you first specify that the Status property must equal Running and the ServiceType property must equal Win32OwnProcess in order for an object's data to be returned. Notice that the -eq comparison operator is used for the equal operator and the -and operator is used to join the two expressions. As a result, both conditions must evaluate to true in order for the object to be returned. Also notice the back tick (`) on the first line. This indicates that the code continues on the next line.
Figure 2 shows the results returned by the command.
These are the services running on my system that meet the Where-Object requirements—that is, the services are running and have a service type of Win32OwnProcess. (Note that I'm running PowerShell in a Windows 7 virtual environment.)
Knowing the names of the properties supported by the ServiceController object makes it much easier to find the exact information for which you're looking. You can take this a step further by tagging on the Format-Table cmdlet so that the results are more readable:
-and $_.ServiceType -eq "Win32OwnProcess"} |
Although the original example had already output the information in a tabular format, the Format-Table cmdlet lets you include the -Autosize parameter, which prevents data from being truncated, as shown in Figure 3.
Now let's explore another advantage of being able to view an object's members. Let's start by returning information about a specific service, MsDtsServer110, which is the service used to run SQL Server Integration Services (SSIS). To get its information, you use the Get-Service cmdlet's -Name parameter to specify the service name:
By piping the Get-Service cmdlet's results to the Format-List cmdlet, you can easily see the various property values associated with this ServiceController object, as shown in Figure 4.
PowerShell also lets you access a specific property value within an object. For instance, suppose you only need to know the value of the Status property associated with the MsDtsServer110 service. One way you can do this is to specify the Format-Wide cmdlet along with its -Property parameter:
Format-Wide -Property Status
In this case, the command returns only the value Stopped, which is the value associated with the service's Status property. Alternatively, you can access the ServiceController object and its property directly:
Notice that the Get-Service command is enclosed in parentheses. This is necessary because, without them, PowerShell would treat MsDtsServer110.Status as the complete name of the service rather than the service name followed by the Status property. The parentheses force PowerShell to first execute the command, which generates a ServiceController object. PowerShell then retrieves the Status property from that object. Once again, the command returns the value Stopped.
Another way you can achieve the same result is to assign the outputted ServiceController object to a variable, then use that variable to call the property value:
As you can see, the $ssis variable is defined and the output of the Get-Service command is assigned as its value. In this case, you don't have to enclose the Get-Service command in parentheses. No properties are being called at this point, so there's no confusion as to what you're assigning to the variable. The command's job is to produce a ServiceController object, and that object becomes the variable's value. You can then use the $ssis variable to reference the ServiceController object's Status property value by adding a period and the property name. Like the two other commands, this command returns the value Stopped.
You're not limited to only the Status property. Using the $ssis variable, you can access any of the other property values. For instance, the following command uses the $ssis variable to access the DisplayName property:
This command returns the value SQL Server Integration Services 11.0.
It's important to note that assigning an object to a variable this way locks the data at that point in time. For example, if the service's status should change, the variable's Status property wouldn't reflect the changed state because the original data had been assigned to that object.
Even so, this doesn't prevent you from using the variable's methods to take actions against the service. But before you do that, let's view a list of the available methods. To do so, pipe the $ssis variable's contents to the Get-Member cmdlet, specifying Method as the member type:
Not surprisingly, the command returns a list of methods associated with the ServiceController object—in this case, the object generated for the MsDtsServer110 service. You can then use the $ssis variable to call any of these methods. For example, the following command starts the MsDtsServer110 service:
Note that when you call a method, you have to include the parentheses at the end, even if you're not passing any parameters to the method. Once the service has started, you can just as easily stop it:
As you can see, understanding how to work with the objects that the cmdlets generate can help you use PowerShell more effectively. However, PowerShell doesn't provide a cmdlet for every .NET class, yet there might be times when you want to use one of those classes to perform a particular operation. In such cases, you can create your own objects based on those classes.
Creating a .NET Object in PowerShell
The .NET Framework and its class library provide a comprehensive set of classes for carrying out a wide range of operations. Although PowerShell's built-in set of cmdlets go a long way in accessing those classes, not all relevant operations are addressed. For that reason, PowerShell includes the New-Object cmdlet so that you can create your own objects.
It's important to note that in addition to creating objects based on .NET classes, PowerShell lets you create modules, .NET classes (types), and Component Object Model (COM) objects. A discussion of these object types is beyond the scope of this article, but you should know that PowerShell can be extended far beyond what I'm showing you here.
To create an object based on a .NET class, you need to specify the New-Object cmdlet, followed by the -TypeName parameter and the name of the class. In most cases, it's easiest to assign the new object to a variable, then use that variable to access the object's members. The following example creates an object based on the .NET class System.Net.NetworkInformation.Ping and assigns the object to the $ping variable:
Notice that you don't need to specify System when you create the object. PowerShell knows where to look for the class.
That's all there is to creating the object. You can then access the object's members by using the $ping variable. In this case, to view the object's members, you need to pipe the variable's contents to the Get-Member cmdlet:
As you can see in Figure 5, the results show the object's members, as returned by the Get-Member cmdlet.
As you would expect, the object is based on the System.Net.NetworkInformation.Ping class. Notice that the class supports a number of methods, one of which is Send. Let's take a closer look at that method by using the Get-Member cmdlet to return more detailed information:
As you can see, you use the Get-Member cmdlet with the -Name parameter followed by the Send method. You then pipe the information retrieved by Get-Member to the Format-List cmdlet, which returns the results shown in Figure 6.
Although the definition is quite extensive, it's essentially saying that the method lets you ping a specific website or IP address, as indicated by the part that reads Send(string hostNameOrAddress). The following example uses the Send method to ping google.com:
In this case, the Send method returns the results shown in Figure 7.
Although this is a very simple example of what you can do with objects created from .NET classes, it demonstrates that you don't need to let cmdlet limitations prevent you from getting at the information you need. It helps to have knowledge about the various classes available in .NET, but you certainly don't need to be an expert in .NET development to take advantage of this powerful feature in PowerShell.
PowerShell's Object-Centric World
Objects are at the root of all PowerShell operations. The more you understand how objects work within the PowerShell environment, the better you'll be able to take advantage of PowerShell's flexibility and perform the variety of tasks that PowerShell supports. Not only can you use the PowerShell cmdlets to their fullest, but you can also create your own objects so you can push even deeper into your systems and network. And what you've seen here is only glimpse of what you can do with objects in PowerShell. Objects give you inroads into the entire .NET Framework and well beyond.