0

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.

3
  • Updated the OP with the full function, so it's more clear. Commented Jul 1, 2015 at 8:07
  • Can you also add a sample of how the data looks after the function runs? I think the fundamental issue is that you're not structuring your data well. The objects in the catch blocks are consistent, I'd have thought if printers are attached, in the try block you should populate a "details" property, and if not you set that property to $null Commented Jul 1, 2015 at 8:18
  • Output added to the OP. It's difficult to predict what Get-Printer will produce. Or I have to duplicate all the object properties of Get-Printer and set it to $NULL on the other [PSCustomObject]. This seems like a tedious and hopefully unnecessary job..It also makes the code a lot bigger. Commented Jul 1, 2015 at 8:30

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I like the idea to add a "nested" Object (PrinterDetails)
Thank you arco444, really appreciated! I've tried to capture the Details in a separate property as you suggested. Should it then also be nested like the PrinterDetails? Or is it possible to have this nested in the PrinterDetails property for each printer individually so it can easily be retrieved on a per printer basis?
Yes, you should add a property for each printer in PrinterDetails which contains an object with the details you retrieved. I guess if you're 100% sure you only want the details you're currently pulling it's not a massive deal, but doing it this way gives you more flexibility. I'd just attach the entire object you get back from the Get-CimInstance call.
That sounds like solid advise. I'm just trying to figure out how I can get $Details = Get-CimInstance @CimParams as a part of PrinterDetails = $Printers. Because then it contains another nested object with all the details. So in the end it would be great if $Printers.PrinterDetails will output everything on a per printer bases. This wont work Printers = $Printers, $Details :(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.