I like Theo's answer. I just want to add some additional stuff that certainly wouldn't fit in a comment...
Initially I think we're all debugging your code, but keeping to the pattern you originally laid out. Strictly speaking there's nothing wrong with that.
I think what's somewhat lost here is your actual question; why can't you simply pipe the output of one function to another?. The answer is in how the receiving cmdlet or function is expecting the data.
If you look at Get-Help Stop-Process -Parameter Id
(OR Parameter Name) you'll see it will take the property via pipeline if the property is named correctly:
-Id <Int32[]>
Specifies the process IDs of the processes to stop. To specify multiple IDs, use commas to separate the IDs. To find the PID of a process, type `Get-Process`.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
So you would've been able to pipe if you're custom object had a property named "Id".
Stop-Process
will accept the process id, but it's looking for the property name to be "Id" and Win32_Process returns "ProcessID".
But there is a second issue. The value of the property passed in must be acceptable to the receiving function/cmdlet. Unfortunately, Win32_Process usually returns the Name with a ".exe" suffix, and Get-Process
won't accept that.
Theo's answer is very good and works with Stop-Process
because his new object has a property named "ID" which is accepted by the pipeline and part of the default parameter set, meaning it's preferred over the name property.
However, if you were to pipe those objects to Get-Process
it wouldn't work. Get-Process
prefers name over ID and is expecting a value like "notepad" not "Notepad.exe, which Win32_Process returns. In that case Get-Process
wouldn't be able to find the process and would error.
Note: The above was corrected based on collaboration with Theo, you can look at the previous revision & comments for reference.
To make the objects also work with Get-Process
simply modify the value going into the "Name" property to remove the trailing '.exe' . I edited Theo's answer just to add that bit. You should see that if he approves it.
I realize that's not part of your original question, but it's to illustrate the additional caveat of piping between different tools/cmdlets/functions etc...
Note: There may still be a couple of exceptions. For example: Win32_Process returns "System Idle Process" But Get-Process
returns "Idle". For your purposes that's probably not an issue. Of course you'd never stop that process!
Note: The likely reason Get-Process
prefers the name while Stop-Process
prefers the ID is that name is not unique but ID is. Stop-Process
Notepad will kill all instances of Notepad, which is usually (and in your case) not what's intended.
Regarding the approach in general. I'd point out there are several ways to both extend objects and create PS Custom objects. Add-Member
is a good approach if you need or want the instance type to remain the same; I'd consider that extending an object. However, in your case you are creating a custom object then adding members to it. In such a case I usually use Select-Object which already converts to PSCustomObjects.
Your code with corrected "Name" property:
$Processes = Get-WmiObject win32_process
$Processes |
ForEach-Object{
$Owner = $_.getowner()
$Process = New-Object PSObject
$Process | Add-Member NoteProperty 'ComputerName' $_.CSName
$Process | Add-Member NoteProperty 'ProcessName' $_.ProcessName
$Process | Add-Member NoteProperty 'ProcessID' $_.ProcessID
$Process | Add-Member NoteProperty 'Domain' $Owner.Domain
$Process | Add-Member NoteProperty 'User' $Owner.User
$Process | Add-Member NoteProperty 'Name' -Value ( $_.ProcessName -Replace '\.exe$' )
$Process
}
Note: For brevity I removed some surrounding code.
Using select would look something like:
$Processes = Get-WmiObject win32_process |
Select-Object ProcessID,
@{Name = 'ComputerName'; Expression = { $_.CSName }},
@{Name = 'Name '; Expression = { $_.ProcessName -Replace '\.exe$' } },
@{Name = 'Id'; Expression = { $_.ProcessID } },
@{Name = 'Domain'; Expression = { $_.GetOwner().Domain} },
@{Name = 'User'; Expression = { $_.GetOwner().User} }
This can then be piped directly to a where clause to filter the processes you are looking for, then piped again to the Stop-Process
cmdlet:
Get-WmiObject win32_process |
Select-Object ProcessID,
@{Name = 'ComputerName'; Expression = { $_.CSName }},
@{Name = 'Name '; Expression = { $_.ProcessName -Replace '\.exe$' } },
@{Name = 'Id'; Expression = { $_.ProcessID } },
@{Name = 'Domain'; Expression = { $_.GetOwner().Domain} },
@{Name = 'User'; Expression = { $_.GetOwner().User} } |
Where-Object{ $TargetUsers -contains $_.User } |
Stop-Process
Note: This drops even the assignment to $Processes
. You'd still needed to populate the $TargetUsers
variable.
Also: An earlier comment pointed out that given what you are doing you don't need all the props so something like:
Get-WmiObject win32_process |
Select-Object @{Name = 'Name '; Expression = { $_.ProcessName -Replace '\.exe$' } },
@{Name = 'User'; Expression = { $_.GetOwner().User} } |
Where-Object{ $TargetUsers -contains $_.User } |
Stop-Process
However, if you are doing other things in your code like logging the terminated processes it's relatively harmless to establish & maintain more properties.
And just for illustration, piping could be facilitated through ForEach-Object
with relative ease as well and no need to stray from the original objects:
Get-WmiObject win32_process |
Where{$TargetUsers -contains $_.GetOwner().User } |
ForEach-Object{ Stop-Process -Id $_.ProcessID }
One of the best things about PowerShell is there are a lot of ways to do stuff. That last example is very concise, but it would be sub-optimal (albeit doable) to add something like logging or console output...
Also Theo is right about Get-CimInstance. If I'm not mistaken Get-WmiObject
is deprecated. Old habits are hard to break so all my examples used Get-WmiObject
However, these concepts should applicable throughout PowerShell including Get-CimInstance
...
At any rate, I hope I've added something here. There are a few articles out there discussing the different object creation and manipulation capabilities pros & cons etc... If I have time I'll try to track them down.