After you use this let me know what you think, this was one of projects I looked for and couldn't find before I started to write it.
Lately I've been working on migrating a Windows 2008 Print Server from from 32 bit to Windows 2008 R2 64bit. During this I've been cleaning up unused printers and ports as my other post
suggested. Considering this print sever had nearly 500 printers installed worked on automating the process. Using Powershell I noticed many people complaining of poor integration of Powershell for Printer support. While I agree there should of been better built-in functions I was able to write every feature I wanted once I figured out how.
There where many posts online but none of them seemed to handle any issues that arrised. For example if you try and create a printer using [WMICLASS]"\\$ComputerName\ROOT\cimv2:Win32_Printer").createInstance() and call .Put() you may get the following error.
Exception calling "Put" with "0" argument(s): "Generic failure "
At C:\Users\towlesd\Documents\My Dropbox\PrinterInformationfor2008.ps1:319 char:24
+ [void] $newprinter.Put <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
This is caused because one of the pramaters you are trying to use isn't valid. It doesn't say that but thats what it means. Frustrating to say the least. The cause of this for me it was the print driver didn't exist on the server. Because of this my InstallPrinters function first checks that all printers and ports first exist on the remote server. Little things like this make the entire process possible.
You really need to break down how you want to use this script it self as there are alot of useful functions depending on what your working on. In all the script can gather all the printer information about a given server; Its printers, ports and drivers and export that to a csv-file. It can try to ping all the ports and detect which are offline or bad DNS entires.
Here are most of the Powershell functions
- Create Printer
- Create Port
- Install Array of Printers
- Get Installed Drivers
- Get Install Ports
- Match printers with their ports in a csv-file
- Create csv files for any of this information
- migrate printers from one serevr to another
Without further delay.
###########################
#
# Purpose: Printer API for Managing and migrating Printers
# Created By: Chris Towles
# Website: http://www.ChrisTowles.com
# When: 04/08/2011
# Version: 1.0
# Change List
#
###########################
function Ping() {
param ([string] $ComputerName)
[bool] $Pingable = $false
try {
$ping = new-object System.Net.NetworkInformation.Ping
$Reply = $ping.send($ComputerName, 1000)
if ($Reply.status –eq “Success”) {
$Pingable = $true
}
}
catch { return "DNS Issue" }
if ($Pingable) { return “Online” }
else { return "Offline" }
}
function Get-Nslookup() {
param ([string] $Name)
$DNSInfo = "" | select "DNSName", "DNS_IP"
$DNSInfo.DNSName = $null
$DNSInfo.DNS_IP = $null
try {
#http://powershellmasters.blogspot.com/2009/04/nslookup-and-powershell.html
$cmd = "nslookup " + $Name + " " + $ns + " 2>&1"
$result = Invoke-Expression ($cmd) -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
$DNSInfo.DNSName = $result.SyncRoot[3]
$DNSInfo.DNSName = $DNSInfo.DNSName.Split(":")[1]
$DNSInfo.DNSName = $DNSInfo.DNSName.Trim()
$DNSInfo.DNS_IP = $result.SyncRoot[4]
$DNSInfo.DNS_IP = $DNSInfo.DNS_IP.Split(":")[1]
$DNSInfo.DNS_IP = $DNSInfo.DNS_IP.Trim()
}
catch{
$DNSInfo.DNSName = $null
$DNSInfo.DNS_IP = $null
}
return $DNSInfo
}
Function Get-Printers( ) {
param ([string] $ComputerName = ".") #sets "." as the default param if none is supplied
Write-Host "Connecting to $ComputerName by WMI to gather printer information."
Write-Host " Warning this may take a few minutes depending how many printers there are." -ForegroundColor Red
$colItems = get-wmiobject -class "Win32_Printer" -namespace "root\CIMV2" -computername $ComputerName
$count = $colItems.Count
$pos = 0
Write-Host "Found $count printers. Getting details on each now."
$Printers = @()
foreach ($objItem in $colItems) {
write-progress -activity "Getting Information on each printer." -status "% Complete" -percentcomplete (($pos++/$count)*100);
#creating a new object called $PrinterInfo
$PrinterInfo = New-Object psobject
$PrinterInfo | Add-Member NoteProperty Caption $objItem.Caption
$PrinterInfo | Add-Member NoteProperty Comment $objItem.Comment
$PrinterInfo | Add-Member NoteProperty "Default" $objItem.Default
$PrinterInfo | Add-Member NoteProperty "DriverName" $objItem.DriverName
$PrinterInfo | Add-Member NoteProperty "Local" $objItem.Local
$PrinterInfo | Add-Member NoteProperty "Name" $objItem.Name
$PrinterInfo | Add-Member NoteProperty "PortName" $objItem.PortName
$PrinterInfo | Add-Member NoteProperty "PrinterStatus" $objItem.PrinterStatus
$PrinterInfo | Add-Member NoteProperty "PrintProcessor" $objItem.PrintProcessor
$PrinterInfo | Add-Member NoteProperty "Shared" $objItem.Shared
$PrinterInfo | Add-Member NoteProperty "ShareName" $objItem.ShareName
$PrinterInfo | Add-Member NoteProperty "Status" $objItem.Status
$PrinterInfo | Add-Member NoteProperty "SystemName" $objItem.SystemName
$PrinterInfo | Add-Member NoteProperty "WorkOffline" $objItem.WorkOffline
$Printers += $PrinterInfo
}
write-progress -activity "Getting Information on each Port." -status "% Complete:" -percentcomplete 100 -Completed;
return $Printers
}
function Get-PortInfo () {
param ([string] $ComputerName = ".",
[bool]$CheckPorts = $true) #sets "." as the default param if none is supplied
Write-Host "Connecting to $ComputerName by WMI to gather printer port information."
Write-Host " Warning this may take a few minutes because its pinging each port." -ForegroundColor Red
$colItems = get-wmiobject -class "Win32_TCPIPPrinterPort" -namespace "root\CIMV2" -computername $ComputerName
$count = $colItems.Count
$pos = 0
Write-Host "Found $count printer ports. Getting details on each now."
$Ports = @()
foreach ($objItem in $colItems) {
write-progress -activity "Getting Information on each Printer Port. " -status "% Complete:" -percentcomplete (($pos++/$count)*100);
$PortInfo = New-Object psobject
$PortInfo | Add-Member NoteProperty "ByteCount" $objItem.ByteCount
$PortInfo | Add-Member NoteProperty "HostAddress" $objItem.HostAddress
$PortInfo | Add-Member NoteProperty "HostAddressLink" ("http://" + $objItem.HostAddress + "/" )
$PortInfo | Add-Member NoteProperty "Name" $objItem.Name
$PortInfo | Add-Member NoteProperty "PortNumber" $objItem.PortNumber
$PortInfo | Add-Member NoteProperty "Protocol" $objItem.Protocol
$PortInfo | Add-Member NoteProperty "Queue" $objItem.Queue
$PortInfo | Add-Member NoteProperty "SNMPCommunity" $objItem.SNMPCommunity
$PortInfo | Add-Member NoteProperty "SNMPDevIndex" $objItem.SNMPDevIndex
$PortInfo | Add-Member NoteProperty "SNMPEnabled" $objItem.SNMPEnabled
if($CheckPorts) {
$pingstring = Ping -ComputerName $objItem.HostAddress
$PortInfo | Add-Member NoteProperty "Ping" $pingstring
$DNSInfo = get-nslookup -Name $objItem.HostAddress
$PortInfo | Add-Member NoteProperty "DNSName" $DNSInfo.DNSName
$PortInfo | Add-Member NoteProperty "DNS_IP" $DNSInfo.DNS_IP
}
$Ports += $PortInfo
}
write-progress -activity "Getting Information on each Port." -status "% Complete:" -percentcomplete 100 -Completed;
return $Ports
}
function Get-PrintersWithPort {
param ( [array] $Printers,
[array] $Ports )
$count = $Printers.Count
$pos = 0
$PrinterWithPortInfo = @()
Foreach($Printer in $Printers) {
write-progress -activity "Sorting Printers to their matching Ports." -status "% Complete" -percentcomplete (($pos++/$count)*100);
$Port = $Ports | where {$_.Name -like ($Printer.PortName) }
if($Port -ne $null) {
$properties = $Port | Get-Member -MemberType NoteProperty
foreach ($member in $properties )
{
$PropName = $member.Name
$PropValue = $Port | Get-Member -MemberType NoteProperty ($member.Name)
$PropValue = $PropValue.ToString().Split("=")[1]
if($PropValue -match "null")
{ $PropValue = ""}
$Printer | Add-Member NoteProperty ("Port_" + $Propname) $PropValue
}
$PrinterWithPortInfo += $Printer
}
}
write-progress -activity "Sorting Printers to their matching Ports. Phase 3 of 3." -status "% Complete" -percentcomplete 100 -Completed;
return $PrinterWithPortInfo
}
function Get-PortsWithPrinterAndOnline {
param ( [array] $Printers ,
[array] $Ports )
$PortsWithPrinterAndOnline = @()
Foreach($Printer in $Printers) {
$Port = $Ports | where { $_.Name -like ($Printer.PortName) }
if(($Port -ne $null ) -and ($Port.Ping -like "Online")) {
$PortsWithPrinterAndOnline += $Port
}
}
return $PortsWithPrinterAndOnline
}
function Get-PortsWithoutPrinters {
param ( [array] $Printers ,
[array] $Ports )
$PortsWithoutPrinters = @()
Foreach($Port in $Ports) {
$Printer = $Printers | where { $_.PortName -like ($Port.Name) }
if(($Printer -eq $null ) ) { #-and ($Port.Ping -like "Online")) {
#Port Has no Printer
$PortsWithoutPrinters += $Port
}
}
$PortsWithoutPrinters = $PortsWithoutPrinters | sort -Property "Name"
return $PortsWithoutPrinters
}
function Get-PrintersWithPortsOffLine {
param ( [array] $Printers,
[array] $Ports )
$PrinterWithPortOffLine = @()
Foreach($Printer in $Printers) {
$Port = $Ports | where { $_.Name -like ($Printer.PortName) }
if(($Port -ne $null ) -and ($Port.Ping -inotlike "Online")) {
$PrinterWithPortOffLine += $Printer
}
}
return $PrinterWithPortOffLine
}
function Get-PharosQueuesWithWInprintProcessor {
param ( [array] $Printers )
$WinPrint = @()
Foreach($Printer in $Printers) {
if($Printer.PrintProcessor -ine "WinPrint" -and $Printer.Name -like '*Q') {
$WinPrint += $Printer
}
}
return $WinPrint | sort "Name"
}
Function Get-InstalledPrinterDrivers () {
param ( [string] $ComputerName = "." )
$colItems = get-wmiobject -class "Win32_PrinterDriver " -namespace "root\CIMV2" -computername $ComputerName
$PrintDrivers = @()
foreach ($objItem in $colItems) {
#creating a new object called $PrinterInfo
$Driver = New-Object psobject
$Driver | Add-Member NoteProperty Name $objItem.Name.Split(",")[0]
$Driver | Add-Member NoteProperty Chip $objItem.Name.Split(",")[2]
$PrintDrivers += $Driver
}
return $PrintDrivers
}
function Create-PrinterPort {
param ( [string] $ComputerName = ".", #sets "." as the default param if none is supplied
[string] $PortName ,
[string] $DNSName ,
[int] $Protocol = 1, #default of 1 for RAW
[string]$Queue = $null,
[string]$PortNumber = "9100",
[bool] $SNMPEnabled ,
[string] $SNMPCommunity = $null)
$newPort = ([WMICLASS]"\\$ComputerName\ROOT\cimv2:Win32_TCPIPPrinterPort").createInstance()
$newport.Name = $PortName
$newport.HostAddress= $DNSName #sometimes IP Address
$newport.Protocol= $Protocol
#Protocols possiable
#1 = RAW, Printing directly to a device or print server.
#2 = LPR, Legacy protocol, which is eventually replaced by RAW.
if ($Protocol -eq 2 ) {
$newport.Queue = $Queue
}
$newPort.PortNumber = "9100"
$newport.SNMPEnabled = $SNMPEnabled
if( $SNMPCommunity -ne $null ) {
$newport.SNMPCommunity = $SNMPCommunity
}
Write-Host "Creating Port $PortName" -foregroundcolor "darkgreen"
[void] $newport.Put()
}
function Create-CSVOutputFile {
param ( [array] $CustomObject ,
[String] $filename )
#writing file to
Write-Host $filename
$CustomObject | Sort-Object "Name" | Export-Csv $filename
#removes the top line as it just has #TYPE System.Management.Automation.PSCustomObject in it.
get-content $filename | select -Skip 1 | set-content "$filename-temp"
if (Test-Path "$filename-temp") {
move "$filename-temp" $filename -Force
}
else {
Remove-Item "$filename"
Write-Host "$filename was deleted because nothing was in it."
}
}
function Create-Printer {
Param (
[string]$ComputerName = ".",
[string]$PrinterName,
[string]$DriverName,
[string]$Location,
[string]$ShareName,
[String]$PortName,
[string]$Comment
)
$newprinter = ([WMICLASS]"\\$ComputerName\ROOT\cimv2:Win32_Printer").createInstance()
$newprinter.Drivername = $DriverName
$newprinter.PortName = $PortName
$newprinter.Shared = $true
$newprinter.Sharename = $ShareName
$newprinter.Location = $Location
$newprinter.Comment = $Comment
$newprinter.DeviceID = $PrinterName
Write-Host "Creating Printer $PrinterName" -foregroundcolor "darkgreen"
[void] $newprinter.Put()
#Exception calling "Put" with "0" argument(s): "Generic failure "
# This Means their is something wrong with a paramter, Most likely its the driver name
}
function Install-PrinterPorts {
param ( [string] $ComputerName ,
[array] $Ports )
Write-Host "Creating the following " $Ports.Count " printer ports on " $ComputerName
foreach ($newport in $Ports) {
[bool] $SNMPEnabled = $false
if( $newport.SNMPEnabled -eq $true ) {
$SNMPEnabled = $true
}
Create-PrinterPort -ComputerName $ComputerName `
-DNSName $newport.HostAddress.ToUpper() `
-PortName $newport.Name.ToUpper() `
-PortNumber $newport.PortNumber `
-Protocol $newport.Protocol `
-SNMPEnabled $SNMPEnabled `
-SNMPCommunity $newport.SNMPCommunity `
-Queue $newport.Queue
}
}
Function Install-Printers {
param (
[string] $ComputerName = ".",
[array] $PrintersToCreate,
[array] $PortsToCreate,
[bool] $TestDriversAndPortsOnly = $false
)
$PrintersToInstall = @()
$MissingPort = @()
$MissingDriver = @()
#Create the Remote Ports
Install-PrinterPorts -ComputerName $ComputerName -Ports $PortsToCreate
#Check Print server for what ports and Printers are installed.
$InstalledDrivers = Get-InstalledPrinterDrivers -ComputerName $ComputerName
$InstalledPorts = Get-PortInfo -ComputerName $ComputerName -CheckPorts $false
#I wished to have all the names in uppercase only.
$Printer.PortName = $Printer.PortName.ToUpper()
foreach($Printer in $Printers)
{
$DriverFound = $InstalledDrivers | where {$_.Name -eq $Printer.DriverName}
$PortFound = $InstalledPorts | where {$_.Name -eq $Printer.PortName}
if($DriverFound -eq $null) {
#Write-Host ("The Print Driver isn't installed on the Destination Server" + $Printer.DriverName )
$MissingDriver += $Printer.DriverName
}
elseif($PortFound -eq $null) {
#Write-Host ("The Printer Port " + $Printer.Port_Name + " didn't existon the destination Server" + $Printer.DriverName )
$MissingPort += $Printer.PortName
}
else{
$PrintersToInstall += $Printer
}
}
Write-Host ""
Write-Host ("The following printer drivers are missing from " + $ComputerName )
$MissingDriver | Sort | Get-Unique | ft -AutoSize
Write-Host ""
Write-Host ("The following printer port didn't exist on " + $ComputerName )
$MissingPort | Sort | Get-Unique | ft -AutoSize
Write-Host ""
if ( $TestDriversAndPortsOnly -eq $false ) {
foreach($Printer in $PrintersToInstall) {
Create-Printer `
-PrinterName $Printer.Name `
-ComputerName $DestinationHost `
-DriverName $Printer.DriverName `
-PortName $Printer.PortName.ToUpper() `
-ShareName $Printer.ShareName `
-Comment $Printer.Comment `
-Location ""
}
}
}
#################################################################################
# MAIN #
#################################################################################
Clear
$ComputerName = Read-Host "Enter the Print Server you wish to gather data about."
#$DestinationHost = "dvprintserv1"
[bool] $QueryData = $true
[bool] $OutputFiles = $true
if( $QueryData ) {
#Gets all the information
$Printers = @() | Get-Printers -ComputerName $ComputerName
$Ports = @() | Get-PortInfo -ComputerName $ComputerName
$PrinterWithPortInfo = @() | Get-PrintersWithPort -Printers $Printers -Ports $Ports
$PortsWithoutPrinters = @() | Get-PortsWithoutPrinters -Printers $Printers -Ports $Ports
$PrintersWithOffLinePort = @() | Get-PrintersWithPortsOffLine -Printers $Printers -Ports $Ports
}
#Used to migrate printers from one server to another.
#Install-Printers -ComputerName $DestinationHost -PrintersToCreate $Printers -Ports $Ports #-TestDriversAndPortsOnly $true
if($OutputFiles) {
#Formated File Names
$d = get-date
$FileTime = "" + $d.Year + "-" + ("{0:D2}" -f $d.Month) + "-" + ("{0:D2}" -f $d.Day) + "-" + ("{0:D2}" -f $d.Hour ) + ("{0:D2}" -f $d.Minute)
$PortsFilename = [String]::Format( "{0}_Ports_{1}.csv", $ComputerName, $FileTime )
$PrintersFilename = [String]::Format( "{0}_Printers_{1}.csv", $ComputerName, $FileTime )
$PrintersWithPortFilename = [String]::Format( "{0}_PrintersWithPort_{1}.csv", $ComputerName, $FileTime )
$PortsWithoutPrintersFilename = [String]::Format( "{0}_PortsWithoutPrinters_{1}.csv", $ComputerName, $FileTime )
$PrintersWithOffLinePortFileName = [String]::Format( "{0}_PrintersWithOffLinePort_{1}.csv", $ComputerName, $FileTime )
#writing file
[Environment]::CurrentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
Write-Host ("Saving CSV Files at " + [Environment]::CurrentDirectory + " Named the following.")
Create-CSVOutputFile $Printers -filename $PrintersFilename
Create-CSVOutputFile $Ports -filename $PortsFilename
Create-CSVOutputFile $PrinterWithPortInfo -filename $PrintersWithPortFilename
Create-CSVOutputFile $PortsWithoutPrinters -filename $PortsWithoutPrintersFilename
Create-CSVOutputFile $PrintersWithOffLinePort -filename $PrintersWithOffLinePortFileName
}
Here an example of some output.
Connecting to dvprintserv1 by WMI to gather printer information.
Warning this may take a few minutes depending how many printers there are.
Found 123 printers. Getting details on each now.
Connecting to dvprintserv1 by WMI to gather printer port information.
Warning this may take a few minutes because its pinging each port.
Found 355 printer ports. Getting details on each now.
Saving CSV Files at C:\Users\towlesd\Documents Named the following.
dvprintserv1_Printers_2011-04-08-1211.csv
dvprintserv1_Ports_2011-04-08-1211.csv
dvprintserv1_PrintersWithPort_2011-04-08-1211.csv
dvprintserv1_PortsWithoutPrinters_2011-04-08-1211.csv
dvprintserv1_PrintersWithOffLinePort_2011-04-08-1211.csv