Home > Articles > Home & Office Computing > Microsoft Windows Desktop

This chapter is from the book

This chapter is from the book

Object Based

Most shells operate in a text-based environment, which means you typically have to manipulate the output for automation purposes. For example, if you need to pipe data from one command to the next, the output from the first command usually must be reformatted to meet the second command's requirements. Although this method has worked for years, dealing with text-based data can be difficult and frustrating.

Often, a lot of work is necessary to transform text data into a usable format. Microsoft has set out to change the standard with PowerShell, however. Instead of transporting data as plain text, PowerShell retrieves data in the form of .NET Framework objects, which makes it possible for commands (cmdlets) to access object properties and methods directly. This change has simplified shell use. Instead of modifying text data, you can just refer to the required data by name. Similarly, instead of writing code to transform data into a usable format, you can simply refer to objects and manipulate them as needed.

Understanding the Pipeline

The use of objects gives you a more robust method for dealing with data. In the past, data was transferred from one command to the next by using the pipeline, which makes it possible to string a series of commands together to gather information from a system. However, as mentioned previously, most shells have a major disadvantage: The information gathered from commands is text based. Raw text needs to be parsed (transformed) into a format the next command can understand before being piped. To see how parsing works, take a look at the following Bash example:

$ ps -ef | grep "bash" | cut -f2

The goal is to get the process ID (PID) for the bash process. A list of currently running processes is gathered with the ps command and then piped to the grep command and filtered on the string "bash". Next, the remaining information is piped to the cut command, which returns the second field containing the PID based on a tab delimiter.

Based on the man information for the grep and cut commands, it seems as though the ps command should work. However, the PID isn't returned or displayed in the correct format.

The command doesn't work because the Bash shell requires you to manipulate text data to display the PID. The output of the ps command is text based, so transforming the text into a more usable format requires a series of other commands, such as grep and cut. Manipulating text data makes this task more complicated. For example, to retrieve the PID from the data piped from the grep command, you need to provide the field location and the delimiter for separating text information to the cut command. To find this information, run the first part of the ps command:

$ ps -ef | grep "bash"
   bob    3628       1 con  16:52:46 /usr/bin/bash

The field you need is the second one (3628). Notice that the ps command doesn't use a tab delimiter to separate columns in the output; instead, it uses a variable number of spaces or a whitespace delimiter, between fields.

The cut command has no way to tell that spaces should be used as a field separator, which is why the command doesn't work. To get the PID, you need to use the awk scripting language. The command and output in that language would look like this:

$ ps -ef | grep "bash" | awk '{print $2}'

The point is that although most UNIX and Linux shell commands are powerful, using them can be complicated and frustrating. Because these shells are text-based, often commands lack functionality or require using additional commands or tools to perform tasks. To address the differences in text output from shell commands, many utilities and scripting languages have been developed to parse text.

The result of all this parsing is a tree of commands and tools that make working with shells unwieldy and time consuming, which is one reason for the proliferation of management interfaces that rely on GUIs. This trend can be seen among tools Windows administrators use, too; as Microsoft has focused on enhancing the management GUI at the expense of the CLI.

Windows administrators now have access to the same automation capabilities as their UNIX and Linux counterparts. However, PowerShell and its use of objects fill the automation need Windows administrators have had since the days of batch scripting and WSH in a more usable and less parsing intense manner. To see how the PowerShell pipeline works, take a look at the following PowerShell example:

PS C:\> get-process bash | format-table id -autosize


PS C:\>

Like the Bash example, the goal of this PowerShell example is to display the PID for the bash process. First, information about the bash process is gathered by using the Get-Process cmdlet. Second, the information is piped to the Format-Table cmdlet, which returns a table containing only the PID for the bash process.

The Bash example requires complex shell scripting, but the PowerShell example simply requires formatting a table. As you can see, the structure of PowerShell cmdlets is much easier to understand and use.

Now that you have the PID for the bash process, take a look at the following example, which shows how to kill (stop) that process:

PS C:\> get-process bash | stop-process
PS C:\>

.NET Framework Tips

Before continuing, you need to know a few points about how PowerShell interacts with the .NET Framework. This information is critical to understanding the scripts you review in later chapters.

New-Object cmdlet

You use the New-Object cmdlet to create an instance of a .NET object. To do this, you simply provide the fully qualified name of the .NET class you want to use, as shown:

PS C:\> $Ping = new-object Net.NetworkInformation.Ping
PS C:\>

