Showing posts with label Printers. Show all posts
Showing posts with label Printers. Show all posts

Saturday, July 2, 2016

PowerShell: Using DNS and Printer CSV List for IP-Based Printer Installs on Random Network Environments

I support incident management teams and have been trying to find a simple-ish way to have users install printers no matter our network environment.  I setup the same server every incident so that is where I place all scripts and drivers.  We perform massive printing in a short amount of time and this can cause printers to be replaced as they wear out.  Instead of visiting every networked laptop to update printers, I now have 10 static printer numbers and use a CSV file to give driver information, IP address, and printer name.  The user locates the nearest printer, double-clicks the installation batch file, and the process removes the old driver and IP address of that printer and installs the current settings.  This process has been tested for PowerShell version 4.0.
Steps in creating this printer installation process:
  1. Remote into each printer and setup a DNS name.  Ping the IP and DNS name after making the change.  Ensure you allow the printer to take the DNS suffix of the local DHCP server.  Use DHCP.
  2. Download and test the driver that'll be used.  Find the correct INF file that has the printer driver name.  You'll have to add the printer driver name and driver shared folder location to the printerlist.csv file.
  3. Label your printers so users know which printer they need to install.
  4. Create your batch files which start the install process:

@CLS
@powershell -command "start-process powershell" -verb runas -argumentlist '-ExecutionPolicy bypass -file \\server\printers\InstallPrinter.ps1 Team12P1'
    • @cls clears screen to get rid of extraneous messages while starting script
    • @powershell -command "start-process powershell" -verb runas elevates PowerShell to runas Administrator and bypass the UAC settings which can sometimes stop driver installations
    • -argumentlist '-ExecutionPolicy bypass -file \\server\printers\InstallPrinter.ps1 Team12P1' bypasses any PS script execution policy setting that may be set on that computer.  Then it runs the InstallPrinter.ps1 script and passes the Team12P1 variable to that script so it can look up the printer in the CSV file. Create separate batch files for each printer and change the variable to the DNS name of that printer.
    1. Create and input printer information into the printerlist.csv file.  Here is a sample:

    DNSName,DriverShare,DriverINF,DriverName,LastKnownIP,MakeModel,Supplies,Notes
    Team12P1,\\Team12server\printers\Drivers\HPOJP8630,hpvyt13.inf,HP Officejet Pro 8630,192.168.1.100,HPOJP8630,951XL,
    Team12P2,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP400MFP,80X,
    Team12P3,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP400MFP,80X,
    Team12P4,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP400MFP,80X,
    Team12P5,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP4015,64A,
    Team12P6,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP4015,64A,
    Team12P7,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJ600,90A,
    Team12P8,\\Team12server\printers\Drivers\HPOJP8630,hpvyt13.inf,HP Officejet Pro 8630,192.168.1.100,HPOJP8630,951XL,
    ,,,,,,,
    Team12P10,\\Team12server\Printers\Drivers\HPCLJ5520,hpc5520u.inf,HP Color LaserJet CP5520 Series PCL 6,192.168.1.100,HPCLJ5525,650A,USB and WiFi Only
    Team12L1,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,,HPLJ1102W,85A,
    Team12L2,,,,,EpsonWF3640,Epson252XL(BCYM),Ordering Manager
    
    • DNSName: Printer name DNS will use to search for an IP
    • DriverShare: Printer driver UNC folder location
    • DriverINF: Printer driver INF file located at the DriverShare location
    • DriverName: Exact printer name found within the Driver INF file
    • LastKnownIP: Static IP used to create a port when the printer and laptop are on different networks. You will have to create a new IP Port when the laptop is on the same network as the printer.
    • MakeModel: Not used in scripts but for your reference
    • Supplies: Not used in scripts but place to note what print cartridges each printer needs
    • Notes: Not used in scripts but provides general information
    1. Create the InstallPrinter.ps1 file referenced in the initial printer installation batch file.  Here is the script I use:

    #No 32-bit support
    $DNSName = $args
    $printerlist = import-csv \\server\printers\installscripts\printerlist.csv|? DNSName -eq $DNSName
    $DriverShare = $printerlist.drivershare
    $DriverINF = $printerlist.driverinf
    $DriverName = $printerlist.drivername
    $LastKnownIP = $printerlist.lastknownIP
    $IP = $LastKnownIP
    
    #Checking for old version of printer, then deleting it so new version can be added
    if ((gwmi win32_service|? name -match spool|select -expand state) -eq 'Stopped'){start-service spooler}
    if ((gwmi win32_printer|select -expand name) -contains $DNSName){
    #This will remove any spooled jobs then delete the printer with the old IP address
    if ((gwmi win32_service|? name -match spool|select -expand state) -eq 'running'){stop-service spooler}
    remove-item c:\windows\system32\spool\printers\*.* -force
    do {sleep 1}until((gwmi win32_service|? name -match spool|select -expand state) -eq 'Stopped')
    start-service spooler
    sleep 5
    $settings = gwmi win32_printer|? name -eq $DNSName|select *
    write-host "`nDeleting old printer installation`n"
    cscript c:\windows\system32\printing_admin_scripts\en-us\prnmngr.vbs -d -p $DNSName
    $Oldport = $settings.portname
    write-host "`nDeleting old Port: $Oldport`n" -f blue
    $portmsg = cscript c:\windows\system32\printing_admin_scripts\en-us\prnport.vbs -d -r $Oldport
    $d = cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -x
    }
    
    write-host ("Printer: " + $DNSName)-f green
    if ($IP.count -gt 0){write-host ("IP address: " + $IP)-f green}else{write-host ("No Active IP, using last known IP: " + $IP)-f red}
    write-host ("`n$DNSName will be available once this window disappears (approx. 3 minutes).`n") -f yellow
    $delprinters = cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -x
    cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -a -m $DriverName -h $DriverShare -i ($DriverShare + "\" + $DriverINF)
    cscript c:\windows\system32\printing_admin_scripts\en-us\prnport.vbs -a -r $IP -h $IP -o raw -n 9100
    #rundll32 printui.dll,PrintUIEntry /if /b $DNSName /r $IP /m $DriverName
    cscript c:\windows\system32\printing_admin_scripts\en-us\prnmngr.vbs -a -p $DNSName -m $DriverName -r $IP
    if ((gwmi win32_printer|select -expand name) -contains $DNSName){write-host "`n$DNSName installed correctly!" -f green;sleep 5}
    

    Now you have a generic printer installation process that you can easily alter when you need to update IPs, Printer Names, or Printer Driver information.  As long as you've renamed your printers DNS names correctly, they should be found on any network subnet you share.  Also, the printerlist.csv file is easy to load into Excel and use for other things besides your installation scripts.

    Last note: In my scenario, I have this system connected to a Box account so all changes are backed up to the cloud and I can update these files from the cloud outside the local subnet/network.

    Sunday, December 21, 2014

    PowerShell: Using SNMP for HTML Network Printer Report

    Managing network printers scattered throughout numerous locations is challenging when ensuring each location has replacement ink at the appropriate time.  A stopped printer loses production time and so there should be an easy way to capture current information about all network printers in a easy-to-use/share format.  As HTML is accepted by any computer and device, I thought it'd be best to use that format.

    I've been searching for a way to aggregate the printers websites in order to create a solution but that didn't work so I wondered if I could pull information via a "public" SNMP connection using PowerShell.  After many online searches, I found someone calling on the ComObject ole.Prn.OleSNMP.  

    $SNMP = new-object -ComObject olePrn.OleSNMP



    The SNMP object has a slight variation of the standard SNMP syntax.  The GetTree function is AKA SNMPWalk to those familiar with SNMP commands.  

    $snmp.open("192.168.0.28","public",2,3000)
    defined: (IP,account,how many attempts, how long before timeout)



    Once connected, use the GetTree to find out information about the area you want in your report.  In my example, I'm querying for the maximum volume of toner.

    $snmp.gettree("43.11.1.1.8.1")

    The OIDFromString function converts a name to its OID location.



    For instance:

    $snmp.OIDFromString(".iso.org.dod.internet.mgmt.mib-2.system.sysDescr")



    To capture an object:
    $printertype = $snmp.Get(".1.3.6.1.2.1.25.3.2.1.3.1")

    Since SNMP is a standard, most objects are in the same location across vendors. One area I found variance is the toner/waste container location. In printers with only one toner cartridge and no waste container, it's a standard:

    $tonername = $snmp.get("43.11.1.1.6.1.1")
    $tonercapacity = $snmp.get("43.11.1.1.8.1.1")

    $currentvolume = $snmp.get("43.11.1.1.9.1.1")

    Example of multi-cartridge printer (notice the last digit):

    $blacktonercapacity = $snmp.get("43.11.1.1.8.1.1")
    $blackcurrentvolume = $snmp.get("43.11.1.1.9.1.1")
    [int]$blackpercentremaining = ($blackcurrentvolume / $blacktonercapacity) * 100 
    $cyantonervolume = $snmp.get("43.11.1.1.8.1.2")
    $cyancurrentvolume = $snmp.get("43.11.1.1.9.1.2")
    [int]$cyanpercentremaining = ($cyancurrentvolume / $cyantonercapacity) * 100
    $magentatonervolume = $snmp.get("43.11.1.1.8.1.3")
    $magentacurrentvolume = $snmp.get("43.11.1.1.9.1.3")
    [int]$magentapercentremaining = ($magentacurrentvolume / $magentatonercapacity) * 100
    $yellowtonervolume = $snmp.get("43.11.1.1.8.1.4")
    $yellowcurrentvolume = $snmp.get("43.11.1.1.9.1.4")
    [int]$yellowpercentremaining = ($yellowcurrentvolume / $yellowtonercapacity) * 100
    $wastetonervolume = $snmp.get("43.11.1.1.8.1.10")
    $wastecurrentvolume = [math]::round([math]::abs($snmp.get("43.11.1.1.9.1.10")))

    [int]$wastepercentremaining = (($wastecurrentvolume / $wastetonercapacity) * 100)

    Amongst the printers, all have their error status location in the same OID.  Here's how I filtered my printers:

    $statustree = $snmp.gettree("43.18.1.1.8")
    $status = $statustree|? {$_ -notlike "print*"} #status, including low ink warnings

    $status = $status|? {$_ -notlike "*bypass*"}

    That's the basics to extracting printer information via SNMP.  Here's how to explore your printers information using the free SNMP software which helped me out.
    http://www.manageengine.com/products/mibbrowser-free-tool/

    Clear out all the MIBs in the module area then File, Open the Printer MIB.
    Once the Printer MIB has been loaded, start exploring by opening the tree and finding an area of interest.  Right click on the folder and select SNMPWALK.
    Here's an example of results of an OID folder search.
    If you right-click on the folder and select "MIB Description", it'll give the OID location.  Notice the .1.3.6.1.2.1.43.1.1, that will be used in your script.
    Here I've copied/pasted the OID into my command line.  Notice the difference between the GUI and command line.  It's easy to navigate once you get to figuring out the structure.

    Once you've found the SNMP information you'd like to report about, then it's time to build an input file and script.  This is how I've built my solution, obviously there are several other methods to extracting and reporting this information.

    Input text file (printerlist.txt):

    192.168.0.236,PX_SPS_FIN_BW,Finance Printer
    192.168.0.237,PX_SPS_FP_COLOR,Forest Practices
    192.168.0.238,PX_SPS_MFC1,Main Color Copier
    192.168.0.254,PH_SPS_PLT,Plotter
    -Mineral
    192.168.1.250,PX_MIN_BW
    192.168.1.235,PX_MIN_MFC

    Here's my script so you can download and customize for your use:

    What you'll see when running the report (note that the printer names are coming directly from SNMP):


    Script:
    (By the way, I style my PowerShell scripts as if they were DOS batch files.  I've been writing scripts since DOS 5.0/Novell 3.x using DOS Edit and Windows Notepad.  Some habits are hard to break so thus this script is not in the typical PS script layout.)
    In this example, I only give one printer 'if' statement.  You'll have to use the $printertype = $snmp.Get(".1.3.6.1.2.1.25.3.2.1.3.1") to gather printer type information for your 'if' statements:

    $printerlist = import-csv .\printerlist.txt -header Value,Name,Description
    $outfile = .\Printers.html
    $SNMP = new-object -ComObject olePrn.OleSNMP
    $total = ($printerlist.value|? {$_ -notlike "-*"}).length

    Write "`
    <html>`
    <head>`
    <title>Printer Report</title>`
    <style>* {font-family:'Trebuchet MS';}</style>`
    </head>`
    <body>"|out-file $outfile

    write "Reporting on $total printers"
    $x = 0

    foreach ($p in $printerlist){

    if ($p.value -like "-*"){write "<h3>",$p.value.replace('-',''),"</h3>"|add-content $outfile}

    if ($p.value -notlike "-*"){

    $x = $x + 1
    $printertype = $nul
    $status = $nul
    $percentremaining = $nul
    $blackpercentremaining = $nul
    $cyanpercentremaining = $nul
    $magentapercentremaining = $nul
    $yellowpercentremaining = $nul
    $wastepercentremaining = $nul

    if (!(test-connection $p.Value -Quiet -count 1)){write ($p.value + " is offline<br>")|add-content $outfile}
    if (test-connection $p.value -quiet -count 1){
    $snmp.open($p.value,"public",2,3000)
    $printertype = $snmp.Get(".1.3.6.1.2.1.25.3.2.1.3.1")
    write ([string]$x + ": " + [string]$p.Value + " " + $printertype)
    }

    if ($printertype -like "*WorkCentre 5655*"){

    $tonervolume = $snmp.get("43.11.1.1.8.1.1")
    $currentvolume = $snmp.get("43.11.1.1.9.1.1")
    [int]$percentremaining = 100 - (($currentvolume / $tonervolume) * 100) 

    $statustree = $snmp.gettree("43.18.1.1.8")
    $status = $statustree|? {$_ -notlike "print*"} #status, including low ink warnings
    $status = $status|? {$_ -notlike "*bypass*"}
    $name = $snmp.get(".1.3.6.1.2.1.1.5.0")
    if ($name -notlike "PX*"){$name = $p.name}

    write ("<b>" + $p.description + "</b><a style='text-decoration:none;font-weight:bold;' href=http://" + $p.value + " target='_new'> " + $name + "</a> <br>" + $printertype + "<br>")|add-content $outfile
    if ($percentremaining -gt 49){write "<b style='font-size:110%;color:green;'>",$percentremaining,"</b>% black toner<br>"|add-content $outfile}
    if (($percentremaining -gt 24) -and ($percentremaining -le 49)){write "<b style='font-size:110%;color:#40BB30;'>",$percentremaining,"</b>% black toner<br>"|add-content $outfile}
    if (($percentremaining -gt 10) -and ($percentremaining -le 24)){write "<b style='font-size:110%;color:orange;'>",$percentremaining,"</b>% black toner<br>"|add-content $outfile}
    if (($percentremaining -ge 0) -and ($percentremaining -le 10)){write "<b style='font-size:110%;color:red;'>",$percentremaining,"</b>% black toner<br>"|add-content $outfile}
    if ($status.length -gt 0){write ($status + "<br><br>")|add-content $outfile}else{write "Operational<br><br>"|add-content $outfile}
    }
    }
    }

    write "</body></html>"|add-content $outfile

    &$outfile

    Sample output:








    Monday, November 25, 2013

    CMD: Install Windows 7 IP-based Printers via batch file

    IT Departments debate over Server-based and IP-based direct printing.  I use IP-based printing as a way to lower WAN traffic, negate at least two points of failure, and speedup user response/print time.  Microsoft has developed some fine printer scripts they introduced in Windows XP.  They further polished these scripts in Windows 7 and placed them into their own printing_admin_scripts subfolder.  I'm sharing my sample script and giving a quick explanation of each command.  For more details about the Microsoft VB script switches, visit Microsoft's TechNet site or use Notepad to read the syntax notes placed inside each VBS file.

    REMARK                   --- Start of Script ---

    REMARK Added a script title so I know what printer I'm installing

    echo *** Installing Canon iPF720 Plotter ***

    REMARK Remove old printer presence via PowerShell registry search

    PowerShell "if (test-path 'hklm:\software\microsoft\windows NT\currentversion\Print\Printers\HP DesignJet 1050C Plotter'){cscript c:\windows\system32\printing_admin_scripts\en-us\prnmngr.vbs -d -p 'HP DesignJet 1050C Plotter'}"

    REMARK Checking for processor type then installing appropriate driver
    REMARK AMD64 works with all typical Windows useable 64-bit processors

    if %processor_architecture% equ AMD64 (cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -a -m "Canon iPF720" -h "\\server\share\Printers\Drivers\Canon iPF720\64-bit\Driver" -i "\\server\share\Printers\Drivers\Canon iPF720\64-bit\Driver\6WJF02M.INF") else (cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -a -m "Canon iPF720" -h "\\server\share\printers\Drivers\Canon iPF720\Driver" -i "\\server\share\printers\Drivers\Canon iPF720\Driver\2WJF02M.INF")

    REMARK Adding the IP port the new printer will use

    cscript c:\windows\system32\printing_admin_scripts\en-us\prnport.vbs -a -r "CanoniPF720" -h 192.168.1.100 -o raw -n 9100

    REMARK Putting the Friendly name, driver, and port together

    cscript c:\windows\system32\printing_admin_scripts\en-us\prnmngr.vbs -a -p "Canon iPF720 Plotter" -m "Canon iPF720" -r "CanoniPF720" 

    REMARK Added a 10 second pause so I can chain install scripts together yet still monitor each installation progress

    timeout /t 10

    REMARK                   --- End of Script ---

    I also thought about adding an audible prompt when things go right or wrong.  This would lessen the need to physically watch the install but rather allow multitasking by listening out for the correct sounds after each install while performing other duties.

    Thursday, June 7, 2012

    (error 0x000006d9) Printer Sharing Error

    Windows 7 and 2008 R2 both have a weird problem when sharing printers.  I found this out when trying to share a generic/text only printer.  At first I thought it was invalid characters in the printers name or the spaces I used, but thanks to an article by Tomas, I got it working.  Here's how:

    1.  Runas /u:administrator net start "windows firewall"  (you'll have to know the local admin password)
    2. Share your printer
    3. Runas /u:administrator net stop "windows firewall"

    Leave a comment if this doesn't work for you.