Driver didn't get add to Windows Driver Store

Hello together,
in my company up to now we had for every printer an own script. There they called cscript and start an windows vbs-Script ({%windir%}\System32\cscript.exe “c:\Windows\System32\Printing_Admin_Scripts\de-DE\prndrvr.vbs” -a -m “{printer_Inf_Name}” -i “{printer_driver_inf_path}”). So when someone needed a printer they told the IT department the ID of the printer and they assign the script for that printer to the computer in our software distribution application.
So I thought why not collect all the printer information in an xml file and write a powershell script which asks the user for the id of the printer, looks up the information in the xml and install the printer.

So far so good. But now I encountered a quite interesting error. Whenever the driver is already in the windows driver store the script works fine, but when it isn’t I got an error and the printer doesn’t get installed. I tried to put it there with “pnputil.exe”, but it seems not to work. Is there any other way to add the printer driver to the driver store of the computer?

            ### INST PRINTER-DRIVER ###
            $instDrv = Get-PrinterDriver | Where-Object {$_.Name -eq $drv_name}
            If (!$instDrv) { 
                Write-Output "Das Treiberpaket befindet sich bisher nicht im Windows Treiberspeicher."
                Try {
                    Write-Output "Es wird versucht das Treiberpaket in den Windows Treiberspeicher zu laden. `nBitte warten..."
                    pnputil.exe /add-driver "$inf_path" /install | Out-Null #Add driver it to driverstore
                    Start-Sleep -Seconds 10
                    Write-Host "Das Treiberpaket wurde erfolgreich in den Windows Treiberspeicher geladen. `n Es wird versucht den Druckertreiber zu installieren. `nBitte warten..."
                    Add-PrinterDriver -Name $drv_name #Install printer driver on the host
                    Start-Sleep -Seconds 10
                }
                Catch {
                    $MsgBody = "Beim Versuch den Drucker zu installieren ist ein Fehler aufgetreten. `nDie Installation wird abgebrochen. `nBitte informieren Sie die IT (it-neugart@support.gc-gruppe.de)."
                    $MsgHeader = "PrintAssist - Skript abgebrochen"
                    Write-Output "$MsgBody"
                    $result = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)
                    Exit
                }
            }
            Else {
                Write-Output "Das Treiberpaket befindet sich bereits im Windows Treiberspeicher."
            }

If I’m tracking with your request, you’re asking to allow users (non-admins) to install printer drivers? If yes, you can’t, by default, anymore. This was patched due to the vulnerability called PrintNightmare. Technically you can enable a setting to allow non-admins to install drivers but you open a pretty big vulnerability.

A better approach might be to deploy the drivers with your system images or as a software push with whatever tooling you use to install software and patch

Hello neemobeer,
thank you for your answer.
I didn’t clarify it, but the deployment of the script is controlled with a software deployment application. There I configure to run the script with admin privileges.
So, it’s not missing administrator privileges. :frowning:

Edit: I just got an idea… at the moment pnputil.exe tries to grab the *.inf from a network share. Maybe that’s a problem. I’ll try if it works, when I use a local path. If you I’ll have to update the script, but that shouldn’t be a big deal.

It sounds like the double hop issue. Connection 1 is from the software deployment app to the destination system. The second hop would be from the destination system to the network share. You can set up constrained delegation to allow a system or a user to complete the second hop.

Hello krzydoug,
I might check that out. For now it seems, that my apprehension was correct. I changed the script, so that it first download the driver and then use the local path for pnputil.exe. It seems, that it worked fine.

So, here the complete script, if someone have the same problem.

Write-Output "the script for the installation of a printer starts..."
$MsgBody = "Please enter the inventory number of the printer, which should be added to your system.", "Add printer"
$MsgHeader = "PrintAssist - Add Printer"
$MsgOK = "Continue"
$MsgCancel = "Cancel"

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$frm_InputBox = New-Object System.Windows.Forms.Form
$frm_InputBox.Text = $MsgHeader
$frm_InputBox.Size = New-Object System.Drawing.Size(440,200) #Size of the input box
$frm_InputBox.StartPosition = 'CenterScreen'

