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-Objectcmdlet).
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.