Author Posts

December 16, 2016 at 4:03 am

Hi all,
although I have been playing around with Powershell for a while now, I never really got into it any deeper. After attending some inspiring sessions at Techmentor in Orlando last week (thanks Don!), I jumped on the first chance to create a script. The script is working and does exactly what I need it to do, but I'd be interested in getting some feedback if there is a better way to do it.

Here was the challenge:
We have around 40 print servers in AD that host various print queues. We try to follow some standards, but unfortunately, not all the admins adhere to them. I wanted to create a script that queries all print servers and identifies any non-standard printer ports. Our standard is that each print queues is associated with one printer port on its print server. The printer ports are supposed to be configured to have the same name as the printer. We require FQDNs and not IP address. So for printer1 on printserver1.domain.com, we should also have an associated printerport that is labeled as printer1.domain.com and its address is also printer1.domain.com.

Here is the code:

Function Get-NonStandardPrinterPorts {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True)]
        [string]$ComputerName
    )

    $nonstdports=@()    
    try {
        # Get all printer ports of printserver $computername
        $ports=Get-WMIObject -Class Win32_TcpIpPrinterPort -ComputerName $ComputerName -ErrorAction stop | Select Name,HostAddress
            
        # enumerate all ports and check if name or hostaddress are not following standard naming convention of printer.henkelgroup.net
        # if printer port does not follow standard, add to array $nonstdports
        foreach ($port in $ports) {
            if (($port.Name).ToLower() -notmatch "^\w+\.domain\.com" -or ($port.HostAddress).ToLower() -notmatch "^\w+\.domain\.com" ) {
               $properties=@{'PortName'=$port.Name;'PortAddress'=$port.HostAddress}
               $object=New-Object -TypeName PSObject -Property $properties
               $nonstdports+=$object
            } # end of if
        } # end of foreach

    } # end of try
    
    Catch {
        # in case of error, throw exception message
        Write-Error $_.Exception.Message
    } # end of catch
    
    # return object 
    Write-Output $nonstdports
} # end of function

Function Get-PrinterPortAssignment {
    [CmdletBinding()]
    param (
        [Parameter(mandatory=$True)]
        [array]$PortName,
        [Parameter(mandatory=$True)]
        [array]$PrinterName,
        [Parameter(mandatory=$True)]
        [string]$ComputerName
    )

    $colPrinters=@()
        
    # enumerate all printer ports that were passed along in object with function call
    foreach ($port in $PortName) {
        
        $done=$false
	#enumerate all printers that were passed along in object with function call
        foreach ($printer in $PrinterName) {            
            #compare if printer port and printer name match up, if they do add them to object
            if ($printer.PortName -eq $port.PortName) {                
                $properties=@{'ComputerName'=$ComputerName;
                              'PortName'=$port.PortName;
                              'PortAddress'=$port.PortAddress; 
                              'Printer'=$printer.name
                             }
                $printq=New-Object -TypeName PSObject -Property $properties            
                $colPrinters += $printq
                $done=$true
                continue
            } # end of if
        } # end of foreach

        # if no matching printer was found for a printer port, add printer port to array, but mark printer as "n/a"
        if (!($done)) {  
            $properties=@{'ComputerName'=$ComputerName;
                          'PortName'=$port.PortName;
                          'PortAddress'=$port.PortAddress; 
                          'Printer'='n/a'
                         }
            $printq=New-Object -TypeName PSObject -Property $properties            
            $colPrinters += $printq
        } # end of if
    } # end of foreach
    
    # return object
    Write-Output $colPrinters
}


# grab list of printservers from AD
$servers=Get-ADComputer -filter "name -like '*printserver*'" | Select name

$endresult=@()

# enumerate list of printservers
foreach ($computername in $servers.name) {
    $printers=@()
        
    # Get all non-standard printer ports from printserver $computername
    $nonstdports=Get-NonStandardPrinterPorts -ComputerName $computername 

    # if non-standard printer ports were found, get a lost of all printers on server $computername
    if ($nonstdports) {
        $printers=Get-Printer -ComputerName $computername -ErrorAction Stop
    } # end of if
    
    # if printers were found on printserver $computername, get matching printer-printerport pairs
    if ($printers) {
        $result=Get-PrinterPortAssignment -PortName $nonstdports -PrinterName $printers -ComputerName $computername
        $endresult+=$result 
    } # end of if
    
} # end of foreach


# format for output on console
$endresult | Select ComputerName,Printer,PortName,PortAddress | Sort-Object ComputerName | Ft -AutoSize

I appreciate any feedback or suggestions for improvement you can provide.

Thanks much!

December 19, 2016 at 3:35 pm

Take a look at this logic and see if it works better for you as we are using a single loop to gather information. You could certainly turn this into a function as well and it does need error handling, but the basic logic is there.

# grab list of printservers from AD
$servers = Get-ADComputer -filter "name -like '*printserver*'" | Select -ExpandProperty Name

# enumerate list of printservers
$results = foreach ($computername in $Servers) {
   #Get server port data
   $ports = Get-WMIObject -Class Win32_TcpIpPrinterPort -ComputerName $ComputerName -ErrorAction stop | Select Name,HostAddress

   #Get server printer data and map port data
   $printers = Get-Printer -ComputerName $Computername | 
   Select *,
   @{Name="PortAddress";Expression={$pn = $_.PortName; $ports | Where{$_.Name -eq $pn} | Select -ExpandProperty Name}},
   @{Name="HostAddress";Expression={$pn = $_.PortName; $ports | Where{$_.Name -eq $pn} | Select -ExpandProperty HostAddress}}

   #Return printer data and check if the portname and hostaddress
   #meet the criteria, this will return a boolean (True\False)
   $printers | 
   Select *,
   @{Name="IsStandardPort";Expression={$_.PortName -match "^\w+\.domain\.com" -or $_.HostAddress -match "^\w+\.domain\.com"}}
    
} # end of foreach

#Show printers where it is not using a standard port
$results | Where{$_.IsStandardPort -eq $false}

December 21, 2016 at 1:28 pm

Thanks for your feedback. I've never used the

 XXXX | Select *,@{} 

functionality before, that looks pretty interesting, seems to shorten everything quite a bit.