Mastering PowerShell Core Concepts: Cmdlets, Pipeline, and Objects

Nov 28, 2025 min read

Introduction

Why do so many developers struggle when they switch from Bash or Python to PowerShell? They often treat it like a text processing engine, missing the true power of the underlying objects. They try to grep and awk their way through data, which works but fights against the design of the language.

In a DevOps environment, reliable automation relies on structured data. Relying on string parsing is fragile; a single extra space or column change in the output can break your entire pipeline. PowerShell solves this by strictly adhering to an object-oriented model. You don’t pass text; you pass rich data structures.

This article explains how to effectively use Cmdlets, understand the mechanics of the PowerShell Pipeline, and manipulate objects to build robust automation scripts. We will break down the anatomy of a Cmdlet, visualize the pipeline, and walk through a practical scenario of filtering system processes as objects.

The Anatomy of a Cmdlet

PowerShell commands are called “Cmdlets” (pronounced command-lets). They follow a strict naming convention that makes the language predictable and easy to learn.

Verb-Noun Syntax

Every Cmdlet uses a Verb-Noun syntax, separated by a hyphen. The verb describes the action (

., Get, Set, New, Remove), and the noun describes the resource being acted upon (

., Service, Process, Item).

This consistency allows you to guess commands even if you’ve never used them. If you want to get information about a service, you guess Get-Service. To stop it? Stop-Service. To make a new file? New-Item.

You can see all approved verbs by running Get-Verb. This list categorizes verbs into groups like Common, Data, Lifecycle, and Security, ensuring that developers don’t create confusing aliases like Kill-Process (it’s Stop-Process) or Create-File (it’s New-Item).

Discovery and Help

PowerShell’s discoverability is one of its strongest features. You don’t need to memorize every command; you just need to know how to find them.

Using Get-Command If you know you want to work with “processes” but don’t know the commands, ask PowerShell:

Get-Command *Process*

This returns every command containing the word “Process,” giving you a targeted list of tools.

Mastering Get-Help Once you find a command, Get-Help is your instruction manual. It provides syntax, descriptions, and most importantly, examples.

# View the basic help
Get-Help Get-Service

# View detailed help with parameters
Get-Help Get-Service -Detailed

# View just the examples (Highly Recommended)
Get-Help Get-Service -Examples

Note: If you see limited help information, run Update-Help to download the latest documentation from Microsoft.

The Object-Based Pipeline

The pipeline (|) is where PowerShell diverges sharply from shells like Bash. In Linux, the pipeline passes text streams. In PowerShell, the pipeline passes entire .NET objects.

Text vs. Objects

When you run ls -l in Bash, you get lines of text. To get the file size, you have to cut specific characters from that line.

In PowerShell, Get-ChildItem (the equivalent of ls) doesn’t return text; it returns `Syste

ileInfoobjects. These objects have properties likeName, Length(size),CreationTime, and LastWriteTime`.

Bash (Text Stream):

# Requires knowledge of column positions
ps aux | grep python | awk '{print $2}'

PowerShell (Object Stream):

# References properties by name
Get-Process python | Select-Object Id

In the PowerShell example, we don’t care which column the ID is in. We simply ask for the Id property of the object.

Essential Pipeline Cmdlets

To work effectively with objects, you need to master four core Cmdlets:

  1. Select-Object: Chooses which properties to keep.

    # Only keep the Name and Status properties
    Get-Service | Select-Object Name, Status
    
  2. Where-Object: Filters the objects based on specific criteria.

    # Pass only Running services down the pipeline
    Get-Service | Where-Object Status -eq 'Running'
    
  3. Sort-Object: Reorders the objects.

    # Sort processes by memory usage
    Get-Process | Sort-Object WorkingSet
    
  4. ForEach-Object: Performs an action on each object individually.

    # Restart every stopped service named 'MyService'
    Get-Service MyService | Where-Object Status -eq 'Stopped' | ForEach-Object { $_.Start() }
    

Working with Objects in Practice

Since everything is an object, how do you know what an object contains?

Inspecting Objects (Get-Member)

Get-Member (alias gm) is the most critical tool for understanding data structures. It reveals the Properties (data) and Methods (actions) of any object in the pipeline.

Get-Service | Get-Member

Output (Truncated):

   TypeName: Syste

erviceProces

erviceController

Name                      MemberType    Definition
----                      ----------    ----------
Close                     Method        void Close()
Start                     Method        void Start(), void Start(string[] args)
Stop                      Method        void Stop()
DisplayName               Property      string DisplayName {get;set;}
Status                    Property      Syste

