Reputation: 14705
We're having some kind of a weird issue. The function we created works fine and outputs all properties when the first object has all the properties needed. When this is not the case, it only limits itself to the output of the first object.
I know this sounds a bit confusing, but here's what I mean:
Function Get-PrintersInstalledHC {
Param (
[Parameter(ValueFromPipeline)]
[Object[]]$Servers
)
Process {
foreach ($S in $Servers) {
Try {
if ($S.Enabled) {
$Printers = Get-Printer -ComputerName $S.Name -Full -EA Stop |
Select-Object *,
@{Name='ComputerStatus';Expression={'Ok'}},
@{Name='RetrievalDate';Expression={(Get-Date -Format "dd/MM/yyyy HH:mm")}}
if ($Printers) {
$CimParams = @{
ClassName = 'Win32_PrinterConfiguration'
ComputerName = $S.Name
Property = '*'
ErrorAction = 'Stop'
}
$Details = Get-CimInstance @CimParams
Foreach ($P in $Printers) {
Foreach($D in $Details) {
if ($P.Name -eq $D.Name) {
$Props = @{
DriverVersion = $D.DriverVersion
Collate = $D.Collate
Color = $D.Color
Copies = $D.Copies
Duplex = $D.Duplex
PaperSize = $D.PaperSize
Orientation = $D.Orientation
PrintQuality = $D.PrintQuality
}
$P | Add-Member -NotePropertyMembers $Props -TypeName NoteProperty -PassThru
Break
}
}
}
}
}
}
Catch {
if (Test-Connection $S.Name -Count 2 -EA Ignore) {
[PSCustomObject]@{
ComputerName = $S.Name
ComputerStatus = "ERROR: $($Error[0].Exception.Message)"
RetrievalDate = (Get-Date -Format "dd/MM/yyyy HH:mm")
}
}
else {
[PSCustomObject]@{
ComputerName = $S.Name
ComputerStatus = 'Offline'
RetrievalDate = (Get-Date -Format "dd/MM/yyyy HH:mm")
}
}
}
}
}
}
When the first server fails on Get-Printer
and the others succeed, it only outputs ComputerName
, ComputerStatus
and RetrievalDate
. Like so:
$Servers = 'SERVER1', 'SERVER2' | Get-ADComputer | Select-Object Name, Enabled
Get-PrintersInstalledHC $Servers
ComputerName ComputerStatus RetrievalDate
------------ -------------- -------------
SERVER1 ERROR: The spooler service is not rea... 01/07/2015 10:21
SERVER2 Ok 01/07/2015 10:21
SERVER2 Ok 01/07/2015 10:21
SERVER2 Ok 01/07/2015 10:21
SERVER2 Ok 01/07/2015 10:21
When the first server is successful with Get-Printer
and others are not, then it outputs all object properties.
$Servers = 'SERVER2', 'SERVER1' | Get-ADComputer | Select-Object Name, Enabled
Get-PrintersInstalledHC $Servers
RenderingMode : SSR
PrinterStatus : Normal
Type : Local
Caption :
Description :
ElementName :
InstanceID :
CommunicationStatus :
DetailedStatus :
HealthState :
InstallDate :
Name : PRINTERNAME
OperatingStatus :
OperationalStatus :
PrimaryStatus :
Status :
StatusDescriptions :
Comment :
ComputerName : SERVER2
Datatype : RAW
DefaultJobPriority : 0
DriverName : Canon
JobCount : 0
KeepPrintedJobs : False
Location : Corridor
PermissionSDDL : G:SYD:(A;;LCSWSDRCWDWO;;;WD)(A;CIIO;RC;;;CO)(A;OIIO;RPWPSDRCWDWO;;;CO)(A;;LCSWSDRCWDWO;;;S-1-5-21
-1078081533-261478967-839522115-331680)(A;OIIO;RPWPSDRCWDWO;;;S-1-5-21-1078081533-261478967-83952
2115-331680)(A;;LCSWSDRCWDWO;;;BA)(A;OIIO;RPWPSDRCWDWO;;;BA)
PortName : PORTNAME
PrintProcessor : winprint
Priority : 1
Published : False
SeparatorPageFile :
Shared : True
ShareName : SHARENAME
StartTime : 0
UntilTime : 0
PSComputerName :
CimClass : ROOT/StandardCimv2:MSFT_Printer
CimInstanceProperties : {Caption, Description, ElementName, InstanceID...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
ComputerStatus : Ok
RetrievalDate : 01/07/2015 10:24
PaperSize : A4 210 x 297 mm
Orientation : 1
Collate : True
Color : 2
Copies : 1
DriverVersion : 1606
Duplex : False
PrintQuality : 600
# More objects here
ComputerName : SERVER1
ComputerStatus : ERROR: The spooler service is not reachable. Ensure the spooler service is running.
RetrievalDate : 01/07/2015 10:25
How can I make sure that all properties are always generated? As it's difficult to predict if the first server will fail or not on Get-Printer
.
Upvotes: 0
Views: 1148
Reputation: 22831
The answer is to make the details of the printers a separate property of the object that you're creating. Your basic object should look like:
ComputerName
ComputerStatus
RetrievalDate
PrinterDetails
The first three you already have, and what you're currently doing is making this object contain a ton of other properties for each printer, if the computer is reachable.
What you need to do is make that ton of properties into its own object and attach it to the fourth property. Then you can walk the output and do stuff if PrinterDetails
is not null.
I don't totally understand what's going on in your ForEach
loop with $P
and $D
it looks like you're enriching the information that returns from Get-Printer
. As long as the $P | Add-Member
is preserved, you should just be able to assign $Printers
to the new PrinterDetails
property (though personally I'd probably not do the enrichment, but create a property for the details itself).
Function Get-PrintersInstalledHC {
Param (
[Parameter(ValueFromPipeline)]
[Object[]]$Servers
)
Process {
foreach ($S in $Servers) {
Try {
if ($S.Enabled) {
$Printers = Get-Printer -ComputerName $S.Name -Full -EA Stop
if ($Printers) {
$CimParams = @{
ClassName = 'Win32_PrinterConfiguration'
ComputerName = $S.Name
Property = '*'
ErrorAction = 'Stop'
}
$Details = Get-CimInstance @CimParams
Foreach ($P in $Printers) {
Foreach($D in $Details) {
if ($P.Name -eq $D.Name) {
$Props = @{
DriverVersion = $D.DriverVersion
Collate = $D.Collate
Color = $D.Color
Copies = $D.Copies
Duplex = $D.Duplex
PaperSize = $D.PaperSize
Orientation = $D.Orientation
PrintQuality = $D.PrintQuality
}
$P | Add-Member -NotePropertyMembers $Props -TypeName NoteProperty
Break
}
}
}
[PSCustomObject]@{
ComputerName = $S.Name
ComputerStatus = "Ok"
RetrievalDate = (Get-Date -Format "dd/MM/yyyy HH:mm")
PrinterDetails = $Printers
}
}
}
}
Catch {
if (Test-Connection $S.Name -Count 2 -EA Ignore) {
[PSCustomObject]@{
ComputerName = $S.Name
ComputerStatus = "ERROR: $($Error[0].Exception.Message)"
RetrievalDate = (Get-Date -Format "dd/MM/yyyy HH:mm")
PrinterDetails = $null
}
}
else {
[PSCustomObject]@{
ComputerName = $S.Name
ComputerStatus = 'Offline'
RetrievalDate = (Get-Date -Format "dd/MM/yyyy HH:mm")
PrinterDetails = $null
}
}
}
}
}
}
$Servers = 'SERVER1', 'SERVER2' | Get-ADComputer | Select-Object Name, Enabled
Get-PrintersInstalledHC $Servers
This may mean that you may need to do some extra analysis after retrieving the details but it gives you a much more predictable object to work with. The PrinterDetails
property should now be an array of custom objects, so you can ForEach
over that (if it's not $null
) and pull back the bits you want.
Upvotes: 2