Automating Printer Installation with Intune and PowerShell: Because Printers Deserve Love Too

Automating Printer Installation with Intune and PowerShell: Because Printers Deserve Love Too
Photo by Ana Rivarola / Unsplash

Ah, printers. The unsung heroes of the office—until they stop working, and suddenly they’re everyone’s problem. Thankfully, with this PowerShell script and a bit of Intune wizardry, you can conquer the chaos of printer deployments like a sysadmin superhero.

But here’s the kicker: Intune doesn’t come with a cost-free, native way to handle printer installs. Shocking, right? That’s where this ingenious workaround comes in, tailored for perfection. I’ve personally used this solution to install over 120 IP printers (UTAX, HP, Xerox, Ricoh—you name it). Spoiler alert: it never fails.


Why Automate Printer Installation? (Or: Why Keep Your Sanity?)

  • Time-Saver: Forget manual setup—you’ve got better things to do, like perfecting your coffee-to-code ratio.
  • Consistency: No more "Oh, it’s different on this device" nonsense.
  • Remote Ninja Skills: Deploy printers while sipping coffee miles away.
  • Scalability: Manage hundreds of printers without breaking a sweat.

Also, let’s face it—doing it manually makes you look like an intern, not the IT rockstar you are.


The Script: A Symphony of PCL and PowerShell

This trusty script ensures your printers are set up with PCL drivers—because life’s too short to deal with random compatibility issues. Here’s what it does:

  1. Creates a printer port.
  2. Stages and installs the PCL driver (because stability is key).
  3. Adds the printer with precise configuration.
  4. Logs every step, so you’re always in the know.

Full Script

<#
.Synopsis
Simple script to install a network printer from an INF file. The INF and required CAB files should be in the same directory as the script if creating a Win32app
#### Win32 app Commands ####
Install:
powershell.exe -executionpolicy bypass -file .\Install-Printer.ps1 -PortName "PortName" -PrinterIP "PrinterIP" -PrinterName "PrinterName" -DriverName "DriverName" -INFFile "INFFile.inf"
Uninstall:
powershell.exe -executionpolicy bypass -file .\Remove-Printer.ps1 -PrinterName "PrinterName"
Detection:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Print\Printers\PrinterName
Name = "PrinterName"
.Example
.\Install-Printer.ps1 -PortName "PortName" -PrinterIP "PrinterIP" -PrinterName "PrinterName" -DriverName "DriverName" -INFFile "INFFile.inf"
#>

[CmdletBinding()]
Param (
    [Parameter(Mandatory = $True)]
    [String]$PortName,
    [Parameter(Mandatory = $True)]
    [String]$PrinterIP,
    [Parameter(Mandatory = $True)]
    [String]$PrinterName,
    [Parameter(Mandatory = $True)]
    [String]$DriverName,
    [Parameter(Mandatory = $True)]
    [String]$INFFile
)

# Reset Error catching variable
$Throwbad = $Null

# Run script in 64bit PowerShell to enumerate correct path for pnputil
If ($ENV:PROCESSOR_ARCHITEW6432 -eq "AMD64") {
    Try {
        &"$ENV:WINDIR\SysNative\WindowsPowershell\v1.0\PowerShell.exe" -File $PSCOMMANDPATH -PortName $PortName -PrinterIP $PrinterIP -DriverName $DriverName -PrinterName $PrinterName -INFFile $INFFile
    }
    Catch {
        Write-Error "Failed to start $PSCOMMANDPATH"
        Write-Warning "$($_.Exception.Message)"
        $Throwbad = $True
    }
}