By using the New-Object cmdlet, you now have an instance of the Ping class that enables you to detect whether a remote computer can be reached via Internet Control Message Protocol (ICMP). Therefore, you have an object-based version of the Ping.exe command-line tool.

If you're wondering what the replacement is for the VBScript CreateObject method, it's the New-Object cmdlet. You can also use the comObject switch with this cmdlet to create a COM object, simply by specifying the object's programmatic identifier (ProgID), as shown here:

PS C:\> $IE = new-object -comObject InternetExplorer.Application
PS C:\> $IE.Visible=$True
PS C:\> $IE.Navigate("www.cnn.com")
PS C:\>

Square Brackets

Throughout this book, you'll notice the use of square brackets ([ and ]), which indicate that the enclosed term is a .NET Framework reference. These references can be one of the following:

  • A fully qualified class name[System.DirectoryServices.ActiveDirectory.Forest], for example
  • A class in the System namespace[string], [int], [boolean], and so forth
  • A type accelerator[ADSI], [WMI], [Regex], and so on

Defining a variable is a good example of when to use a .NET Framework reference. In this case, the variable is assigned an enumeration value by using an explicit cast of a .NET class, as shown in this example:

PS C:\> $SomeNumber = [int]1
PS C:\> $Identity = [System.Security.Principal.NTAccount]"Administrator"
PS C:\>

If an enumeration can consist of only a fixed set of constants, and you don't know these constants, you can use the System.Enum class's GetNames method to find this information:

PS C:\>
PS C:\>

Static Classes and Methods

Square brackets are used not only for defining variables, but also for using or calling static members of a .NET class. To do this, just use a double colon (::) between the class name and the static method or property, as shown in this example:

PS C:\> [System.DirectoryServices.ActiveDirectory.Forest]::

