PowerShell is a powerful language. But what makes this scripting language so powerful? PowerShell objects. What are these magical objects and how does PowerShell work with them? Stay tuned to find out.
Not a reader? Watch this related video tutorial!PowerShell is an object-oriented language and shell. This is a departure from the traditional shells like cmd and Bash. These traditional shells focused on text aka strings and while still useful, are limited in their capabilities. Nearly everything in PowerShell is an object.
In this article, you’ll learn some key concepts when it comes to objects in PowerShell. By the end of this article, you’ll have learned how to apply this knowledge to your own scripting through helpful example code and visualizations.
If you’re more of a visual learner, feel free to watch this article’s companion video below.
So buckle up! You’re in for a memorable experience that will help you master the concept of objects in PowerShell!
Prerequisites
In this article, you’re going to learn about objects in PowerShell via a hands-on approach. If you choose to follow along and try the code examples, Windows PowerShell 5.1 or any version of PowerShell 6+ should work. However, all examples you see will be performed on Windows 10 build 1903 with Windows PowerShell 5.1.
Understanding the Anatomy of an Object
Objects are everywhere in PowerShell. You might be asking yourself “What does an object look like?” In this first section, you’ll get an overview of what an object consists of. Once you get a broad view of what makes an object an object, you’ll then get to dive into some code examples!
Discovering Object Members with Get-Member
Objects have many different types of information associated with them. In PowerShell, this information is sometimes called members. An object member is a generic term that refers to all information associated with an object.
To discover information about an object (members), you can use the Get-Member
cmdlet. The Get-Member
cmdlet is a handy command that allows you find available properties, methods and so on for any object in PowerShell.
For example, let’s say you want to view members for a particular object returned via the Get-Service
cmdlet. You can do so by piping the output of the Get-Service
command to the Get-Member
cmdlet as see below.
Get-Service -ServiceName 'BITS' | Get-Member
Get used to the Get-Member
cmdlet. You’re going to be using it a lot in this article.
Every command in PowerShell that produces output can be piped to
Get-Member
. Just remember to make this cmdlet the very last in the pipeline as it will overwrite output with its own output.
Object Types and Classes
Without going into a lot of detail on object-oriented programming, every object has a “schema”. An object’s “schema” is a template of sorts that contains the blueprint to create an object. That blueprint is called a type.
Every object in PowerShell has a specific type. Each object has a blueprint from which it was created. An object type is defined by a class. Consider this example; 9 is number, Bluegill is fish, Labrador is dog, etc. The class comes before the type.
Objects are instances of classes with a particular type.
Don’t worry about getting deep into this topic. Unless you’re a software developer, you probably don’t need to worry too much about the semantics at this point. It is, however, an important concept to know at a basic level.
Properties
The most important concept about objects you should understand is properties. Properties are attributes that describe an object. An object can have many different properties attached to it representing various attributes.
One of the easiest ways to discover what properties exists on objects is using the Get-Member
cmdlet. You can see below that by using the MemberType
parameter, Get-Member
will limit the output returned to only objects. You’ll also see it displays the object type of System.ServiceProcess.ServiceController as well.
PS51> Get-Service | Get-Member -MemberType Property
Now take the example of the BITS service covered earlier and see the specific values of that object’s properties using the below code. The Get-Member
cmdlet allows you to find the property names but not the values. Using PowerShell’s Select-Object
cmdlet, however, you can inspect the property values.
Below you can see that StartType
is a property on the object. The object returned has many different members but by using the Select-Object
cmdlet, it is limiting the output to only show that property.
PS51> Get-Service -ServiceName 'BITS' | Select-Object -Property 'StartType'
Properties are, by far, the most common component of an object you’ll work with in PowerShell.
Aliases
Some properties have a MemberType
of Alias
. Aliases are pseudonyms for property names. They can sometimes give properties a more intuitive name.
You can see an example of an object with aliases using the Get-Service
cmdlet again as shown below. The Property Name
is aliased to ServiceName
and RequiredServices
is aliased to the ServicesDependedOn
property.
PS51> Get-Service | Get-Member -MemberType 'AliasProperty'
When a property has an alias, you can reference that property’s value using the alias name rather than the actual property name. In this example, a descriptive attribute like Name
and RequiredServices
is more intuitive and easier to type than ServiceName
and ServicesDependedOn
.
You can see an example of referencing these aliases below.
# Use the AliasProperty in place of an actual property name
PS51> $Svc = Get-Service -ServiceName 'BITS' #Object you are working with
PS51> $Svc.Name
BITS
PS51> $Svc.RequiredServices
You should see the following output. Notice again that you are keeping the code short, clean and concise. The information is the same, regardless of using the alias or not:
Methods
Properties are only one piece that creates an object; methods are also an important concept to understand. Methods are the actions that can be performed on an object. Like properties, you can discover methods on an object by using the Get-Member
cmdlet.
To limit Get-Member
‘s output to only methods, set the MemberType
parameter value to Method
as shown below.
PS51> Get-Service | Get-Member -MemberType 'Method'
As a beginner, you’ll use methods much less frequently than properties.
Other MemberTypes
Properties, methods and aliases are not the only types of members an object can have. However, they will be, by far, the most common type of members you’ll be working with.
For completeness, below are a few other types of members you might come across.
- Script Property – These are used to calculate property values.
- Note Property – These are used for static property names.
- Property Sets – These are like aliases that contain just what the name implies; sets of properties. For example, you have created a custom property called
Specs
for yourGet-CompInfo
function.Specs
is actually a subset of the properties Cpu, Mem, Hdd, IP. The primary purpose of property sets is to provide a single property name to concatenate a group of properties.
It’s also important to mention the concept of object events. Events are outside of the scope of this article.
Working with Objects in PowerShell
Now that you have a basic understanding of what an object consists of, let’s start getting our hands dirty and get into some code.
Many PowerShell commands produce output but sometimes you don’t need to see all of this output. You need to limit or manipulate that output somehow. Fortunately, PowerShell has a few different commands that allow you to do just that.
Let’s start out with an example of enumerating all services on the local computer using the Get-Service
cmdlet as shown below. You can see by the output many different services (objects) are returned.
PS51> Get-Service -ServiceName *
Controlling Returned Object Properties
Continuing on with the Get-Service
example, perhaps you don’t need to see each property. Instead, you just need to see the Status
and DisplayName
properties. To limit properties returned, you’d use the Select-Object
cmdlet.
The Select-Object
cmdlet “filters” various properties from being returned to the PowerShell pipeline. To “filter” object properties from being returned, you can use the Property
parameter and specify a comma-delimited set of one or more properties to return.
You can see below an example of only returning the Status
and DisplayName
properties.
PS51> Get-Service -ServiceName * | Select-Object -Property 'Status','DisplayName'
Sorting Objects
Perhaps you’re building a report to show services and their status. For easy information digesting, you’d like to sort the objects returned by the value of the Status
property. To do so, you can use the Sort-Object
cmdlet.
The Sort-Object
cmdlet allows you to collect all of the objects returned and then output them in the order you define.
For example, using the Property
parameter of Sort-Object
, you can specify one or more properties on the incoming objects from Get-Service
to sort by. PowerShell will pass each object to the Sort-Object
cmdlet and then return them sorted by the value of the property.
You can see below an example of returning all service objects sorted by their Status
properly returned in descending order using the Descending
switch parameter of Sort-Object
.
PS51> Get-Service -ServiceName * | Select-Object -Property 'Status','DisplayName' |
Sort-Object -Property 'Status' -Descending
The pipe [
|
] in PowerShell is one of a few line continuation techniques you should use when necessary. Use it rather than backticks.
Filtering Objects
Maybe you decide you don’t want to see all of the services on a machine. Instead, you need to limit the output by specific criteria. One way to filter the number of objects returned is by using the Where-Object
cmdlet.
While the Select-Object
cmdlet limits the output of specific properties, the Where-Object
cmdlet limits the output of entire objects.
The Where-Object
cmdlet is similar in function to the SQL WHERE clause. It acts as a filter of the original source to only return certain objects that match a specific criteria.
Perhaps you’ve decided that you only want objects returned with a Status
property value of Running
and only those with a DisplayName
property value that begins with A
.
You can see in the next code snippet a Where-Object
reference was inserted between Select-Object
and Sort-Object
in the pipeline order. Using a scriptblock value with a required condition crated for each object to meet via the FilterScript
parameter, you can craft any kind of query you’d like.
Check out the
Format-Table
cmdlet if you want to manipulate how output is returned to the console.
PS51> Get-Service * | Select-Object -Property 'Status','DisplayName' |
Where-Object -FilterScript {$_.Status -eq 'Running' -and $_.DisplayName -like "Windows*" |
Sort-Object -Property 'DisplayName' -Descending | Format-Table -AutoSize
Counting and Averaging Objects Returned
The Get-Service
command returns many different objects. Using the Where-Object
cmdlet, you have filtered out a portion of those objects but how many? Introducing the Measure-Object
cmdlet.
The Measure-Object
cmdlet is a PowerShell command that, among other mathematical operations, can count how many objects it receives via the pipeline.
Perhaps you’d like to know how many objects are eventually returned by the time your combined commands run. You can pipe the final output to the Measure-Object
cmdlet to find the total number of objects returned as shown below.
PS51> Get-Service * | Select-Object -Property 'Status','DisplayName' |
Where-Object {$_.Status -eq 'Running' -and $_.DisplayName -like "Windows*" |
Sort-Object -Property 'DisplayName' -Descending | Measure-Object
Once the commands are done processing, you’ll see, in this case, there were 21 objects returned initially created with the Get-Service
cmdlet.
Perhaps you’re only looking for the total objects returned. Since the Measure-Object
command returns the total objects found via a Count
property, you can reference the Select-Object
cmdlet again. But this time, only returning the Count
property.
PS51> Get-Service * | Select-Object -Property 'Status','DisplayName' |
Where-Object {$_.Status -eq 'Running' -and $_.DisplayName -like "Windows*" |
Sort-Object -Property 'DisplayName' -Descending |
Measure-Object |
# We start over again, filtering first, formatting last
Select-Object -Property 'Count'
Taking Action on Objects with Loops
As each object is processed via the pipeline, you can take action on each object with a loop. There are different kinds of loops in PowerShell but sticking with pipeline examples, let’s look into the ForEach-Object
cmdlet.
The ForEach-Object
cmdlet allows you to take action on each object flowing into it. This behavior is best explained with an example.
Continuing to use a Get-Service
example, perhaps you’d like to find all services on a Windows computer with a name that starts with “Windows” and is running. Using the Where-Object
cmdlet, you can create the conditions as you’ve done earlier.
Where-Object -FilterScript {$_.DisplayName -Like "Windows*" -and $_.Status -eq 'Running'}
But now instead of returning entire objects or even a few properties, you’d like to return the string <ServiceName> is running for each object using the code **Write-Host -ForegroundColor 'Yellow' <ServiceName> "is running"
.
You are now manipulating the output and creating your own string. The only way to do that is to use the ForEach-Object
cmdlet as shown below. Below you can see that for each object returned via Where-Object
, PowerShell runs the code Write-Host -ForegroundColor 'Yellow' $_.DisplayName "is running"
.
PS51> Get-Service -ServiceName * |
Where-Object {$_.DisplayName -Like "Windows*" -and $_.Status -eq 'Running'} |
Foreach-Object {
Write-Host -ForegroundColor 'Yellow' $_.DisplayName "is running"
}
The ForEach-Object
cmdlet is useful in many ways. As an example, you could build in additional logic to enumerate through every service object found and based on a property value, change the color and wording of the output string or even perform an additional action such as starting a stopped service.
Imagine the possibilities this provides to you! With a little thought and planning, you could create a script that takes one command and effortlessly executes it across many objects.
Comparing Objects
Sometimes you need to look at two objects and compare property values.
Perhaps you have two systems on your network that are nearly identical. However, you are experiencing what you expect to be a configuration issue with a service on one of the two systems.
You conclude that since these systems are in different parts of your network that you will need to use remote commands to gather the information in a PowerShell session. You open your favorite editor and create a script. This script, as you can see below, connects to two different servers and enumerates all processes on each of them.
$A = 'Svr01a.contoso.com'
$B = 'Svr02b.contoso.com'
$ProcA = Invoke-Command -Computername $A -Scriptblock {Get-Process -Name *}
$ProcB = Invoke-Command -ComputerName $B -Scriptblock {Get-Process -Name *}
You’ve now captured all of the processes on each computer in the $ProcA
and $ProcB
variables. You now need to compare them. You could manually look through each set of processes or you could do it the easy way and use a cmdlet called Compare-Object
.
Compare-Object
allows you to compare two different objects’ property values. This cmdlet reads each property in each object, looks at their values and then returns what’s different, by default and also what’s the same.
To use Compare-Object
, specify a ReferenceObject
and a DifferenceObject
parameter providing each object as the parameter values as shown below.
Compare-Object -ReferenceObject $ProcA -DifferenceObject $ProcB
By default, Compare-Object
will only return differences in the objects indicated by the SideIndicator
property. The symbols or side indicators used are >
, <
, & =
to show the matches of objects being compared.
You can use the switch parameter
IncludeEqual
withCompare-Object
to return object properties that are the same. If so, you’ll see==
as the side indicator. Similarly, you can useExcludeDifferent
to leave out differences.
The Compare-Object
cmdlet is a very useful cmdlet. If you would like to learn more, be sure to visit the online documentation.
Working With Custom Objects
Now that you have a good understanding of what objects are and how you can work with them, it’s time to create your own objects!
Creating Custom Objects From Hashtables
One way to create your own objects is by using hashtables. Hashtables are sets of key/value pairs precisely what you need for creating properties for an object.
Let’s start by creating a custom PowerShell object with some key/values using a hashtable. In the below example, you are creating a hashtable. This hashtable is representing a single object and it’s properties. Once the hashtable $CarHashtable
has been defined, you then cast use the PsCustomObject
type accelerator.
The pscustomobject type accelerator is a quick way to create an instance of the pscustomobject class. This behavior is called casting.
By the end of the below code snippet, you have an object ($CarObject
) of type pscustomobject with five properties assigned to it.
## Define the hashtable
$CarHashtable = @{
Brand = 'Ford'
Style = 'Truck'
Model = 'F-150'
Color = 'Red'
Drivetrain = '4x4'
}
## Create an object
$CarObject = [PsCustomObject]$CarHashTable
Alternatively, you can also use the New-Object
cmdlet. Using the same hashtable, instead of using the pscustomobject type accelerator, you could do it the long-form way with New-Object
. An example of this is shown below.
$CarHashtable = @{
Brand = 'Ford'
Style = 'Truck'
Model = 'F-150'
Color = 'Red'
Drivetrain = '4x4'
}
#Create an object
$CarObject = New-Object -TypeName PsObject -Properties $CarHashtable
When $CarObject
is created, you can now see below that you can reference each of the properties just as if it came from a built-in PowerShell cmdlet like Get-Service
.
Adding & Removing Properties
Not only can you create custom objects, but you can add to them as well. Recall using the Get-Member
cmdlet? Get-Member
has a relative called Add-Member
. The Add-Member
cmdlet doesn’t enumerate members, it adds them.
Using the previously created custom object as an example, perhaps you’d like to add a model year property to that object. You can do that by piping an object to Add-Member
specifying:
- The type of member (in this case a simple
NoteProperty
) - The name of the property (
Year
) - The value of the property (
Value
)
You can see an example of this below.
PS51> $CarObject | Add-Member -MemberType NoteProperty -Name 'Year' -Value '2010'
You can see below again that it appears just like any other property.
You can use similar techniques to add many different types of members. If you would like to explore more on your own, take a look at the Add-Member
documentation.
You can just as easily remove a member from an object. Although there is no Remove-Member
cmdlet, you can still make it happen by calling the Remove()
method on the object as shown below. You’ll learn about methods in the next section.
PS51> $CarObject.psobject.properties.remove('Drivetrain')
Quick Intro to Methods
Throughout this article, you’ve been working with properties. You’ve read property values, created your own properties and added and removed them. But you haven’t really done much to the environment. You haven’t changed anything on the server. Let’s take some action with methods.
Methods perform some kind of action. Objects store information while methods take action.
For example, you may be aware of the Stop-Service
command. This command stops a Windows service. To do that, you can send an object from Get-Service
directly to Stop-Service
to make it happen.
You can see below an example of stopping the BITS service. This example stops the BITS service and then checks the status to ensure it’s stopped. You’ve taken two actions with cmdlets; stopping the service and checking the status.
PS51> Get-Service -ServiceName 'BITS' | Stop-Service
PS51> Get-Service -ServiceName 'BITS'
Rather than running Get-Service
twice and executing a separate command, Stop-Service
, instead, you can leverage methods that are built right into the service objects. Many objects have methods. In this case, Stop-Service
and that second Get-Service
reference isn’t even needed.
By invoking methods on the service object itself, you can stop and retrieve the updated status all using a single object. Below you can see this in action. Notice that by using the Stop()
and Start()
methods, you can manipulate the service just like the commands did.
To ensure the Status
property value is up to date after the service status has changed, you can invoke the Refresh()
method which acts like another Get-Service
command call.
## Stop BITS on the local machine
$Svc = Get-Service -ServiceName 'BITS' #Object you are working with
$Svc.Stop() #Method / action you are taking
$Svc.Refresh() #Method / action you are taking
$Svc.Status #Property
#Start BITS on the local machine
$Svc = Get-Service -ServiceName 'BITS' #Object you are working with
$Svc.Start() #Method / action you are taking
$Svc.Refresh() #Method / action you are taking
$Svc.Status #Property
You should see the following output:
For more information on methods, check out the about_Methods help topic.
Conclusion
There are many things that you can do with objects in PowerShell. This article was just a primer to get you started learning them. In this article, you have learned some of the basics about what objects are, how to take action, manipulate and create them. You’ve seen a few different scenarios which showed you how to perform these tasks with code samples. Keep calm, and learn PowerShell. Thanks for reading!