This article is a text version of a lesson from our PowerShell and Active Directory Essentials video course (use code ‘blog’ for free access).
The course has proven to be really popular as it walks you through creating a full Active Directory management utility from first principles.
Get the Free PowerShell and Active Directory Essentials Video Course
What makes a PowerShell Object?
If there’s one thing you fundamental difference between PowerShell and other scripting languages that have come before, it’s PowerShell’s default use of Objects (structured data) instead of plain strings (undifferentiated blobs of data).
Consider something like a car. It has:
- Colors
- Doors
- Lights
- Wheels
These items that describe this particular object are called properties. Your car can also do things, it can turn left and right, it can move forward and back – these are the methods of the object.
Properties: the aspects and details of the object.
Methods: actions the object can perform.
What’s the PowerShell Pipeline?
PowerShell was inspired by many of the great ideas that make up “The Unix Philosophy” – most notable for us today are two points:
- Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new “features”.
- Expect the output of every program to become the input to another, as yet unknown, program. Don’t clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don’t insist on interactive input.
In practice, what these somewhat abstract points of philosophy mean is that you should create lots of small, purposeful PowerShell scripts that each do a particular task. Every time you go to put an If/Else, another flag, another bit of branching logic, you should ask yourself: “Would this be better as a separate script?”
An example: don’t make a script that downloads a file and then parses the downloaded data. Make two scripts:
- One that downloads the data – download.ps
- A second that handles parsing the data into something usable – parse.ps
To get the data from the download.ps to parse.ps you would “pipe” the data in between the two scripts.
How to find the Properties and Methods of a PowerShell Object
There are way too many aspects of even the simplest object in PowerShell to remember. You need a way to interactively find out what each object you encounter can do as you’re writing your scripts can do.
The command you’ll need to do this is Get-Member cmdlet provided by Microsoft.
How To Use Get-Member
Get-Member
[[-Name] ]
[-Force]
[-InputObject ]
[-MemberType ]
[-Static]
[-View ]
[]
Get-Member helps reinforce an idea that I had a lot of difficulty grappling with in moving from bash to PowerShell scripting, that everything (literally everything) in PowerShell is an object. Let’s take a really simple example:
1. Use the Write-Output cmdlet to write some info into our PowerShell console.
Write-Output ‘Hello, World’
2. Assign that output to a variable called $string
$string = Write-Output `Hello, World`
3. Pipe the $string variable (That contains ‘Hello, World’) to the Get-Member cmdlet
$string | Get-Member
You’ll get some output that looks like the screenshot below:
A list of properties and methods for this Object of type String. As the underlying data of the object changes so changes the responses of the methods and properties.
Some examples:
A string object of “Hello, World” has a length (property) of 13
A string object of “Hello, People of Earth!” has a length of 24
Calling Methods and Properties with Dot Notation
All of the Methods and Properties of an Object need to be called with a type of syntax called “Dot Notation” which is just a fancy way of saying:
OBJECT.PROPERTY
Some examples:
$string.Length
13
Methods are invoked in the say way, but parentheses are added.
$string.ToUpper()
HELLO, WORLD!
$string.ToLower()
hello, world!
Both of these methods don’t take any “arguments” – additional commands passed in as parameters within the parentheses.
$string.Replace(‘hello’,’goodbye’)
Goodbye, world!
The Replace method does, the first argument is what you’re looking for in the string ‘hello’ and the second is what you’d like to replace it with.
How to Make our Own PowerShell Objects
Our $string variable that we created was of Type System.String – but what if we wanted to create our own type of object instead of relying upon the built-in types?
1. Create HashTable
A hash table is a Key + Value datastore where each ‘key’ corresponds to a value. If you’ve ever been given an employee number at a job or had to fill out a timesheet with codes given to each company you’ll be familiar with the concept.
$hashtable = @{ Color = ‘Red’; Transmission = ‘Automatic’; Convertible = $false}
If you pipe this to Get-Member you’ll now get a different listing of methods and properties because it’s a different Type (it’s System.Collections.Hashtable instead of System.String).
2. Creating a PowerShell Custom Object
To transform this from a hashtable to a full-blown PowerShell Object, we’ll use what’s called a “type accelerator” -> pscustomobject – [pscustomobject]$hashtable
When we run this and compare the results to what we have previously with Get-Member you’ll notice a wild difference. Gone are the generic methods and properties of a hashtable and instead are the properties that you had specified (Color, Transmission and whether or not it was a Convertible).
Getting Into the Pipeline
Some people get really hung up on what’s the difference between a script and an application. In general, scripts are small and do one very concise action. Applications are large (comparatively) and bundle together tons of features.
Consider the approach to exposing functionality in Microsoft Word versus how similar features would be presented as a series of scripts.
In Word, the word count is continually displayed in the status bar at the bottom of the editing window.
You can click it and get more detailed statistics (one of the many thousands of features in Microsoft Word).
In PowerShell scripting you’d use two separate cmdlets to achieve this functionality:
Get-Content will import a text file as an object (everything in PowerShell is an object) and Measure-Object will then collect statistics about that object for us.
Putting it together you’d have:
Get-Content c:\documents\myfile.txt | Measure-Object -word
The `|` character in between the two commands is the “pipe” which indicates that instead of displaying the output of the Get-Content command in the PowerShell command window, it should instead pass that data to the next script (the Measure-Object cmdlet).
Now, you might be looking at this example and thinking to yourself: “That’s a very convoluted way to finding out how many words are in a file” and you wouldn’t be wrong, But the important thing to consider is that the scripting doesn’t “care” what comes before the pipe.
Instead of importing a single file, maybe we’re writing a novel with 60 different chapters (one chapter per file), we could concatenate all of those files together and pipe the result to Measure-Object and get a word count for the whole book in one go.
How to Use the Pipeline
As a more practical example of using piping for sysadmin tasks, let us try to find and restart a service with PowerShell.
For this, we’re going to be using two cmdlets:
To start, we can walk through the steps as if we were doing everything manually.
First, let’s look for the Windows Audio Service
Get-Service -Name audiosrv
If you’re in PowerShell (look for the PS prompt) – you should get something that looks like:
And having found the service is present, we could then restart it.
Restart-Service -Name audiosrv
If we’re using pipelines, we could instead pipe the entire object into the Restart-Service cmdlet.
Get-Service -Name audiosrv | Restart-Service
The above is functionally the same but happens as a single command
To extend this further, we can use the -PassThru command to keep passing the input object through each script.
Get-Service -Name audiosrv | Restart-Service -PassThru | Stop-Service
Through this, we’re able to apply a number of command to the same initial object.
Now for a more real-world example.
Pinging a Collection of Computers with PowerShell
To start, we have a number of computer hostnames (one per line) in a text file.
Your first instinct might be to try and directly pass the file to the Test-Connection cmdlet, like:
Get-Content -Path C:\Example.txt | Test-Connection
However, we still need to be cognizant of what type of object is being passed. The above is passing in the file as if it was a chapter in a book, it’s not sure what to do with it. We need to first format the file data into the expected format.
To figure that out, we turn to the Get-Help cmdlet
Get-Help -Name Test-Connection -Full
“Full” indicates that the parameter listings should include not just the names and usage, but also whether or not they accept pipeline input, and if they do, what format.
In the above screenshot, you can see the “Accept pipeline input?” is True and indicates that it accepts input via a Property Name (instead of an object).
The following will extract each line of the input file and transform it via the pscustomobject command into a property name (as required by the Test-Connection cmdlet.
Get-Content -Path C:\Example.txt | ForEach-Object { [pscustomobject]@{ComputerName = $PSItem} } | Test-Connection
Next Steps with PowerShell
Want to learn more? Use unlock code ‘blog’ for free access to the full PowerShell and Active Directory Essentials video course.
What should I do now?
Below are three ways you can continue your journey to reduce data risk at your company:
Schedule a demo with us to see Varonis in action. We'll personalize the session to your org's data security needs and answer any questions.
See a sample of our Data Risk Assessment and learn the risks that could be lingering in your environment. Varonis' DRA is completely free and offers a clear path to automated remediation.
Follow us on LinkedIn, YouTube, and X (Twitter) for bite-sized insights on all things data security, including DSPM, threat detection, AI security, and more.