$btn_OK = New-Object System.Windows.Forms.Button
$btn_OK.Location = New-Object System.Drawing.Point(60,110) #Position of the OK button
$btn_OK.Size = New-Object System.Drawing.Size(100,35) #Size of the OK button
$btn_OK.Text = $MsgOK
$btn_OK.DialogResult = [System.Windows.Forms.DialogResult]::OK
$frm_InputBox.AcceptButton = $btn_OK
$frm_InputBox.Controls.Add($btn_OK)

$btn_Cancel = New-Object System.Windows.Forms.Button
$btn_Cancel.Location = New-Object System.Drawing.Point(280,110) #Position of the Cancel button
$btn_Cancel.Size = New-Object System.Drawing.Size(100,35) #Size of the Cancel button
$btn_Cancel.Text = $MsgCancel
$btn_Cancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$frm_InputBox.CancelButton = $btn_Cancel
$frm_InputBox.Controls.Add($btn_Cancel)

$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(60,20) #Position of the text field
$label.Size = New-Object System.Drawing.Size(320,50) #Size of the text field
$label.Text = $MsgBody
$frm_InputBox.Controls.Add($label)

$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(60,70) #Position of the input mask
$textBox.Size = New-Object System.Drawing.Size(320,35) #Size of the input mask
$frm_InputBox.Controls.Add($textBox)

$frm_InputBox.Topmost = $true
$frm_InputBox.Add_Shown({$textBox.Select()})

$result = $frm_InputBox.ShowDialog()
$prt_inv = $textBox.Text

if ($result -eq [System.Windows.Forms.DialogResult]::OK){
    $prt_inv = $prt_inv.Trim()
    Write-Output "Your entry ""$prt_inv"" gets checked..."
    $id_valid = "false"
    
    if($prt_inv.length -eq 5){
        $id_letter = $prt_inv[0]
        $id_letter = "$id_letter".ToUpper()
        $id_number = $prt_inv.Substring(1)

        if($id_number -match "^[\d]+$"){
            if($id_letter -match "^[A-Z]+$"){
                $case = "4" #Inventar number correct.
            }else{
                $case = "3" #The entered inventory number is not correct. The first character has to be a letter.
            }
        }
        else{
            $case = "2" #The entered inventory number is not correct. The last four characters have to be digits.
        }
    }
    else{
        if($prt_inv -eq ""){
            $case = "0" #No entry in the input mask.
        }
        else{
        $case = "1" #The entered inventory number has not the correct length. It have to be five charcters, a letter followed by four digits.
        }
    }
}

Else {
    $MsgBody = "The script has been canceled and will close."
    $MsgHeader = "PrintAssist - Script canceled"
    Write-Output "$MsgBody"
    $result = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)
    Exit
}

