PowerShell is an Object-Oriented Shell for anyone working with or within the Microsoft Windows ecosystem.
And since almost everything is an Object in PowerShell, it is important to know how to manipulate them.
This module will cover what kind of components are Objects made of and how to interact with them, Operators used in PowerShell and finally, how to use Quotes properly.
What makes an Object?
A PowerShell Object can consist of following Object Members:
- Property
- Method
- AliasProperty
- Script Property
- Note Property
- Proptery Sets
You will learn about the two first ones in next chapter, the third one should be self-explanatory, an alias for a Property and the three latter ones are out of scope for this module.
To check which of these Object Members are available for the Object you are working with, you would use Get-Member
cmdlet.
Get-Member
is basically the second most useful cmdlet you have, next to Get-Help
.
Here are some examples:
Get-Process | Get-Member
"string" | Get-Member
$var = Get-Service
$var | Get-Member
As you can see, you can always query for Object Members. It does not matter what kind of Object it is, however outcome will vary between different Types of Objects.
Next up you will learn about the most common Object Types.
Next up: Methods and Properties.
Methods
Methods allow you to perform some sort of activity on an Object. In the following example, you want to stop Video.UI Process, you get the process like so first:
Get-Process -Name 'Video.UI'
There are better ways to perform this activity in real world (examples below),
Stop-Process -Name 'Video.UI'
Get-Process -Name 'Video.UI' | Stop-Process
however in this example you will instead use a Method this time and you will stop the process by enclosing the cmdlet in parentheses and accessing the Process Object’s Kill Method, like shown below:
(Get-Process -Name 'Video.UI').Kill()
By using a useful Count Property, which is not cmdlet specific, you can easily count how many Processes the machine is currently running, like so:
(Get-Process).Count
Or show how many failed logon attempts there were within the last minute:
(Get-WinEvent -FilterHashtable @{LogName = 'Security'; ID = '4625'; StartTime = (Get-Date).AddMinutes(-1) }).Count
Properties
Properties are Members of an Object that display certain information.
These Members usually make up the view you see when you run a cmdlet.
You may have noticed that when you run Get-Process
there were only 8 columns shown, but there are a lot more Properties available, but not shown by default.
The default view is made by module creator/maintainer and it may, or may not, represent all of the Properties in that view.
Out of all the different Members of an Object, you will be dealing with Properties the most.
For example, you run Get-NetAdapater
and see the default view of Network Adapters, which does not shown you a lot, but gives you the overall information at a glance.
You pipe Get-NetAdapater
to Get-Member
and see a long list of Properties.
You want MTU Size, Network Interface Driver Version and Virtual check Properties to be included in the output, so you pick out these Properties and put together the following cmdlet:
Get-NetAdapter | Select-Object -Property Name, ifIndex, Status, MacAddress, LinkSpeed, MtuSize, DriverVersion, Virtual
In order to display all the Properties, you would use the * wildcard:
Get-Process -ProcessName System | Select-Object -Property *
NOTE: You do not want to abuse the * wildcard. Filter the queried data as much as you can and then you are safe to use it. Otherwise you will just be overwhelmed by the huge amount of information and it is going to be useless in most cases. The example above shows all the available Properties for the System Process.
By running: Get-Process | Get-Member
.
On the Definition column, on the right, you will see get and set, where get is data retrieval and set indicates that you can change the Property value.
More on that in the example below.
You could also interact with the Object Members by storing the cmdlet into a Variable and then access them using that Variable.
In this example you will Enable History for Task Scheduler tasks:
First, you will store Task Scheduler Event Log into a variable:
$TSHistory = Get-WinEvent -ListLog Microsoft-Windows-TaskScheduler/Operational
Then pipe the Variable into Get-Member
and have a look what you are allowed to do with that Object.
$TSHistory | Get-Member
By looking at the list of Properties and Methods, you see IsEnabled Property, which allows setting a value to the Property, and SaveChanges Method. Mentioned Property and Method allow you to accomplish what you need to do. You will use IsEnabled Property to Enable Task Schedule logging (History) functionality, by using a $True/$False boolean:
$TSHistory.IsEnabled = $True
This however will not make the change just yet. All these changes are currently contained in the Variable you made. You have to use SaveChanges Method, to commit the actual change:
$TSHistory.SaveChanges()
Note the parentheses! If you are not using a Variable, to access the Object, you have to enclose the cmdlet in parentheses in order to access a Method, or a Single Property (You can not access multiple Properties, and/or Methods at once either way).
Also remember that not all cmdlets are the same, Get-Member
will let you know what you can do with, and what you can get from, a cmdlet.
In the following examples you will first access the DisplayName Property of WinRM Windows Service directly and in the next example you will store WinRM Windows Service into a Variable and access the Property using the Variable:
(Get-Service -Name WinRM).DisplayName
$svc = Get-Service -Name WinRM
$svc.DisplayName
Object States
When you, for example want to see only Process Names and you invoke it as shown in this example:
(Get-Process).ProcessName
The output you get will cease to be a regular Object and is just a string. This is useful if a list of the Process Names was all you wanted.
There are some cases where you can end up with a string output, instead of an Object, this is one of these cases. If you would pipe this cmdlet to Get-Member:
(Get-Process).ProcessName | Get-Member
You would still see a list of Methods, but these are no longer Get-Process
specific members of an Object, but generic string manipulation Methods.
You will also notice this at the beginning of the members list, where it says TypeName: System.String, in this case.
Like often with many things, there are exceptions. One being that if you do something like this:
(Get-Service -Name Spooler).Name | Start-Service
Start-Service
cmdlet accepts input by Value.
Value can be an Object, or String, so if there is a Windows Service by the name of Spooler, Start-Service
cmdlet will be able to Start it.
This is also the reason why you could do something like this: "Spooler", "BITS" | Restart-Service
.
You are feeding an array of Strings to Restart-Service
, because it accepts Input by Value, this will Succeed.
However, when the cmdlet you are piping into is expecting an Object and you are giving it a String, it will error out.
Operators in PowerShell
Comparison Operators
Like in every environment that has scripting capability, you often need to check, compare, filter values. You will see that all the Operators follow a logical pattern.
- Filter Processes where Process Name is Equal to explorer:
Get-Process | Where-Object ProcessName -eq 'explorer'
- Filter Processes where Process Name does Not Equal to svchost:
Get-Process | Where-Object ProcessName -ne 'svchost'
-
Filter Processes where 64-bit Memory Working Set of a Process is Less Than 10MB.
Note the MB used here! WorkingSet and WorkingSet64 Properties hold data in bytes and in Windows PowerShell 5.1
Get-Process
shows you the output in KiloBytes, however Processes today use easily tens of MegaBytes of memory, or more. So, instead of writing$PSItem.WorkingSet64 -lt "10000000"
, you would do it like shown in the example below.
Get-Process | Where-Object WorkingSet64 -lt '10MB'
- Filter Processes where Handle Count of a Process is Greater Than 10000.
Get-Process | Where-Object HandleCount -gt '10000'
- Filter Processes where Handle Count of a Process is Less than, or Equal to 100.
Get-Process | Where-Object HandleCount -le '100'
- Filter Processes where Handle Count of a Process is Greater than, or Equal to 10000.
Get-Process | Where-Object HandleCount -ge '10000'
- Match operator lets you use RegEx to find matches. Here you will filter Processes by Process Name that match either explorer, or svchost.
Get-Process | Where-Object ProcessName -match 'explorer|svchost'
* To achieve the opposite effect, you would use **notmatch Operator**. * Like operator lets you use **Wildcards** to find matches that **look like** given string.
Get-Service | Where-Object Name -like "*win*"
* To achieve the opposite effect, you would use **notlike Operator**. * Filter Processes where **Process Name** is **in** the included Array.
Get-Process | Where-Object ProcessName -in @('vmmem', 'WmiPrvSE', 'System')
* To achieve the opposite effect, you would use **notin Operator**.
* *NOTE: **in** and **notin Operators** take either comma separated list of items: `-in 'vmmem','WmiPrvSE','System'`, or an array of items `-in @('vmmem','WmiPrvSE','System')`*
Logical Operators
Logical Operators are as follows: -and, -or, -xor, -not, !
- -and means that both conditions have to be True. This will return the specified Windows Service, when both conditions match. If given Windows Service would be Stopped, you would not get anything back.
Get-Service | Where-Object -FilterScript { $PSItem.Name -eq 'W32Time' -and $PSItem.Status -eq 'Running' }
- -or means that at least one condition has to be True. This will return specified Windows Services, when at least one of the conditions matches.
Get-Service | Where-Object -FilterScript { $PSItem.Name -eq 'W32Time' -or $PSItem.Name -eq 'WinRm' }
- -xor means that only one condition can be True. Following example will return the System Process because the Process ID is incorrect. Change the Process ID to the correct value of 4 and you will not get a result.
Get-Process | Where-Object -FilterScript { ($PSItem.ProcessName -eq 'System') -xor ($PSItem.Id -eq '3') }
- -not and ! are the same. Reverses the statement following the Operator. Although the specified Windows Service does exist, you will get output of False, because of the ! Operator.
!(Get-Service | Where-Object -FilterScript { $PSItem.Name -eq 'W32Time' })
NOTE: When comparing groups of logical operations, you would put the groups in parentheses. As shown in -xor example above.
Using Correct Quotes
It is important to know where to use the correct type of quotes. It will determine whether you code will work, or not.
“Double quotes” are used when it is necessary that whatever is in them, is able to expand, or run.
Variable expansion is the ability to access Variable’s Objects and Methods and to parse whatever information it holds, or ability to run stored Variables in quotes.
‘Single quotes’ on the other hand are literal quotes, meaning that whatever is in them, is parsed as a text.
Some examples:
(0..2) | ForEach-Object { Write-Output -InputObject "This line nr $PSItem." }
Output:
This line nr 0.
This line nr 1.
This line nr 2.
(0..2) | ForEach-Object { Write-Output -InputObject 'This line nr $PSItem.' }
Output:
This line nr $PSItem.
This line nr $PSItem.
This line nr $PSItem.
As you can see, the first example is working as intended, in a normal situation, while the other did not. However, you might want to use single quotes when you want to convey your point while pointing to a Variable you do not want to be parsed for this particular instance, for example:
Write-Output -InputObject 'Use $Employees Variable to interact with the data!'
Output:
Use $Employees Variable to interact with the data!
Conclusion
You have now used some of PowerShell Operators. You have an understanding of how to use them and what logic the Operators follow. You have also looked into Objects, queried Properties not shown by default and used Methods to perform actions on a Object. You also know how not to shoot yourself in the foot by misusing quotes.