Generally speaking:
select $item.Name,$item.CanonicalName,$item.OperatingSystem
should be:
select Name, CanonicalName, OperatingSystem
That is, you need to pass the property names (e.g., Name
), not the current input object's property values (e.g., $item.Name
) to select
(the Select-Object
cmdlet).
The net effect is that Select-Object
creates custom objects whose properties are (mistakenly) named for the property values and themselves have no value, given that the input objects have no such properties.
This explains the output you saw.
However, the bigger problem is that even that won't work, given that the property names relate to the $item
object, not to the objects output by Get-HotFix
, which are the ones select
operates on.
As it turns out, what you really need is to use the Get-HotFix
call as a conditional, so as to only write a CSV row for the computer at hand if at least one of the specified hotfixes is installed:
$hotfixIds = 'KB4534310', 'KB4534314', 'KB4534283', 'KB4534288', 'KB4534297', 'KB4534309', 'KB4534271', 'KB4534273'
if (0 -ne (Get-HotFix -ErrorAction SilentlyContinue -Id $hotfixIds -ComputerName $item.Name).Count) {
$result += $item | select Name, CanonicalName, OperatingSystem
}
Note:
Note how it is now $item
(the computer at hand) that is piped to select
, to ensure that its properties are extracted (in the form of a custom object with these properties).
You could omit 0 -eq
altogether and rely on PowerShell's implicit to-Boolean conversion, where any nonzero number evaluates to $true
(see the bottom section of this answer for a summary of all rules.
- If instead you want to test for all of the specified hotfixes being installed, replace
0 -ne
with $hotfixIds.Count -eq
.
-ErrorAction SilentlyContinue
silences the errors from computers where none of the specified hotfixes are installed; you could examine the automatic $Error
collection afterwards, or use -ErrorVariable err
to collect all command-specific errors in variable $err
.
Also, your overall command can be greatly streamlined - see the bottom section.
A solution for a different scenario, that may be of interest as well:
If you wanted to combine properties from the Get-HotFix
output objects with properties from the $item
objects (representing the computer at hand):
The following command:
- selects all properties from the
Get-HotFix
output objects (-Property *
)
- adds the properties of interest from the current
$item
, using calculated properties
# Additional 'KB...' values omitted for brevity.
Get-HotFix -Id KB4534310, KB4534314 -ComputerName $item.Name |
Select-Object -Exclude Name -Property *,
@{ n = 'Name'; e = { $item.Name } },
@{ n = 'CanonicalName'; e = { $item.CanonicalName } },
@{ n = 'OperatingSystem'; e = { $item.OperatingSystem } }
Note that -Exclude Name
excludes the Name
property from the input objects (Get-HotFix
output objects that have such a property, but it is empty), so that Name
can be added as a property containing the computer name.
As for what you tried:
Aside from the Select-Object
property-name problem mentioned above, your major problem was that you expected a pipeline segment as a conditional, which is not how pipelines work:
Get-HotFix ... | select ...
The above simply sends Get-HotFix
's output objects to select
(Select-Object
), which then unconditionally processes them (and, as stated, looks for properties with the given names on these objects).
Now, if Get-HotFix
produced no output, then conditional logic applies implicitly: the select
command would then simply not be invoked.
Conversely, if Get-HotFix
produces multiple outputs, select
would be invoked on each.
That is, if we had naively tried to correct your command from:
Get-HotFix ... | select ...
to:
Get-HotFix ... | ForEach-Object { $item | select ... }
you would have potentially created multiple output objects per computer, namely whenever a given computer happens to have more than one among the given hotfixes installed.
A streamlined version of your (corrected) command:
Your command can be streamlined to use a single pipeline only, without the need for aux. variables:
Get-ADComputer -Filter '(OperatingSystem -like "Windows Server 2019*") -and (enabled -ne $false)' -Property * |
ForEach-Object {
if (0 -ne (Get-HotFix -ErrorAction SilentlyContinue -ComputerName $item.Name -Id KB4534310,KB4534314,KB4534283,KB4534288,KB4534297,KB4534309,KB4534271,KB4534273).Count) {
$item | select Name, CanonicalName, OperatingSystem
}
} | Export-Csv -Path C:\Users\user1\Desktop\Servers.csv -NoTypeInformation
Note:
If you end a line with |
, you do not need a trailing `
to signal line continuation.
- PowerShell [Core] v7.0+ now also allows placing
|
at the start of the very next line.
A single-quoted string ('...'
) is used instead of a script block ({ ... }
) to pass the -Filter
argument, because tt's best to avoid the use of script blocks ({ ... }
) as -Filter
arguments.
The output custom object instances created with $item | select Name, CanonicalName, OperatingSystem
are sent directly to the pipeline.