switch ($case){
    0{
        $MsgBody = "No inventory number entered. `nThe script will close."
        $MsgHeader = "PrintAssist - Script canceled"
    }
    1{
        $MsgBody = "Invalid entry. `nThe entered inventory number has not the correct length. It have to be five charcters, a letter followed by four digits. `nThe script will close."
        $MsgHeader = "PrintAssist - Script canceled"
    }
    2{
        $MsgBody = "Invalid entry. `nThe entered inventory number is not correct. The last four characters have to be digits. `nThe script will close."
        $MsgHeader = "PrintAssist - Script canceled"
    }
    3{
        $MsgBody = "Invalid entry. `nThe entered inventory number is not correct. The first character has to be a letter. `nThe script will close."
        $MsgHeader = "PrintAssist - Script canceled"
    }
    4{
        Write-Output "The format of your entry is valid. `nIt will be checked if the printer is not already installed on your computer..."
        $printer = Get-Printer | Where-Object {$_.Name -like "$prt_inv*"} #Checks if the printer with the entered inventory number is already installed.
        If ($printer -ne $null) {
            $MsgBody = "The printer with the inventory number ""$prt_inv"" is already installed on your computer. `nThe scipt will close."
            $MsgHeader = "PrintAssist - Script canceled"
            Break
        }
        Else{
            Write-Output "The printer with the inventory number ""$prt_inv"" is not installed on your computer yet. `nThe list with available printers will checked..."
            $xml_file = "\\YOURSERVER\YourPath\PrinterList.xml"
            $xml_path_id = "/printers/ids/$prt_inv"
            $xml_prt = Select-Xml -Path $xml_file -XPath $xml_path_id
            If ($xml_prt.Node.InnerText -eq $null) {
                $MsgBody = "A printer with the inventory number ""$prt_inv"" can't found in the list of available printers. `nThe script will close."
                $MsgHeader = "PrintAssist - Script canceled"
                Write-Output "$MsgBody"
                $result = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)
                Exit
            }
            Write-Output "A printer with the inventory number ""$prt_inv"" had been found in the printer list. `nThe printer information will get detected..."
            $prt_brand = $xml_prt.Node.brand
            Write-Output " - Brand: $prt_brand"
            $prt_model = $xml_prt.Node.model
            Write-Output " - Model: $prt_model"
            $prt_ip = $xml_prt.Node.ip
            Write-Output " - IP address: $prt_ip"
            $xml_path_drv = "/printers/drivers/$prt_brand/$prt_model"
            $xml_drv = Select-Xml -Path $xml_file -XPath $xml_path_drv
            $drv_name = $xml_drv.Node.DRV_name
            Write-Output " - Driver name: $drv_name"
            $drv_path_net = $xml_drv.Node.DRV_path
            $drv_path_loc = "C:\Driver\$prt_brand\$prt_model"
            $inf_path = $xml_drv.Node.INF_path
            $inf_path_net = "$drv_path\$inf_path"
            $inf_path_loc = "$drv_path_loc\$inf_path"
            Write-Output " - Driver file detected: $inf_path"
            Write-Output "The printer information have been detected."
            New-Item -ItemType Directory -Path "$drv_path_loc" -Force | Out-Null
            Write-Output "An folder for the printer driver has been created. `nThe driver will copied to your system..."
            Copy-Item -Path "$drv_path_net" -Destination "$drv_path_loc" -Recurse -Force | Out-Null
            Write-Output "The driver has been copied. 'nThe installation of the driver started..."

            ### INST PRINTER-DRIVER ###
            $instDrv = Get-PrinterDriver | Where-Object {$_.Name -eq $drv_name}
            If (!$instDrv) { 
                Write-Output "The driver is not in the driver store yet."
                Try {
                    Write-Output "It's being tried to load the driver into the windows driver store. `nPlease wait..."
                    pnputil.exe /add-driver "$inf_path_loc" /install | Out-Null #Add driver to the windows driverstore
                    Start-Sleep -Seconds 10
                    Write-Host "The driver has been successful installed into the windows driver store. `n It will be tried to installed the driver. `nPlease wait..."
                    Add-PrinterDriver -Name $drv_name #Install printer driver on the host
                    Start-Sleep -Seconds 10
                }
                Catch {
                    $MsgBody = "The attempt to installed the driver has failed. `nThe installation will canceled."
                    $MsgHeader = "PrintAssist - Script canceled"
                    Write-Output "$MsgBody"
                    $result = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)
                    Exit
                }
            }
            Else {
                Write-Output "The driver is already in the windows driver store."
            }

            ### INST PRINTER-PORT ###
            Write-Output "It will be checked if the required printer port already exists."
            $instPrt = Get-PrinterPort | Where-Object {$_.Name -eq "IP_$prt_ip"}
            If (!$instPrt) {
                Write-Output "The required printer port couldn't be found. `nIt will be tried to install the new printer port..."
                Try {
                    Add-PrinterPort -Name "IP_$prt_ip" -PrinterHostAddress $prt_ip #Adds an local printport to the new printer
                    Write-Output "The new printer port has succesfully installed"
                }
                Catch {
                    $MsgBody = "The attempt to installed the printer port failed. `nThe installation will canceled."
                    $MsgHeader = "PrintAssist - Script canceled"
                    Write-Output "$MsgBody"
                    $result = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)
                    Exit
                }   
            }
            Else {
                Write-Output "The required printer port is already installed."
            }

            ### ADD PRINTER ###
            Write-Host "It will tried to install the printer..."
            Try {
                Add-Printer -DriverName $drv_name -Name "$prt_inv - $prt_brand $prt_model" -PortName "IP_$prt_ip" #Create printer with the driver and the port
                $MsgBody = "The printer with the inventory number ""$prt_inv"" has successfully added to your system.`nPrinter name: ""$prt_inv - $prt_brand $prt_model"" `nThe script will close."
                $MsgHeader = "PrintAssist - Script completed"
                Write-Host "$MsgBody"
                $prt_inv = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)
                Exit
            }
            Catch{
                $MsgBody = "The attempt to installed the printer failed. `nThe installation will canceled."
                $MsgHeader = "PrintAssist - Script canceled"
                Write-Output "$MsgBody"
                $result = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)
                Exit
            }
        }
    }
}
Write-Output "$MsgBody"
$result = [System.Windows.Forms.MessageBox]::Show($MsgBody,$MsgHeader,0)

