How to Filter Objects with Where-Object in PowerShell

Abstract data pipeline filtering failed services and outdated computers

What You’ll Learn

In this lesson, you will learn how to use PowerShell’s Where-Object cmdlet to keep only the objects that match a condition. This is useful when administrative commands return many results and you need to find a smaller group, such as stopped services or computers that have not checked in recently.

  • Understand how Where-Object filters objects in a pipeline.
  • Use $_ to refer to the current object.
  • Compare object properties with operators such as -eq and -lt.
  • Filter administrative data by service status or computer activity date.

The Concept

PowerShell commands often return objects, not just lines of text. An object contains properties that describe it. For example, a service object might have properties named Name, Status, and StartType.

Where-Object examines objects in a pipeline and keeps only the objects whose properties match a condition.

The basic syntax is:

$items | Where-Object { $_.Property -eq "Value" }

Inside the script block, $_ represents the object currently being examined. The dot notation accesses one of that object’s properties. In the example above, $_.Property means “the Property value of the current object.”

PowerShell uses comparison operators such as these:

  • -eq: equal to
  • -ne: not equal to
  • -lt: less than
  • -gt: greater than
  • -and: requires two conditions to be true

You can use Where-Object with real administrative commands, such as filtering the results of Get-Service, or with data collected from another command, file, or management system.

Basic Example

The following example represents service information returned by an administrative command. It filters the objects so that only stopped services remain.

$services = @(
    [PSCustomObject]@{
        Name = "Spooler"
        Status = "Running"
        StartType = "Automatic"
    }
    [PSCustomObject]@{
        Name = "LegacyBackup"
        Status = "Stopped"
        StartType = "Automatic"
    }
    [PSCustomObject]@{
        Name = "FileSync"
        Status = "Stopped"
        StartType = "Manual"
    }
)

$failedServices = $services | Where-Object {
    $_.Status -eq "Stopped"
}

$failedServices | ForEach-Object {
    "$($_.Name): $($_.Status)"
}

Expected Output

LegacyBackup: Stopped
FileSync: Stopped

How the Code Works

Administrative service or computer objects flow into Where-Object. Each current object is evaluated against property conditions such as Status equals Stopped or LastSeen earlier than a cutoff, with only matching objects continuing to filtered results for display.
Where-Object tests each pipeline object and keeps only those whose properties satisfy the selected condition or combination of conditions.

The first statement creates an array named $services. Each item is a custom PowerShell object with three properties:

  • Name identifies the service.
  • Status describes whether the service is running or stopped.
  • StartType describes how the service is configured to start.

This pipeline performs the filtering:

$failedServices = $services | Where-Object {
    $_.Status -eq "Stopped"
}

PowerShell sends each service object through Where-Object one at a time. For each object, the expression checks whether its Status property is equal to "Stopped".

For the Spooler object, the condition is false, so that object is discarded. For LegacyBackup and FileSync, the condition is true, so those objects are stored in $failedServices.

The final pipeline formats each remaining object as a readable line. The filtering itself has already happened; this last step only controls how the results are displayed.

With actual service data, the same idea could be applied directly to the output of Get-Service:

$stoppedServices = Get-Service | Where-Object {
    $_.Status -eq "Stopped"
}

Another Example

Administrative reports often contain a LastSeen property for each computer. A computer is potentially stale when its last check-in date is earlier than a chosen cutoff date.

$computers = @(
    [PSCustomObject]@{
        ComputerName = "FIN-015"
        LastSeen = [datetime]"2025-02-12"
        Owner = "Finance"
    }
    [PSCustomObject]@{
        ComputerName = "HR-004"
        LastSeen = [datetime]"2024-11-18"
        Owner = "Human Resources"
    }
    [PSCustomObject]@{
        ComputerName = "ENG-022"
        LastSeen = [datetime]"2025-03-01"
        Owner = "Engineering"
    }
)

$cutoffDate = [datetime]"2025-01-01"

$staleComputers = $computers | Where-Object {
    $_.LastSeen -lt $cutoffDate
}

$staleComputers | ForEach-Object {
    "$($_.ComputerName) last checked in on $($_.LastSeen.ToString("yyyy-MM-dd"))"
}

Here, -lt means “less than.” The filter keeps computers whose LastSeen date is earlier than $cutoffDate.

Common Mistakes

Using an assignment operator instead of a comparison operator

Use -eq when you want to compare a property with a value. Do not use = for this purpose.

$services | Where-Object {
    $_.Status -eq "Stopped"
}

Forgetting the current-object variable

Inside the Where-Object script block, $_ refers to the object currently being tested. A property name by itself does not tell PowerShell which object should be examined.

Filtering the wrong property

Check the properties returned by a command before writing a filter. You can inspect them by piping results to Get-Member or by displaying a few objects. A filter for Status will not work if the command returns a differently named property such as State.

Expecting results when no objects match

If no objects satisfy the condition, Where-Object returns no objects. This is not necessarily an error; it may simply mean that there are no stopped services or stale computers in the data.

Try It Yourself

Use the following administrative records to find computers that have an Active status. Store the matching objects in a variable named $activeComputers, then display their computer names.

$computerRecords = @(
    [PSCustomObject]@{
        ComputerName = "SALES-101"
        Status = "Active"
    }
    [PSCustomObject]@{
        ComputerName = "SALES-102"
        Status = "Retired"
    }
    [PSCustomObject]@{
        ComputerName = "SALES-103"
        Status = "Active"
    }
)

Challenge

Find computers that meet both of these administrative conditions:

  • The computer runs Windows 10.
  • Its last check-in occurred before January 1, 2025.

Store the matching objects in $outdatedWindows10Computers and display each computer name with its last check-in date. Use Where-Object and the -and operator.

Solution

$computerInventory = @(
    [PSCustomObject]@{
        ComputerName = "ACCT-007"
        OperatingSystem = "Windows 10"
        LastSeen = [datetime]"2024-12-15"
    }
    [PSCustomObject]@{
        ComputerName = "ACCT-008"
        OperatingSystem = "Windows 11"
        LastSeen = [datetime]"2024-10-20"
    }
    [PSCustomObject]@{
        ComputerName = "ACCT-009"
        OperatingSystem = "Windows 10"
        LastSeen = [datetime]"2025-02-04"
    }
    [PSCustomObject]@{
        ComputerName = "ACCT-010"
        OperatingSystem = "Windows 10"
        LastSeen = [datetime]"2024-08-30"
    }
)

$cutoffDate = [datetime]"2025-01-01"

$outdatedWindows10Computers = $computerInventory | Where-Object {
    $_.OperatingSystem -eq "Windows 10" -and
    $_.LastSeen -lt $cutoffDate
}

$outdatedWindows10Computers | ForEach-Object {
    "$($_.ComputerName): $($_.LastSeen.ToString("yyyy-MM-dd"))"
}
ACCT-007: 2024-12-15
ACCT-010: 2024-08-30

The filter keeps an object only when both conditions are true. ACCT-008 is too old but runs Windows 11, while ACCT-009 runs Windows 10 but checked in after the cutoff date. Therefore, neither one appears in the results.

Key Takeaways

  • Where-Object filters objects that travel through a PowerShell pipeline.
  • Use $_ to refer to the object currently being tested.
  • Access a property with dot notation, such as $_.Status or $_.LastSeen.
  • Use comparison operators such as -eq and -lt to define the filter.
  • Use -and when an object must satisfy multiple conditions.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top