function Write-LogEntry {
    param (
        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Value,
        [parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$FileName = "$($PrinterName).log",
        [switch]$Stamp
    )

    $LogFile = Join-Path -Path $env:SystemRoot -ChildPath $("Temp\$FileName")
    $Time = -join @((Get-Date -Format "HH:mm:ss.fff"), " ", (Get-WmiObject -Class Win32_TimeZone | Select-Object -ExpandProperty Bias))
    $Date = (Get-Date -Format "MM-dd-yyyy")

    If ($Stamp) {
        $LogText = "<$($Value)> <time=\"$($Time)\" date=\"$($Date)\">"
    }
    else {
        $LogText = "$($Value)"   
    }
    
    Try {
        Out-File -InputObject $LogText -Append -NoClobber -Encoding Default -FilePath $LogFile -ErrorAction Stop
    }
    Catch [System.Exception] {
        Write-Warning -Message "Unable to add log entry to $LogFile.log file. Error message at line $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.Message)"
    }
}

Write-LogEntry -Value "##################################"
Write-LogEntry -Stamp -Value "Installation started"
Write-LogEntry -Value "##################################"
Write-LogEntry -Value "Install Printer using the following values..."
Write-LogEntry -Value "Port Name: $PortName"
Write-LogEntry -Value "Printer IP: $PrinterIP"
Write-LogEntry -Value "Printer Name: $PrinterName"
Write-LogEntry -Value "Driver Name: $DriverName"
Write-LogEntry -Value "INF File: $INFFile"

$INFARGS = @(
    "/add-driver"
    "$INFFile"
)

If (-not $ThrowBad) {

    Try {

        # Stage driver to driver store
        Write-LogEntry -Stamp -Value "Staging Driver to Windows Driver Store using INF \"$($INFFile)\""
        Write-LogEntry -Stamp -Value "Running command: Start-Process pnputil.exe -ArgumentList $($INFARGS) -wait -passthru"
        Start-Process pnputil.exe -ArgumentList $INFARGS -wait -passthru

    }
    Catch {
        Write-Warning "Error staging driver to Driver Store"
        Write-Warning "$($_.Exception.Message)"
        Write-LogEntry -Stamp -Value "Error staging driver to Driver Store"
        Write-LogEntry -Stamp -Value "$($_.Exception)"
        $ThrowBad = $True
    }
}

If (-not $ThrowBad) {
    Try {
    
        # Install driver
        $DriverExist = Get-PrinterDriver -Name $DriverName -ErrorAction SilentlyContinue
        if (-not $DriverExist) {
            Write-LogEntry -Stamp -Value "Adding Printer Driver \"$($DriverName)\""
            Add-PrinterDriver -Name $DriverName -Confirm:$false
        }
        else {
            Write-LogEntry -Stamp -Value "Print Driver \"$($DriverName)\" already exists. Skipping driver installation."
        }
    }
    Catch {
        Write-Warning "Error installing Printer Driver"
        Write-Warning "$($_.Exception.Message)"
        Write-LogEntry -Stamp -Value "Error installing Printer Driver"
        Write-LogEntry -Stamp -Value "$($_.Exception)"
        $ThrowBad = $True
    }
}

If (-not $ThrowBad) {
    Try {

        # Create Printer Port
        $PortExist = Get-Printerport -Name $PortName -ErrorAction SilentlyContinue
        if (-not $PortExist) {
            Write-LogEntry -Stamp -Value "Adding Port \"$($PortName)\""
            Add-PrinterPort -name $PortName -PrinterHostAddress $PrinterIP -Confirm:$false
        }
        else {
            Write-LogEntry -Stamp -Value "Port \"$($PortName)\" already exists. Skipping Printer Port installation."
        }
    }
    Catch {
        Write-Warning "Error creating Printer Port"
        Write-Warning "$($_.Exception.Message)"
        Write-LogEntry -Stamp -Value "Error creating Printer Port"
        Write-LogEntry -Stamp -Value "$($_.Exception)"
        $ThrowBad = $True
    }
}

If (-not $ThrowBad) {
    Try {

        # Add Printer
        $PrinterExist = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue
        if (-not $PrinterExist) {
            Write-LogEntry -Stamp -Value "Adding Printer \"$($PrinterName)\""
            Add-Printer -Name $PrinterName -DriverName $DriverName -PortName $PortName -Confirm:$false
        }
        else {
            Write-LogEntry -Stamp -Value "Printer \"$($PrinterName)\" already exists. Removing old printer..."
            Remove-Printer -Name $PrinterName -Confirm:$false
            Write-LogEntry -Stamp -Value "Adding Printer \"$($PrinterName)\""
            Add-Printer -Name $PrinterName -DriverName $DriverName -PortName $PortName -Confirm:$false
        }

        $PrinterExist2 = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue
        if ($PrinterExist2) {
            Write-LogEntry -Stamp -Value "Printer \"$($PrinterName)\" added successfully"
        }
        else {
            Write-Warning "Error creating Printer"
            Write-LogEntry -Stamp -Value "Printer \"$($PrinterName)\" error creating printer"
            $ThrowBad = $True
        }
    }
    Catch {
        Write-Warning "Error creating Printer"
        Write-Warning "$($_.Exception.Message)"
        Write-LogEntry -Stamp -Value "Error creating Printer"
        Write-LogEntry -Stamp -Value "$($_.Exception)"
        $ThrowBad = $True
    }
}

If ($ThrowBad) {
    Write-Error "An error was thrown during installation. Installation failed. Refer to the log file in %temp% for details"
    Write-LogEntry -Stamp -Value "Installation Failed"
}

Bonus TIP: You can just run the script from ./ location, and it will install on your test VM/Device (will for sure save you some time knowing if its working or not)

Why PCL Drivers?

PCL (Printer Command Language) drivers are the gold standard for reliable printing across a wide range of devices. They’re lightweight, stable, and universally loved (well, as much as drivers can be). Whether it’s UTAX, HP, Xerox, or Ricoh, PCL drivers ensure that everything just works—no drama.


Deploying the Script via Intune: The Fun Part

Step 1: Wrap It Up as a Win32 App

  1. Download the Microsoft Win32 Content Prep Tool.
  2. Place your script and driver files in one happy folder.

Run this magic command:

IntuneWinAppUtil.exe -c "C:\PathToFiles" -s "Install-Printer.ps1" -o "C:\OutputPath"

Step 2: Add the Win32 App to Intune

  1. Head to the Microsoft Endpoint Manager Admin Center.
  2. Navigate to Apps > Windows > Add > Windows app (Win32).
  3. Upload your shiny new .intunewin package.
  4. Set the Detection Rule:
    • Rule type: Registry
    • Path: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Print\Printers\[PrinterName]
    • Key: Name
    • Value: [PrinterName]

Configure the Uninstall Command (just in case):

powershell.exe -executionpolicy bypass -file .\Remove-Printer.ps1 -PrinterName "PrinterName"

Configure the Install Command:

powershell.exe -executionpolicy bypass -file .\Install-Printer.ps1 -PortName "PortName" -PrinterIP "PrinterIP" -PrinterName "PrinterName" -DriverName "DriverName" -INFFile "INFFile.inf"

Step 3: Assign the App to Devices

  • Assign the app to relevant groups.
  • Watch your printers deploy like magic.

Troubleshooting: Or, "What’s That Error?"

  • Logs Are Your Friends: The script logs everything in %temp%. When in doubt, check the logs.
  • Driver Drama: Ensure the PCL driver matches the OS architecture.
  • Network Snags: Test printer IP connectivity before blaming the script.
  • Detection Rule Check: If detection fails, double-check the registry path.

Final Thoughts: Printer Bliss

Deploying printers via Intune with PowerShell isn’t just a workaround; it’s a masterpiece. Sure, it’s a pity there’s no native, cost-free Intune solution, but this script makes up for it in spades. With over 120 installs across brands like UTAX, HP, Xerox, and Ricoh, this method has proven itself as rock-solid.

So go ahead, automate those installs, and let your printers shine. Because when printers work flawlessly, so do you.

Cheers from the 3AM Deploy Team—where we script, sip coffee, and slay IT dragons.

Read more