And for those who are interested, here is the structure of the .xml file with some printer information as an example:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<printers>
	<ids>	
		<A1111>
			<brand>Lexmark</brand>
			<model>T644n</model>
			<ip>10.xxx.xxx.xxx</ip>
		</A1111>
		<B2000>
			<brand>Brother</brand>
			<model>HL-5240</model>
			<ip>10.xxx.xxx.xxx</ip>
		</B2000>
		<C3000>
			<brand>Brother</brand>
			<model>MFC-8460N</model>
			<ip>10.xxx.xxx.xxx</ip>
		</C3000>
		<D3400>
			<brand>Lexmark</brand>
			<model>T654n</model>
			<ip>10.xxx.xxx.xxx</ip>
		</D3400>
		<E3670>
			<brand>Brother</brand>
			<model>MFC-8460N</model>
			<ip>10.xxx.xxx.xxx</ip>
		</E3670>		
		<E3800>
			<brand>Lexmark</brand>
			<model>T654n</model>
			<ip>10.xxx.xxx.xxx</ip>
		</E3800>
		<F8000>
			<brand>Toshiba</brand>
			<model>eSTUDIO2515AC</model>
			<ip>10.xxx.xxx.xxx</ip>
		</F8000>
	</ids>
	<drivers>
		<Brother>
			<HL-5240>
				<DRV_name>Brother Universal Printer (BR-Script3)</DRV_name>
				<DRV_path>\\YOURSERVER\YOURPATH\Brother\Brother Universal Printer Driver\Files\01.18.00</DRV_path>
				<INF_path>BRUPSB0A.INF</INF_path>
			</HL-5240>
			<MFC-8460N>
				<DRV_name>Brother Universal Printer (BR-Script3)</DRV_name>
				<DRV_path>\\YOURSERVER\YOURPATH\Brother\Brother Universal Printer Driver\Files\01.18.00</DRV_path>
				<INF_path>BRUPSB0A.INF</INF_path>
			</MFC-8460N>
		</Brother>
		<Canon>
		</Canon>
		<Lexmark>
			<T644n>
				<DRV_name>Lexmark Universal v2 XL</DRV_name>
				<DRV_path>\\YOURSERVER\YOURPATH\Lexmark\Lexmark Universal Printer Driver\Files\2.14.1</DRV_path>
				<INF_path>LMUD1p40.INF</INF_path>
			</T644n>
			<T654n>
				<DRV_name>Lexmark Universal v2 XL</DRV_name>
				<DRV_path>\\YOURSERVER\YOURPATH\Lexmark\Lexmark Universal Printer Driver\Files\2.14.1</DRV_path>
				<INF_path>LMUD1p40.INF</INF_path>
			</T654n>
			<XC2235>
				<DRV_name>Lexmark Universal v2 XL</DRV_name>
				<DRV_path>\\YOURSERVER\YOURPATH\Lexmark\Lexmark Universal Printer Driver\Files\2.14.1</DRV_path>
				<INF_path>LMUD1p40.INF</INF_path>
			</XC2235>
		</Lexmark>
		<Oki>
			<ES5431dn>
				<DRV_name>ES8453 MFP(PS)</DRV_name>
				<DRV_path>\\YOURSERVER\YOURPATH\OKI\OKI Universal Treiber\Files\1.0.2</DRV_path>
				<INF_path>OKW3S058.INF</INF_path>
			</ES5431dn>
		</Oki>
		<Samsung>
		</Samsung>
		<Toshiba>
			<eSTUDIO2515AC>
				<DRV_name>TOSHIBA Universal Printer 2</DRV_name>
				<DRV_path>\\YOURSERVER\YOURPATH\Toshiba\Toshiba eStudio Treiber\Files\7.222.5412</DRV_path>
				<INF_path>eSf6u.inf</INF_path>
			</eSTUDIO2515AC>
		</Toshiba>
	</drivers>
</printers>

Thank your for the help. :slight_smile: