What about Printer Drivers

Last Updated on November 17, 2022 by rudyooms

After reading a question on the Technet community about how to deploy printers with a printer driver to Azure Ad joined devices, I realized I only created a blog about the wonderful Microsoft Universal Cloud Print solution! So here we go!

I will divide this blog into multiple Parts/options to deploy your printers.

  1. Introduction
  2. Installing Printers and Drivers with the PnPutil
  3. Installing All Printers and Drivers with Printbrm.exe
  4. Install HP Printers with the Universal Printer Driver.
  5. Install Printer Drivers with PrnDrvr.vbs
  6. PowerShell Execution Policy
  7. But What About the Printer Configurations?

1. Introduction

Of course, transforming to a modern workplace will also mean you need to do something with your existing Print solution. One of the best solutions would probably be to start using the Microsoft Universal Print solution. In one of my first blogs I was telling you how you could configure this and what my wishes were for this fantastic product:

But what, if you don’t have the possibility to migrate your printers to this solution right away? I know how it works! A customer calls and he/she suddenly wants to deploy a new printer to all of their devices and they don’t want to hear they need to move their printers to cloud-based solutions first. So what can you do? Telling the customer: No No?

The best option would be to install the printer first and arrange a meeting with the customer to urge them to move their printers to a cloud-based solution.

So for now the printer has to be installed. Are we letting the customer do this manually as I showed you in this blog? P.S I am also showing you how to solve the Printer Nightmare issue in this blog

Or are we going to make sure the Printer and drivers are installed without any user interaction?

2. Installing Printers and Drivers with PnpUtil

In this option, we are going to use the PnPutil tool to “Stage” the Printer Driver to the Windows Driver Store. The driver needs to be “staged” (Copied to the Driver Store AKA /Add-driver) before we can make use of it to install the printer driver (Add-PrinterDriver). After we have staged and installed the required drivers, we can install the printer itself!

Let start with downloading the driver first, so we can begin!

Step 1: Download the driver

In this example, I want to deploy a Toshiba printer, so I downloaded the Universal printer drivers from Toshiba first and placed them inside the folder c:\intune\toshiba

After the zip file finished downloading, I extracted the whole zip file to start searching for the inf file (installation file) After I found the correct folder with the inf file in it, I copied it to the whole folder to c:\intune\toshiba\

To be 100% sure I had all the files needed, I moved this whole folder inc all the content to the c:\intune\toshiba root folder. When you don’t want to copy all of the content, you could check out the inf file and search for: “SourceDisksNames

As shown above, it will tell you which additional files you need.

Please note, that you always need to have the *.Inf file (installation file with driver information) and the *.cat file (security catalog to determine if it’s a valid signed driver). So next to the inf and cat file you will need that *.cab file

I renamed the folder that I copied earlier, to one with some fewer characters. I decided to just rename the folder to drivers. I guess that name says it all and it’s way shorter..

Step 2: Creating the Install script

Before we can create the installation script we need to have some additional information. So again, open the *.inf file in the folder you extracted earlier.

We need to have the exact Printer Driver Name. As shown above, it was an easy find! So let’s build the install.ps1 PowerShell script. Before I continue I need to add an additional Note:

Please Note: do not change the Sysnative folder to System32! I am explaining that part below the script

Install.ps1

###############################
#TOSHIBA Universal Printer 2  #
################################
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$drivername = "TOSHIBA Universal Printer 2"
$portName = "IP_192.168.40.251"
$PortAddress = "192.168.40.251"

###################
#Staging Drivers   #
###################
C:\Windows\SysNative\pnputil.exe /add-driver "$psscriptroot\Drivers\eSf6u.inf" /install

#######################
#Installing Drivers   #
#######################

Add-PrinterDriver -Name $drivername

##########################################################
#Install Printerport | check if the port already exist   #
##########################################################
$checkPortExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue
if (-not $checkPortExists) 
{
Add-PrinterPort -name $portName -PrinterHostAddress $PortAddress
}

####################################
#Check if PrinterDriver Exists     #
####################################
$printDriverExists = Get-PrinterDriver -name $DriverName -ErrorAction SilentlyContinue


##################
#Install Printer #
##################
if ($printDriverExists)
{
Add-Printer -Name "Toshiba HoofdKantoor" -PortName $portName -DriverName $DriverName
}
else
{
Write-Warning "Printer Driver not installed"
}

SLEEP 360

Did you notice the part where I am mentioning Sysnative instead of System32? If you want to read all about this, please read this blog:

And of course, we need to specify a nice uninstall.ps1, that’s going to be a lot easier!

Uninstall.ps1

remove-printer "Toshiba Hoofdkantoor"
SLEEP 360
remove-printerPort -Name "192.168.40.251" 

3. Creating the Intunewinapp

Now we have everything we need, we need to create the intunewinapp, so download the tool and zip it as shown below:

When we have created the intunewinapp we still need to publish it. To do so, open Intune and start creating a new Win32 app with the settings I show below

Please make sure you add the “-Executionpolicy Bypass” switch. I will tell you more about the execution policy in part 6!

Detection Rules

Of course, we need to specify detection rules. We need to make sure when the printer is not installed it will be installed. The best method would be to choose a registry detection rule. But how do we know what key we must use? That’s easy… just open the registry and browse to this registry key to find your printer:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Printers\

You will notice all printers are listed here. We can use the same value as we configured in the install.ps1 as the printer name. In this example, I am using: Toshiba Hoofdkantoor

As shown above, I am making use of the “detection method“: String Comparision to find the Registry Value: “Name” and detect if it is the same as “Toshiba Hoofdkantoor”. You can check out the registry value “Name” in the registry key I mentioned above to be 100% you get it right.

Assign the win32app to the correct users/groups/devices as required (or available if you want the let the end-user install the printer themselves when they really need it )

Results:

After the Win32 App finished installing, we notice a new printer has been installed. Let’s check it out!

Looking at the printer port, it has the proper TCP/IP port attached to it

And of course, it has exactly the same printer driver which we specified to be installed!

4. Finetuning

Of course, we don’t want every page to be printed in color, so change the color setting to $false to make sure you will save some money.

I will also disable the duplex option and specify the paper size in this example. You can add this part to the end of the install.ps1 script to make sure the printer settings are changed when the printer is installed.

Set-PrintConfiguration -PrinterName “Toshiba hoofdkantoor” -PaperSize A4 -color $false -duplexingmode onesided

3.Installing Printers and Drivers with Printbrm.exe

Just like PnPutil, it’s a nice build-in utility… so why not use it? A big difference with the PnPutil.exe is that with this nice tool you can export ALL Printers and their corresponding drivers to one big Printerexport file.

printbrm.exe -B -F c:\test.printerexport

Please beware: PrintBRM is not be included in the Windows PATH but will be found at c:\windows\System32\spool\tools\printbrm.exe

So when creating your PowerShell script and converting to an Intunewin App together with the Printerexport, please make sure you define the full path to the PrintBRM tool! Your restore command would be c:\windows\System32\spool\tools\printbrm.exe -R -F .\exportfile.printerexport -O

Now the only thing left for you to do is to create the IntuneWinApp and upload it to Intune!

4. Install Hp Printers with the Universal Printer Driver

Someone mentioned in the comments that it’s also possible to use the HP universal printer driver (install.exe) to make sure the Printer and drivers are installed correctly.

First, you will need to download the Universal Printer Driver from the Hp website or this link.

Make sure you extract the files to a folder named HP. Now create a PowerShell script and name that one install.ps1

$PortName = "PortName"
$PortAddress = "xxx.xxx.xxx.xxx"
$NewPrinter = "PrinterName"
$checkPortExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue
if (-not $checkPortExists) {Add-PrinterPort -name $portName -PrinterHostAddress $PortAddress}

.\hp\install.exe /n "$NewPrinter" /sm "$PortName" /h /q /nd /u

Sleep -Seconds 30

The install switches are: /h = hide installer /q = Quiet no user interaction /nd = Not default /u = Don’t install driver if already present.

Convert that whole folder to an Intunewinfile (the .\hp\ folder is also included). Upload this file to Intune and make sure you configure the install command like this:

powershell.exe -executionpolicy Bypass -file .\install.ps1

Of course, with every Win32App you will need to have a detection rule…. You could set up one by using the registry to detect if the printer already exists by specifying this key:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Printers\PrinterName (change the printer name to match the one from the script)

5. Install Printer Drivers with PrnDrvr.vbs

Another cool possibility is to just use the old PrnDrvr.vbs file and combine it with Cscript. This Prndrvr.vbs can be used to install or delete a driver. This file can be found in your Printing_Admin_Scripts folder as shown below

Of course, like always before we can add the Driver, we need to add the Printer Port first, just like we did with the HP universal driver.


$PortName = "PortName"
$PortAddress = "xxx.xxx.xxx.xxx"
$NewPrinter = "PrinterName"
$checkPortExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue
if (-not $checkPortExists) {Add-PrinterPort -name $portName -PrinterHostAddress $PortAddress}

And now we can use Cscript to call upon the visual basic script file to install the driver.

cscript "C:\Windows\System32\Printing_Admin_Scripts\en-US\prndrvr.vbs" -a -m "Canon Generic Plus PCL6" -i ".\GPlus_PCL6_Driver_V250_32_64_00\x64\Driver\CNP60MA64.INF" -h ".\GPlus_PCL6_Driver_V250_32_64_00\x64\Driver" -v 3

Add-Printer -Name $newprinters -PortName $portname  -DriverName "Canon Generic Plus PCL6"

When you combine both of these scripts to one PowerShell script and add the Driver to the folder you can convert it to a nice Win32app and deploy it with Intune to your devices

6. PowerShell Execution Policy

Because I have been getting some questions about the PowerShell Execution Policy, I will try to get some stuff clarified about the PowerShell Execution policy

6.1. IntuneWinapp/Win32 App

When you have configured a new Intunewinapp/Win32App, and you want to make sure your PowerShell script is going to be executed successfully. You need to make sure this PowerShell Script is executed with the proper policy to bypass the execution policy. You can so by simply execute Powershell.exe -ExecutionPolicy ByPass -File “.\full-script-path\script.ps1”

When using this option Microsoft tells us this: “Nothing is blocked and there will be no warnings or prompts”

6.2. PowerShell Scripts

When you have deployed a new PowerShell Script to Intune the Intune Management Extension (IME) will make sure this PowerShell script is executed on the device.

The IME doesn’t require any change whatsoever to the PowerShell Execution policy on the device. The IME will take care of the correct execution of your PowerShell scripts.

6.3. Set-ExecutionPolicy at the Top!

When you have the bright idea to push a PowerShell script with a set-executionpolicy at the top of it to change the execution policy, that isn’t going to work! Because the PowerShell execution policy is checked before the script is run so changing it within the script has no value or meaning. It really sounds like the chicken and the egg situation?

6.4. Changing the MachinePolicy

Also changing the execution MachinePolicy on devices with the use of a PowerShell script deployed by the IME and using Set-ExecutionPolicy is not going to work

As shown above, the PowerShell command is not able to modify the MachinePolicy. Changing the MachinePolicy for Execution Policy is only allowed with the use of a Group Policy only. (by default)

Why I am mentioning the by default? As you “could” change it with the use of Regedit as the Group Policy just equals some registry values

Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell

6.5. Beware of Applocker

Also, I need to point out, that when running a PowerShell script in the user context, please beware of the fact when you have configured Applocker, all of your PowerShell scripts will be run in. Constrained Language mode.

When trying to execute a script you will get a nice error “Cannot invoke method. Method invocation is supported only on core types in this language mode.”

7. What About the Printer Configs?

That’s indeed a very good question! Until now I only showed you how you could solve the missing Drivers but now we still need to take a look at how we could export and import the Printer Configurations.

To do so we need to use Printui.exe. You can find this wonderful tool and its corresponding Dll inside the system32 folder.

To get a good understanding of all the options we have let’s open it

As shown above, this tool could easily Store And Restore its Printer settings. Now let’s try it out. In this example, I exported my Lexmark Printer Configuration with the command Printui.exe /Ss /n “Lexmark XC4140 CVB” /a “c:\temp\config.dat”

Now we have the DAT file, we could add this file to the same folder we used while Installing Printers and Drivers with PnpUtil.

With the config file in place, we could add a Driver Configuration Restore command to the PowerShell script we created to install those drivers. To restore drivers we only need to change 1 letter!

Printui.exe /Sr /n “Printer Name” /a “.\config.dat”

If you don’t want to use the Printui.exe you could also just use the corresponding Printui.DLL as shown below