erviceProces

erviceControllerStatus Status {get;}
ServiceName               Property      string ServiceName {get;set;}
  • Properties: Tell you the state (

., Status, ServiceName).

  • Methods: Tell you what you can do to the object (

., Start, Stop).

Creating Custom Objects (PSCustomObject)

Sometimes you need to combine data from multiple sources into a single, structured report. You can create your own objects using [PSCustomObject].

$Report = [PSCustomObject]@{
    ServerName = $env:COMPUTERNAME
    Timestamp  = (Get-Date)
    FreeSpaceGB = [math]::Round((Get-Volume -DriveLetter C).SizeRemaining / 1GB, 2)
}

This creates a structured object that can be sorted, filtered, or exported to CSV (Export-Csv) or JSON (ConvertTo-Json) just like any native PowerShell object.

Practical Example: System Audit Script

Let’s build a script for a common scenario: You need to identify the top 5 memory-consuming processes on a server and format them for a management report.

Requirements:

  1. Retrieve the list of running processes.
  2. Sort them by memory usage (highest first).
  3. Select the Name, ID, and Memory usage.
  4. Convert the memory from bytes to Megabytes (MB) for readability.

Implementation:

# Get-Process retrieves all process objects
# Sort-Object sorts them by the WorkingSet property (Memory) in Descending order
# Select-Object picks the first 5 and creates a custom 'MemoryMB' property
Get-Process | 
    Sort-Object WorkingSet -Descending | 
    Select-Object -First 5 Name, Id, @{
        Name       = 'MemoryMB'
        Expression = { [math]::Round($_.WorkingSet / 1MB, 2) }
    }

Output:

Name       Id MemoryMB
----       -- --------
chrome   4520   850.25
teams    9812   620.10
code     1204   450.55
svchost   892   120.00
MsMpEng  2301    95.40

This script is readable, robust, and easy to modify. If you later decide you need the top 10 processes, you simply change -First 5 to -First 10.

Best Practices and Tips

Do:

  • Filter Left: Filter your data as early as possible. Get-Service -Name 'b*' is much faster than Get-Service | Where-Object Name -like 'b*' because the first command only retrieves the relevant objects, while the second retrieves everything and then discards most of it.
  • Use Full Cmdlet Names: In scripts, always use Where-Object instead of ? and Select-Object instead of select. This improves readability for others (and your future self).
  • Use Get-Member: Whenever you are unsure why a property isn’t appearing, pipe the object to Get-Member to verify the property name and type.

Don’t:

  • Don’t Parse Text: Avoid Select-String (grep) if an object property exists.
  • Don’t Use Aliases in Production: Aliases like gwmi or ls are fine for the console, but they make scripts hard to maintain.
  • Don’t Destroy Objects Prematurely: Don’t run Format-Table or Format-List in the middle of a pipeline. These commands convert objects into formatting instructions (text), making them unusable for further processing. Only format at the very end of the pipeline for display.

Troubleshooting Common Issues

Issue 1: “Property not found” errors

  • Cause: You may have destroyed the original object. For example, Select-Object Name creates a new custom object that only has a Name property. The original object’s methods (like .Kill()) are gone.
  • Solution: Check the object type with Get-Member at each step of your pipeline to ensure you still have the data you need.

Issue 2: Pipeline input not binding

  • Cause: Not all Cmdlets accept input from the pipeline, or they expect the input property to have a specific name (

., Name vs ComputerName).

  • Solution: You may need to explicitly map properties using Select-Object or use a ForEach-Object loop to pass the data manually.

Conclusion

Mastering PowerShell starts with changing your mindset from “text processing” to “object manipulation.”

Key Takeaways:

  1. Think in Objects: Stop parsing strings. Access properties directly.
  2. Discoverability: Use Get-Command and Get-Help to find what you need without memorizing everything.
  3. The Pipeline: Use the pipeline to pass rich data structures between commands seamlessly.
  4. Inspect Data: Get-Member is the most important diagnostic tool you have.
  5. Filter Left: Improve performance by filtering data as early as possible.

Next Steps: Open your terminal right now and run Get-Service | Get-Member. Look at the properties available to you. Then, try to write a one-liner that displays only the services that are currently ‘Stopped’.

In the next article, “Tooling Up for PowerShell DevOps,” we will set up a professional development environment with VS Code, Git, and Pester.