Name                  : taosage.internal
Sites                 : {HOME}
Domains               : {taosage.internal}
GlobalCatalogs        : {sol.taosage.internal}
ApplicationPartitions : {DC=DomainDnsZones,DC=taosage,DC=internal,
ForestMode            : Windows2003Forest
RootDomain            : taosage.internal
Schema                :
SchemaRoleOwner       : sol.taosage.internal
NamingRoleOwner       : sol.taosage.internal

PS C:\>


Reflection is a feature in the .NET Framework that enables developers to examine objects and retrieve their supported methods, properties, fields, and so on. Because PowerShell is built on the .NET Framework, it provides this feature, too, with the Get-Member cmdlet. This cmdlet analyzes an object or collection of objects you pass to it via the pipeline. For example, the following command analyzes the objects returned from the Get-Process cmdlet and displays their associated properties and methods:

PS C:\> get-process | get-member

Developers often refer to this process as "interrogating" an object. It's a faster way to get information about objects than using the Get-Help cmdlet (which at the time of this writing provides limited information), reading the MSDN documentation, or searching the Internet.

PS C:\> get-process | get-member

   TypeName: System.Diagnostics.Process

Name                          MemberType     Definition
----                          ----------     ----------
Handles                       AliasProperty  Handles = Handlecount
Name                          AliasProperty  Name = ProcessName
NPM                           AliasProperty  NPM = NonpagedSystemMemorySize
PM                            AliasProperty  PM = PagedMemorySize
VM                            AliasProperty  VM = VirtualMemorySize
WS                            AliasProperty  WS = WorkingSet
add_Disposed                  Method         System.Void add_Disposed(Event...
add_ErrorDataReceived         Method         System.Void add_ErrorDataRecei...
add_Exited                    Method         System.Void add_Exited(EventHa...
add_OutputDataReceived        Method         System.Void add_OutputDataRece...
BeginErrorReadLine            Method         System.Void BeginErrorReadLine()
BeginOutputReadLine           Method         System.Void BeginOutputReadLine()
CancelErrorRead               Method         System.Void CancelErrorRead()
CancelOutputRead              Method         System.Void CancelOutputRead()
Close                         Method         System.Void Close()
CloseMainWindow               Method         System.Boolean CloseMainWindow()
CreateObjRef                  Method         System.Runtime.Remoting.ObjRef...
Dispose                       Method         System.Void Dispose()
Equals                        Method         System.Boolean Equals(Object obj)
get_BasePriority              Method         System.Int32 get_BasePriority()
get_Container                 Method         System.ComponentModel.IContain...
get_EnableRaisingEvents       Method         System.Boolean get_EnableRaisi...
__NounName                    NoteProperty   System.String __NounName=Process
BasePriority                  Property       System.Int32 BasePriority {get;}
Container                     Property       System.ComponentModel.IContain...
EnableRaisingEvents           Property       System.Boolean EnableRaisingEv...
ExitCode                      Property       System.Int32 ExitCode {get;}
ExitTime                      Property       System.DateTime ExitTime {get;}
Handle                        Property       System.IntPtr Handle {get;}
HandleCount                   Property       System.Int32 HandleCount {get;}
HasExited                     Property       System.Boolean HasExited {get;}
Id                            Property       System.Int32 Id {get;}
MachineName                   Property       System.String MachineName {get;}
MainModule                    Property       System.Diagnostics.ProcessModu...
MainWindowHandle              Property       System.IntPtr MainWindowHandle...
MainWindowTitle               Property       System.String MainWindowTitle ...
MaxWorkingSet                 Property       System.IntPtr MaxWorkingSet {g...
MinWorkingSet                 Property       System.IntPtr MinWorkingSet {g...
Company                       ScriptProperty System.Object Company {get=$th...
CPU                           ScriptProperty System.Object CPU {get=$this.T...
Description                   ScriptProperty System.Object Description {get...
FileVersion                   ScriptProperty System.Object FileVersion {get...
Path                          ScriptProperty System.Object Path {get=$this....
Product                       ScriptProperty System.Object Product {get=$th...
ProductVersion                ScriptProperty System.Object ProductVersion {...

PS C:\>

This example shows that objects returned from the Get-Process cmdlet have additional property information that you didn't know. The following example uses this information to produce a report about Microsoft-owned processes and their folder locations. An example of such a report would be as follows:

PS C:\> get-process | where-object {$_.Company -match ".*Microsoft*"} |
format-table Name, ID, Path -Autosize

Name         Id Path
----         -- ----
ctfmon     4052 C:\WINDOWS\system32\ctfmon.exe
explorer   3024 C:\WINDOWS\Explorer.EXE
iexplore   2468 C:\Program Files\Internet Explorer\iexplore.exe
iexplore   3936 C:\Program Files\Internet Explorer\iexplore.exe
mobsync     280 C:\WINDOWS\system32\mobsync.exe
notepad    1600 C:\WINDOWS\system32\notepad.exe
notepad    2308 C:\WINDOWS\system32\notepad.exe
notepad    2476 C:\WINDOWS\system32\NOTEPAD.EXE
notepad    2584 C:\WINDOWS\system32\notepad.exe
OUTLOOK    3600 C:\Program Files\Microsoft Office\OFFICE11\OUTLOOK.EXE
powershell 3804 C:\Program Files\Windows PowerShell\v1.0\powershell.exe
WINWORD    2924 C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE

PS C:\>

You wouldn't get nearly this much process information by using WSH with only a single line of code.

The Get-Member cmdlet isn't just for objects generated from PowerShell cmdlets. You can also use it on objects initialized from .NET classes, as shown in this example:

PS C:\> new-object System.DirectoryServices.DirectorySearcher

The goal of using the DirectorySearcher class is to retrieve user information from Active Directory, but you don't know what methods the returned objects support. To retrieve this information, run the Get-Member cmdlet against a variable containing the mystery objects, as shown in this example.

PS C:\> $Searcher = new-object System.DirectoryServices.DirectorySearcher
PS C:\> $Searcher | get-member

   TypeName: System.DirectoryServices.DirectorySearcher

Name                        MemberType Definition
----                        ---------- ----------
add_Disposed                Method     System.Void add_Disposed(EventHandle...
CreateObjRef                Method     System.Runtime.Remoting.ObjRef Creat...
Dispose                     Method     System.Void Dispose()
Equals                      Method     System.Boolean Equals(Object obj)
FindAll                     Method     System.DirectoryServices.SearchResul... 
FindOne                     Method     System.DirectoryServices.SearchResul...
Asynchronous                Property   System.Boolean Asynchronous {get;set;}
AttributeScopeQuery         Property   System.String AttributeScopeQuery {g...
CacheResults                Property   System.Boolean CacheResults {get;set;}
ClientTimeout               Property   System.TimeSpan ClientTimeout {get;s...
Container                   Property   System.ComponentModel.IContainer Con...
DerefAlias                  Property   System.DirectoryServices.Dereference...
DirectorySynchronization    Property   System.DirectoryServices.DirectorySy...
ExtendedDN                  Property   System.DirectoryServices.ExtendedDN ...
Filter                      Property   System.String Filter {get;set;}         
PageSize                    Property   System.Int32 PageSize {get;set;}
PropertiesToLoad            Property   System.Collections.Specialized.Strin...
PropertyNamesOnly           Property   System.Boolean PropertyNamesOnly {ge...
ReferralChasing             Property   System.DirectoryServices.ReferralCha...
SearchRoot                  Property   System.DirectoryServices.DirectoryEn...
SearchScope                 Property   System.DirectoryServices.SearchScope...
SecurityMasks               Property   System.DirectoryServices.SecurityMas...
ServerPageTimeLimit         Property   System.TimeSpan ServerPageTimeLimit ...
ServerTimeLimit             Property   System.TimeSpan ServerTimeLimit {get...
Site                        Property   System.ComponentModel.ISite Site {ge...
SizeLimit                   Property   System.Int32 SizeLimit {get;set;}
Sort                        Property   System.DirectoryServices.SortOption ...
Tombstone                   Property   System.Boolean Tombstone {get;set;}
VirtualListView             Property   System.DirectoryServices.

PS C:\>

Notice the FindAll method and the Filter property. These are object attributes that can be used to search for information about users in an Active Directory domain. To use these attributes the first step is to filter the information returned from DirectorySearcher by using the Filter property, which takes a filter statement similar to what you'd find in a Lightweight Directory Access Protocol (LDAP) statement:

PS C:\> $Searcher.Filter = ("(objectCategory=user)")

Next, you retrieve all users from the Active Directory domain with the FindAll method:

PS C:\> $Users = $Searcher.FindAll()

At this point, the $Users variable contains a collection of objects holding the distinguished names for all users in the Active Directory domain:

PS C:\> $Users

Path                                    Properties
----                                    ----------
LDAP://CN=Administrator,CN=Users,DC=... {homemdb, samaccounttype, countrycod...
LDAP://CN=Guest,CN=Users,DC=taosage,... {samaccounttype, objectsid, whencrea...
LDAP://CN=krbtgt,CN=Users,DC=taosage... {samaccounttype, objectsid, whencrea...
LDAP://CN=admintyson,OU=Admin Accoun... {countrycode, cn, lastlogoff, usncre...
LDAP://CN=servmom,OU=Service Account... {samaccounttype, lastlogontimestamp,...
LDAP://CN=SUPPORT_388945a0,CN=Users,... {samaccounttype, objectsid, whencrea...
LDAP://CN=Tyson,OU=Acc... {msmqsigncertificates, distinguished...
LDAP://CN=Maiko,OU=Acc... {homemdb, msexchhomeservername, coun...
LDAP://CN=servftp,OU=Service Account... {samaccounttype, lastlogontimestamp,...
LDAP://CN=Erica,OU=Accounts,OU... {samaccounttype, lastlogontimestamp,...
LDAP://CN=Garett,OU=Accou... {samaccounttype, lastlogontimestamp,...
LDAP://CN=Fujio,OU=Accounts,O... {samaccounttype, givenname, sn, when...
LDAP://CN=Kiyomi,OU=Accounts,... {samaccounttype, givenname, sn, when...
LDAP://CN=servsql,OU=Service Account... {samaccounttype, lastlogon, lastlogo...
LDAP://CN=servdhcp,OU=Service Accoun... {samaccounttype, lastlogon, lastlogo...
LDAP://CN=servrms,OU=Service Account... {lastlogon, lastlogontimestamp, msmq...

PS C:\>

Now that you have an object for each user, you can use the Get-Member cmdlet to learn what you can do with these objects:

PS C:\> $Users | get-member

   TypeName: System.DirectoryServices.SearchResult

Name              MemberType Definition
----              ---------- ----------
Equals            Method     System.Boolean Equals(Object obj)
get_Path          Method     System.String get_Path()
get_Properties    Method     System.DirectoryServices.ResultPropertyCollecti...
GetDirectoryEntry Method     System.DirectoryServices.DirectoryEntry GetDire... 
GetHashCode       Method     System.Int32 GetHashCode()
GetType           Method     System.Type GetType()
ToString          Method     System.String ToString()
Path              Property   System.String Path {get;}
Properties        Property   System.DirectoryServices.ResultPropertyCollecti...

PS C:\>

To collect information from these user objects, it seems as though you need to step through each object with the GetDirectoryEntry method. To determine what data you can retrieve from these objects, you use the Get-Member cmdlet again, as shown here:

PS C:\> $Users[0].GetDirectoryEntry() | get-member -MemberType Property

   TypeName: System.DirectoryServices.DirectoryEntry

Name                            MemberType Definition
----                            ---------- ----------
accountExpires                  Property   System.DirectoryServices.Property...
adminCount                      Property   System.DirectoryServices.Property...
badPasswordTime                 Property   System.DirectoryServices.Property...
badPwdCount                     Property   System.DirectoryServices.Property...
cn                              Property   System.DirectoryServices.Property...
codePage                        Property   System.DirectoryServices.Property...
countryCode                     Property   System.DirectoryServices.Property...
description                     Property   System.DirectoryServices.Property...
displayName                     Property   System.DirectoryServices.Property...
distinguishedName               Property   System.DirectoryServices.Property...
homeMDB                         Property   System.DirectoryServices.Property...
homeMTA                         Property   System.DirectoryServices.Property...
instanceType                    Property   System.DirectoryServices.Property...
isCriticalSystemObject          Property   System.DirectoryServices.Property...
lastLogon                       Property   System.DirectoryServices.Property...
lastLogonTimestamp              Property   System.DirectoryServices.Property...
legacyExchangeDN                Property   System.DirectoryServices.Property...
logonCount                      Property   System.DirectoryServices.Property...
mail                            Property   System.DirectoryServices.Property...
mailNickname                    Property   System.DirectoryServices.Property...
mDBUseDefaults                  Property   System.DirectoryServices.Property...
memberOf                        Property   System.DirectoryServices.Property...
msExchALObjectVersion           Property   System.DirectoryServices.Property...
msExchHomeServerName            Property   System.DirectoryServices.Property...
msExchMailboxGuid               Property   System.DirectoryServices.Property...
msExchMailboxSecurityDescriptor Property   System.DirectoryServices.Property...
msExchPoliciesIncluded          Property   System.DirectoryServices.Property...
msExchUserAccountControl        Property   System.DirectoryServices.Property...
mSMQDigests                     Property   System.DirectoryServices.Property...
mSMQSignCertificates            Property   System.DirectoryServices.Property...
name                            Property   System.DirectoryServices.Property...
nTSecurityDescriptor            Property   System.DirectoryServices.Property...
objectCategory                  Property   System.DirectoryServices.Property...
objectClass                     Property   System.DirectoryServices.Property...
objectGUID                      Property   System.DirectoryServices.Property...
objectSid                       Property   System.DirectoryServices.Property...
primaryGroupID                  Property   System.DirectoryServices.Property...
proxyAddresses                  Property   System.DirectoryServices.Property...
pwdLastSet                      Property   System.DirectoryServices.Property...
sAMAccountName                  Property   System.DirectoryServices.Property...
sAMAccountType                  Property   System.DirectoryServices.Property...
showInAddressBook               Property   System.DirectoryServices.Property...
textEncodedORAddress            Property   System.DirectoryServices.Property...
userAccountControl              Property   System.DirectoryServices.Property...
uSNChanged                      Property   System.DirectoryServices.Property...
uSNCreated                      Property   System.DirectoryServices.Property...
whenChanged                     Property   System.DirectoryServices.Property...
whenCreated                     Property   System.DirectoryServices.Property...

PS C:\>

To use PowerShell effectively, you should make sure you're familiar with the Get-Member cmdlet. If you don't understand how it works, figuring out what an object can and can't do may be at times difficult.

Now that you understand how to pull information from Active Directory, it's time to put together all the commands used so far:

PS C:\> $Searcher = new-object System.DirectoryServices.DirectorySearcher
PS C:\> $Searcher.Filter = ("(objectCategory=user)")
PS C:\> $Users = $Searcher.FindAll()
PS C:\> foreach ($User in $Users){$User.GetDirectoryEntry().sAMAccountName}
PS C:\>

Although the list of users in this domain isn't long, it shows that you can interrogate a set of objects to understand their capabilities.

The same is true for static classes, however, when attempting to use the Get-Member cmdlet in the same manner as before creates the following error:

PS C:\> new-object System.Net.Dns
New-Object : Constructor not found. Cannot find an appropriate constructor for
type System.Net.Dns.
At line:1 char:11
+ New-Object  <<<< System.Net.Dns
PS C:\>

As you can see, the System.Net.Dns class doesn't have a constructor, which poses a challenge when you're trying to find out what this class does. However, the Get-Member cmdlet can handle this challenge. With the Static parameter, you can gather information from static classes, as shown in this example:

PS C:\> [System.Net.Dns] | get-member -Static

   TypeName: System.Net.Dns

Name                  MemberType Definition
----                  ---------- ----------
BeginGetHostAddresses Method     static System.IAsyncResult BeginGetHostAddr...
BeginGetHostByName    Method     static System.IAsyncResult BeginGetHostByNa...
BeginGetHostEntry     Method     static System.IAsyncResult BeginGetHostEntr...
BeginResolve          Method     static System.IAsyncResult BeginResolve(Str...
EndGetHostAddresses   Method     static System.Net.IPAddress[] EndGetHostAdd...
EndGetHostByName      Method     static System.Net.IPHostEntry EndGetHostByN...
EndGetHostEntry       Method     static System.Net.IPHostEntry EndGetHostEnt...
EndResolve            Method     static System.Net.IPHostEntry EndResolve(IA...
Equals                Method     static System.Boolean Equals(Object objA, O...
GetHostAddresses      Method     static System.Net.IPAddress[] GetHostAddres...
GetHostByAddress      Method     static System.Net.IPHostEntry GetHostByAddr...
GetHostByName         Method     static System.Net.IPHostEntry GetHostByName...
GetHostEntry          Method     static System.Net.IPHostEntry GetHostEntry(...
GetHostName           Method     static System.String GetHostName()
ReferenceEquals       Method     static System.Boolean ReferenceEquals(Objec...
Resolve               Method     static System.Net.IPHostEntry Resolve(Strin...

PS C:\>

Now that you have information about the System.Net.Dns class, you can put it to work. As an example, use the GetHostAddress method to resolve the IP address for the Web site www.digg.com:

PS C:\> [System.Net.Dns]::GetHostAddresses("www.digg.com")

IPAddressToString :
Address           : 516669248
AddressFamily     : InterNetwork
ScopeId           :
IsIPv6Multicast   : False
IsIPv6LinkLocal   : False
IsIPv6SiteLocal   : False

PS C:\>

Extended Type System (ETS)

You might think that scripting in PowerShell is typeless because you rarely need to specify the type for a variable. PowerShell is actually type driven, however, because it interfaces with different types of objects from the less than perfect .NET to Windows Management Instrumentation (WMI), Component Object Model (COM), ActiveX Data Objects (ADO), Active Directory Service Interfaces (ADSI), Extensible Markup Language (XML), and even custom objects. However, you typically don't need to be concerned about object types because PowerShell adapts to different object types and displays its interpretation of an object for you.

In a sense, PowerShell tries to provide a common abstraction layer that makes all object interaction consistent, despite the type. This abstraction layer is called the PSObject, a common object used for all object access in PowerShell. It can encapsulate any base object (.NET, custom, and so on), any instance members, and implicit or explicit access to adapted and type-based extended members, depending on the type of base object. Furthermore, it can state its type and add members dynamically. To do this, PowerShell uses the Extended Type System (ETS), which provides an interface that allows PowerShell cmdlet and script developers to manipulate and change objects as needed.

Therefore, with ETS, you can change objects by adapting their structure to your requirements or create new ones. One way to manipulate objects is to adapt (extend) existing object types or create new object types. To do this, you define custom types in a custom types file, based on the structure of the default types file, Types.ps1xml.

In the Types.ps1xml file, all types are contained in a <Type></Type> node, and each type can contain standard members, data members, and object methods. Using this structure as a basis, you can create your own custom types file and load it into a PowerShell session by using the Update-TypeData cmdlet, as shown here:

PS C:\> Update-TypeData D:\PS\My.Types.Ps1xml

You can run this command manually during each PowerShell session or add it to your profile.ps1 file.

The second way to manipulate an object's structure is to use the Add-Member cmdlet to add a user-defined member to an existing object instance, as shown in this example:

PS C:\> $Procs = get-process
PS C:\> $Procs | add-member -Type scriptProperty "TotalDays" {
>> $Date = get-date
>> $Date.Subtract($This.StartTime).TotalDays}
PS C:\>

This code creates a scriptProperty member called TotalDays for the collection of objects in the $Procs variable. The scriptProperty member can then be called like any other member for those objects, as shown in the next example:

PS C:\> $Procs | where {$_.name -Match "WINWORD"} | ft Name,
TotalDays -AutoSize

Name             TotalDays
----             ---------
WINWORD 5.1238899696898148

PS C:\>

Although the new scriptProperty member isn't particularly useful, it does demonstrate how to extend an object. Being able to extend objects from both a scripting and cmdlet development context is extremely useful.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information

To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.


Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.


If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information

Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.


This site is not directed to children under the age of 13.


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information

If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information

Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents

California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure

Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact

Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice

We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020