rundll32 printui.dll,PrintUIEntry /Sr /n $PrinterName /a “.\config.dat” d g r

Conclusion

When migrating to a full-based cloud solution you will need to migrate your printer solutions also. Microsoft Universal Print should be your end goal but in the meantime, you could take a few baby steps and make sure some printers are still installed when necessary.

28 thoughts on “What about Printer Drivers

  1. Thank you for posting this. I’m trying to do exactly what you described. For some reason my app is still failing. Not sure what is causing the failure. I’m having a hard time finding the exact logs I should look at to troubleshoot the problem.

    1. WHen the app is failed installing, the intune management log would be the first place to start. There are a millions reasons why, the app failed to install..
      If you post/send me the script etc I could test if for you to see what happens

  2. In the post PrintNightmare world most of the ability to install drivers using powershell commands have fail. I have resorted using PowerShell to create the Port. Because most of our printers are HP and Universal Print Driver compatable I was able to use the Install.exe from the Basic Driver with Install from HP.

    My IntuneWin package consisted of the Driver files and the Install and Uninstall PS Scripts. I ran this Install Powers shell scrip with the Install command of: powershell -executionPolicy bypass -Command “Start-Process PowerShell -Verb RunAs” .\install.ps1
    ___________
    Script:
    $PortName = “West_Local”
    $PortAddress = “xxx.xxx.xxx.xxx”
    $NewPrinter = “West_Local”
    $checkPortExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue
    if (-not $checkPortExists) {Add-PrinterPort -name $portName -PrinterHostAddress $PortAddress}

    .\install.exe /n”$NewPrinter” /sm”$PortName” /h /q /nd /u

    Sleep -Seconds 30
    _____________________________________
    The install switches are /h = hide installer /q = Quiet no user interaction /nd = Not default /u = Don’t install driver if already present.

    I did Sleep for 30 seconds because with out it the detection fails because it tries to detect before the install is complete.

    Detection is the presence in the registry = HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Printers\West_Local

    This works everytime.

    1. There are multiple methods to push printer drivers to the devices indeed… I will add your solution to the blog… so people can choose which option they prefer (of don’t have hp printers 🙂 )
      Can you post the link to the proper install.exe file?

    2. Thanks for this but I couldn’t get it to work, perhaps HP or a PrintNightmare patch has changed something. The first part works to create the port but I cannot get install.exe to use this port, it always adds another. I was able to get it working using a one liner in my install.ps1 script:
      Start-Process ‘.\install.exe’ -argument ‘/n”HP Printer” /sm192.168.1.200 /h /q /nd /u’ -wait
      Install command: powershell.exe -ExecutionPolicy Bypass -File install.ps1
      My next issue is I need to disable SNMP after the printer is installed, I have that in my script but it doesn’t process. When installing from the company portal, it just sticks at installing even though the reg key listed in the detection section exists in the reg.

  3. I’m doing exactly the same thing as you discribe in your blog but the install script woun’t start at devices with execution policy set do “restricted”.
    As soon as I set the execution policy on the machine to “unrestricted” the install works perfect.
    But the default value for the execution policy is “restricted” so the install woun’t run on any unmodified machine.

    Have you any ideas to this?

      1. Hi,

        How did you configured the Intune install cmd? did you add the -executionpolicy bypass to it? Like I am describing in part 6 of the blog?

  4. Hi I am having trouble installing and getting it to work tried two methods from the blog and both failed :(.

    Is this correct what I have done

    $PortName = “10.240.45.145”
    $PortAddress = “10.240.45.145”
    $NewPrinter = “WPA Printer”
    $checkPortExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue
    if (-not $checkPortExists) {Add-PrinterPort -name $portName -PrinterHostAddress $PortAddress}

    .\hp\install.exe /n “$NewPrinter” /sm “$PortName” /h /q /nd /u

    Sleep -Seconds 30

    if anyone can guide me I will be gratefuly

  5. With the latest release i was not able to get this working properly…

    #Install HP Universal Printing PCL 6
    pnputil.exe /add-driver “$psscriptroot\hpcu255u.inf” /install
    Add-PrinterDriver -Name “HP Universal Printing PCL 6”

    #Install Printerport with check if the port already exist
    $portName = “IP_192.168.111.84”
    $PortAddress = “192.168.111.84”
    $checkPortExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue
    if (-not $checkPortExists) {
    Add-PrinterPort -name $portName -PrinterHostAddress $PortAddress
    }

    #Install Printer
    Add-Printer -Name “HP LaserJet MFP M130fn” -DriverName “HP Universal Printing PCL 6” -PortName $portName

    SLEEP 360
    This worked for me!

  6. Was perfect! Thank you for this!

    I had to tweak the uninstall as it failed. This worked for me:

    remove-printer “(Printer Name)”
    SLEEP 360
    remove-printerPort -Name “IP_(IP of your Printer)”

  7. I followed your how to.
    But the printer is not installed.
    In the event log (powershell) I can see:
    The file “C:Windows\IMECache\….\install.ps1” cannot be loaded because the execution of scripts is disabled on this system. Please see “about_Execution_Policies” for more details.
    full quallified error-id = UnauthorizedAccess

    I definitely used
    powershell.exe -Executionpolicy -Bypass .\install.ps1
    as installation command.

    Do I have to change the script execution settings somewhere else as well? Where?

    Thanks in advance

    1. Hi, could you change the
      powershell.exe -Executionpolicy -Bypass .\install.ps1

      To

      Powershell.exe -ExecutionPolicy ByPass -File “.\full-script-path\script.ps1”

  8. All these Solution worked for me for Personal Devices.
    Self Service Devices do always get a Error (Error installing) on the same Package that work on Personal Devices.

  9. Hi Rudy. This is great, thank you! Very helpful insight in to how to get this done. I’m looking to deploy a printer shared from a print server with Intune. We will need to pre-stage the drivers no matter what since PNightmare I think because they’re universal for HP and Canon, and our users are non-admin. I can use use InTune to deploy the drivers and maybe run pnputil to stage, but can the print be deployed as well if it’s a shared queue on a print server? Direct to IP will bypass software we run for cost accounting that monitors the queues at the server.

  10. Everything works well except for the additional setting for A4 and non color printing didn’t work. When printer installed it is set to Auto not Black & white. May I know how you make it works?

    1. Hi,

      Thanx for your reply. What happens when you try to run it like -color:$false

      Or maybe changing the papersize with an object ?

      $PrintConfiguration = Get-PrintConfiguration -PrinterName $printername
      $PrintConfiguration.paperSize = A4
      Set-PrintConfiguration -InputObject $PrintConfiguration

  11. Just a detail but I think there is a little mistake in the uninstall.ps1:

    remove-printerPort -Name “192.168.40.251”
    SLEEP 360
    remove-printer “Toshiba Hoofdkantoor”

    The command “remove-printers” is not recognized on my device. I use remove-printer instead.

  12. Hi,

    Your script actually refers to “TOSHIBA Universal Printer” when it should refer to “TOSHIBA Universal Printer 2”, just thought I’d let you know.

    Can you clarify what this does please?

    $PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition

  13. Thanks for this but I am curious about the printer configs. I have tried to use Printui.exe /Sr /N “Printer Name” /a “.\config.dat” and rundll32 printui.dll,PrintUIEntry /Sr /n “Printer Name” /a “.\config.dat” u r but neither work when run as a system user with Intune. I can get both working when run as a user. Is there something else that I am missing?

  14. Thank you for this great writeup. A few problems I had to fix:
    $PortAddress should be “192.168.40.251” without “IP_”

    At least in Windows 11 printui needs the following changes
    Quotes around parameters must be straight ” not fancy ”
    Printui.exe should not be used, the manual says to use rundll32
    Parameter for printer name is /n not /N
    Not all parameters are allowed to be set inside script so must be limited at the end of the command for example “d g r” works.
    I added a parameter for $PrinterName
    Finalprintui command:
    rundll32 printui.dll,PrintUIEntry /Sr /n $PrinterName /a “.\config.dat” d g r

    In Uninstall.ps1 the commands are mixed up. remove-printer must be run first before SLEEP and remove-printerPort otherwise you get error “port in use”

    If you want to test the script locally run as admin c:\Windows\SysWOW64\cmd.exe then type cd c:\temp\folder and then powershell.exe -ExecutionPolicy Bypass c:\temp\folder\Install.ps1

  15. Hi,

    I’m curious what this line does:

    $PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition

    Thanks

Leave a Reply

Your email address will not be published.

24  +    =  32