31290
31290

Reputation: 45

Getting Printer's Driver Version alongside Printer name in PowerShell

Hello :)

Hope you are doing fine today.

Well, I've been asked to pull out a report of all our Printers and the Drivers version they are utilizing (more than 1300 printers worldwide and 150+ Hosts).

At that point, I can retrieve most of the information I need.

Here's the code so far:

$ReportFileName = "c:\SAC_IS\printerreport.csv" 
$PrintServersList="C:\SAC_IS\servlist_TEST.txt" 

$servers =  Get-Content -Path $PrintServersList 
$allprinters = @() 
foreach( $server in $servers ){ 
Write-Host "checking $server ..." 
$printers = $null 
$printers = Get-WmiObject -class Win32_Printer -computername $server | select Name,Shared,ShareName,Local, DriverName, PortName, @{n="HostName";e={$server}} ,@{n="Driver Version";e={Get-WmiObject Win32_PrinterDriver -ComputerName $server | select @{n="Version";e={(Get-Item $_.DriverPath).VersionInfo.ProductVersion}}}}, Location,Comment,SpoolEnabled,Published
$allprinters += $printers 
 } 

$allprinters | Export-CSV -Path $ReportFileName -Delimiter ";" -NoTypeInformation -Force -Encoding UTF8 

My problem here is that I'm unable to retrieve the driver's version that the printer's using:

CSV Output

Running the script returns no errors at all :/

Can an hand be given please? :D

Subsidiary question, why the hell is it taking like 5 minutes per Host. Can't this be quicker?

Thanks a lot to everyone that can help :)

Upvotes: 0

Views: 4584

Answers (2)

Theo
Theo

Reputation: 61028

Probably the main thing about your code is that, although you are working on remote machines, you use the local DriverPath to get the driver version and also do not specify the driver you are aiming for.

The code below uses a small helper function to get the driver version from the actual driver file on the server using the UNC path instead of the local path. Also, I think by using a function here can make the code more readable.

To try and speed things up, I'm using Get-CimInstance instead of Get-WmiObject and collect the objects without adding to a predefined array:

$ReportFileName   = 'C:\SAC_IS\printerreport.csv' 
$PrintServersList = 'C:\SAC_IS\servlist_TEST.txt' 

function Get-PrinterProductVersion ($server, $printer) {
    $prn = Get-CimInstance -ClassName Win32_PrinterDriver -ComputerName $server | 
           Where-Object { ($_.Name -split ',')[0] -eq $($printer.DriverName) }
    if ($prn) { 
        # transform the local driver path into a UNC path
        $path = Join-Path -Path "\\$server" -ChildPath ($prn.DriverPath -replace ':', '$')
        (Get-Item $path).VersionInfo.ProductVersion 
    }
}

$allprinters = Get-Content -Path $PrintServersList | ForEach-Object {
    # capture the server name in a self-defined variable
    $server = $_
    Write-Host "checking server $server ..." 
    Get-CimInstance -ClassName Win32_Printer -ComputerName $server | 
        Select-Object Name,Shared,ShareName,Local,DriverName,PortName, 
                      @{Name = "HostName"; Expression = { $server }},
                      @{Name = "Driver Version"; Expression = { Get-PrinterProductVersion $server $_ }}, 
                      Location,Comment,SpoolEnabled,Published
 } 

# output on screen
$allprinters

# output to CSV file
$allprinters | Export-CSV -Path $ReportFileName -Delimiter ";" -NoTypeInformation -Force -Encoding UTF8 

P.S. Personally, I wouldn't want a header with a space in its name like "Driver Version" but that's up to you


Edit

I cannot test this myself, but perhaps you can speed this up if you let the servers themselves gather the wanted printer info.

Something like this:

$ReportFileName   = 'C:\SAC_IS\printerreport.csv' 
$PrintServersList = 'C:\SAC_IS\servlist_TEST.txt' 

# create a scriptblock for each server to execute
$scriptBlock = {
    function Get-PrinterProductVersion ($printer) {
        # or try with 'Get-WmiObject -Class Win32_PrinterDriver' if Get-CimInstance not available
        $prn = Get-CimInstance -ClassName Win32_PrinterDriver | 
               Where-Object { ($_.Name -split ',')[0] -eq $($printer.DriverName) }
        if ($prn) { (Get-Item $prn.DriverPath).VersionInfo.ProductVersion }
    }

    # or try with 'Get-WmiObject -Class Win32_Printer' if Get-CimInstance not available
    Get-CimInstance -ClassName Win32_Printer | 
        Select-Object Name,Shared,ShareName,Local,DriverName,PortName, 
                      @{Name = "HostName"; Expression = { $env:COMPUTERNAME }},
                      @{Name = "Driver Version"; Expression = { Get-PrinterProductVersion $_ }}, 
                      Location,Comment,SpoolEnabled,Published


}

# create a list to gather all results from the servers
$allprinters = New-Object System.Collections.Generic.List[object]

Get-Content -Path $PrintServersList | ForEach-Object {
    Write-Host "checking server $_ ..." 
    if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
        $printers = Invoke-Command -ScriptBlock $scriptBlock -ComputerName $_
        $allprinters.AddRange($printers)
    }
    else {
        Write-Warning "Server $_ cannot be reached"
    }
 } 

# output on screen
$allprinters

# output to CSV file
$allprinters | Export-CSV -Path $ReportFileName -Delimiter ";" -NoTypeInformation -Force -Encoding UTF8 

Upvotes: 1

Scepticalist
Scepticalist

Reputation: 3923

Try it this way?

$servers | ForEach-Object {
    $CurrentServer = $_
    Write-Host "Checking $CurrentServer_ ..." 
    $Drivers = Get-PrinterDriver -ComputerName $CurrentServer | Select-Object Name, @{n="DriverVersion";e={ $ver = $_.DriverVersion;(3..0 | ForEach-Object { ($ver -shr ($_ * 16)) -band 0xffff }) -join '.'}}

    Get-WmiObject -class Win32_Printer -Computername $CurrentServer | ForEach-Object {
        $ThisPrintDriverName = $_.DriverName
        $ThisDriver = $Drivers | Where-Object { $_.Name -eq $ThisPrintDriverName }

        [pscustomobject]@{Name = $_.Name
                        Shared = $_.Shared
                        ShareName = $_.ShareName
                        Local = $_.Local
                        DriverName = $_.DriverName
                        PortName = $_.PortName
                        HostName = $CurrentServer
                        Location = $_.Location
                        Comment = $_.Comment
                        SpoolEnabled = $_.SpoolEnabled
                        Published = $_.Published
                        Driver = $ThisDriver.Name
                        Version = $ThisDriver.DriverVersion
                        }
    }
}

Upvotes: 1

Related Questions