DarkLite1
DarkLite1

Reputation: 14705

PowerShell Objects retrieving all object properties

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

Answers (1)

arco444
arco444

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

Related Questions