Automating Network Printer Deployment with Microsoft Intune, PowerShell and PrintBRM

Deploy Local IP Printers with Microsoft Intune and PowerShell

Automating Network Printer Deployment with Microsoft Intune, PowerShell and PrintBRM

A practical approach to packaging, deploying and detecting network printers on Windows 11 devices

Deploying printers to Windows 11 devices managed by Microsoft Intune can be challenging, especially when the devices are Microsoft Entra joined and not joined to a traditional Active Directory domain.

This is a common scenario for Windows kiosk devices, where the device is managed entirely through Intune but still needs access to an organization's printers.

Two Common Approaches

1. Microsoft Universal Print

Universal Print provides a cloud-based printing solution where printers can be managed and assigned through Microsoft 365 and Intune. This can be a good option for organizations that want to move their printing infrastructure to the cloud.

2. Intune + PowerShell

If you already have network printers that can be reached directly over the network, you can deploy them directly to the Windows client using Intune and PowerShell. In this scenario, the client connects directly to the printer rather than through a traditional Windows Print Server:

Windows 11 Kiosk │ │ Intune ▼ PowerShell / Win32 App │ │ TCP/IP ▼ Network Printer

This guide focuses on this second approach. It is especially useful for environments with a limited number of known network printers where you want to deploy the same printer configuration consistently to multiple Intune-managed Windows devices.

Note: This is different from a printer that is shared from a traditional Windows Print Server, for example \\PRINTSERVER01\Reception. In that scenario, the client connects to the shared printer queue on the server, and additional considerations such as network connectivity, authentication and permissions apply.

For the solution in this guide, we will use Windows Print Management, PrintBRM, PowerShell and Microsoft Intune Win32 apps.

The workflow:

Configure printer → Export printer → Package with PowerShell → Deploy with Intune → Verify printer

The goal is to create a deployment that is repeatable, logged and easy to troubleshoot.

Important: Always test the printer export and deployment on a clean Windows 11 device before assigning the application broadly.

What We Are Going to Build

The final Intune application will:

  • Import the printer configuration
  • Make sure the Print Spooler service is running
  • Install the expected printer queues
  • Verify that the printers were installed successfully
  • Write a log for troubleshooting
  • Allow Intune to detect the installation based on the actual printer queues

The deployment will run in System context, making it suitable for device-based deployments such as Windows kiosk devices.

🖨️ Example Environment: Four Departments With Unique Network Printers

The following sample printers are used only to demonstrate how Intune-based IP printer deployment works. Replace the models, names, drivers, and IP addresses with the values from your own environment.

LocationPrinter ModelDisplay NameDriverIP Address
City LibraryCanon i-SENSYS MF655CdwLibrary-Color-01Canon UFR II Generic Driver10.30.5.41
Contact CenterBrother HL-L8360CDWCC-Mono-Fast-01Brother Universal Driver10.30.12.88
IT HelpdeskEpson WorkForce Pro WF-C5790IT-Print-Secure-01Epson Universal Print Driver10.30.30.22
Social ServicesRicoh IM 350FSocial-Dept-Print-01Ricoh PCL6 Universal Driver10.30.45.17

Why Use PrintBRM?

Instead of manually creating every printer, port and driver configuration with PowerShell, we can configure the printers once on a reference Windows 11 device and export the configuration using Print Management.

The exported .printerExport file can then be imported on the target devices using PrintBrm.exe. This gives us a simple deployment workflow:

Reference Windows 11 device │ ▼ Configure printers │ ▼ Print Management │ ▼ .printerExport │ ▼ Intune Win32 application │ ▼ PowerShell + PrintBRM │ ▼ Intune-managed Windows 11 device │ ▼ Network printer

This approach is particularly useful for kiosk devices where the printer is known in advance and the device should receive its printer configuration automatically.


🖨️ Exporting Your Local Printers (Windows 11)

To prepare the printer package, start by exporting the printers from a clean Windows 11 reference device — this ensures consistent drivers and configuration across all Intune-managed clients.

  1. Open the Start Menu and search for Print Management.
  2. In the left navigation pane, expand: Print Servers → YourDeviceName → Printers. Here you'll find all printers currently installed on the machine.
  3. Right-click the device name (for example, W11-Reference) and select Export printers to a file…
  4. The export wizard will guide you through saving a .printerExport file. Only actual installed printers are included — Windows' built-in virtual printers (like Microsoft Print to PDF) are automatically excluded.

This export file contains: printer objects, ports, drivers, and configuration settings. It will later be imported on Intune-managed endpoints using your deployment script.

Export-Printers.ps1
#requires -Version 5.1

$ErrorActionPreference = 'Stop'

Write-Host ""
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host "          Printer BRM Export Tool" -ForegroundColor Cyan
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host ""

# ------------------------------------------------------------
# 1. Locate PrintBRM.exe
# ------------------------------------------------------------

$PrintBrm = Join-Path $env:windir "System32\spool\tools\PrintBrm.exe"

Write-Host "Checking for PrintBRM.exe..." -ForegroundColor Yellow

if (-not (Test-Path -LiteralPath $PrintBrm)) {
    Write-Host ""
    Write-Host "ERROR: PrintBrm.exe was not found." -ForegroundColor Red
    Write-Host ""
    Write-Host "Expected location:" -ForegroundColor Yellow
    Write-Host $PrintBrm
    Write-Host ""
    exit 1
}

Write-Host "PrintBRM found:" -ForegroundColor Green
Write-Host $PrintBrm
Write-Host ""

# ------------------------------------------------------------
# 2. Check Print Spooler
# ------------------------------------------------------------

Write-Host "Checking Print Spooler service..." -ForegroundColor Yellow

try {
    $Spooler = Get-Service -Name Spooler -ErrorAction Stop
}
catch {
    Write-Host ""
    Write-Host "ERROR: Print Spooler service was not found." -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
    exit 1
}

if ($Spooler.Status -ne 'Running') {
    Write-Host "Print Spooler is not running." -ForegroundColor Yellow
    Write-Host "Starting Print Spooler..." -ForegroundColor Yellow

    try {
        Start-Service -Name Spooler -ErrorAction Stop
        $Spooler.WaitForStatus(
            [System.ServiceProcess.ServiceControllerStatus]::Running,
            [TimeSpan]::FromSeconds(15)
        )
    }
    catch {
        Write-Host ""
        Write-Host "ERROR: Unable to start Print Spooler." -ForegroundColor Red
        Write-Host $_.Exception.Message -ForegroundColor Red
        exit 1
    }
}

Write-Host "Print Spooler is running." -ForegroundColor Green
Write-Host ""

# ------------------------------------------------------------
# 3. Get installed printers
# ------------------------------------------------------------

Write-Host "Scanning installed printers..." -ForegroundColor Yellow

try {
    $printers = @(Get-Printer -ErrorAction Stop)
}
catch {
    Write-Host ""
    Write-Host "ERROR: Unable to retrieve printers." -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
    exit 1
}

if ($printers.Count -eq 0) {
    Write-Host ""
    Write-Host "No printers found on this device." -ForegroundColor Red
    exit 1
}

# ------------------------------------------------------------
# 4. Display printers
# ------------------------------------------------------------

Write-Host ""
Write-Host "Detected printers:" -ForegroundColor Green
Write-Host ""

for ($i = 0; $i -lt $printers.Count; $i++) {
    $printer = $printers[$i]

    Write-Host ("[{0}] {1}" -f ($i + 1), $printer.Name) -ForegroundColor White
    Write-Host ("     Driver : {0}" -f $printer.DriverName) -ForegroundColor DarkGray
    Write-Host ("     Port   : {0}" -f $printer.PortName) -ForegroundColor DarkGray

    try {
        $port = Get-PrinterPort -Name $printer.PortName -ErrorAction SilentlyContinue
        if ($port -and $port.PrinterHostAddress) {
            Write-Host ("     IP     : {0}" -f $port.PrinterHostAddress) -ForegroundColor DarkGray
        }
    }
    catch {
        # Ignore port lookup errors
    }

    Write-Host ""
}

# ------------------------------------------------------------
# 5. Select printers
# ------------------------------------------------------------

$selection = Read-Host "Enter printer numbers you want to export (example: 1,2)"

if ([string]::IsNullOrWhiteSpace($selection)) {
    Write-Host ""
    Write-Host "No selection was made." -ForegroundColor Red
    exit 1
}

$numbers = $selection -split ',' |
    ForEach-Object { $_.Trim() } |
    Where-Object { $_ -match '^\d+$' } |
    ForEach-Object { [int]$_ }

$selected = foreach ($number in $numbers) {
    if ($number -ge 1 -and $number -le $printers.Count) {
        $printers[$number - 1]
    }
    else {
        Write-Host "Ignoring invalid printer number: $number" -ForegroundColor DarkYellow
    }
}

$selected = @($selected)

if ($selected.Count -eq 0) {
    Write-Host ""
    Write-Host "No valid printers selected." -ForegroundColor Red
    exit 1
}

# ------------------------------------------------------------
# 6. Display selection
# ------------------------------------------------------------

Write-Host ""
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host "             Selected printers" -ForegroundColor Green
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host ""

foreach ($printer in $selected) {
    Write-Host " - $($printer.Name)" -ForegroundColor White
}

Write-Host ""

# ------------------------------------------------------------
# 7. Prepare export folder
# ------------------------------------------------------------

$ExportFolder = "C:\PrinterInventory"
$ExportFile = Join-Path $ExportFolder "PrinterExport.printerExport"

if (-not (Test-Path -LiteralPath $ExportFolder)) {
    Write-Host "Creating export folder..." -ForegroundColor Yellow
    New-Item -Path $ExportFolder -ItemType Directory -Force | Out-Null
}

# ------------------------------------------------------------
# 8. Remove previous export
# ------------------------------------------------------------

if (Test-Path -LiteralPath $ExportFile) {
    Write-Host "Removing previous export..." -ForegroundColor Yellow
    Remove-Item -LiteralPath $ExportFile -Force
}

# ------------------------------------------------------------
# 9. Run PrintBRM
# ------------------------------------------------------------

Write-Host ""
Write-Host "Starting PrintBRM export..." -ForegroundColor Yellow
Write-Host ""
Write-Host "Export file:" -ForegroundColor Cyan
Write-Host $ExportFile -ForegroundColor White
Write-Host ""
Write-Host "Running command:" -ForegroundColor DarkGray
Write-Host "$PrintBrm -b -f `"$ExportFile`"" -ForegroundColor DarkGray
Write-Host ""

# IMPORTANT:
# Call PrintBRM directly instead of Start-Process.
# This allows us to see PrintBRM output and avoids
# argument/quoting problems.

try {
    & $PrintBrm -b -f $ExportFile
    $PrintBrmExitCode = $LASTEXITCODE
}
catch {
    Write-Host ""
    Write-Host "ERROR: PrintBRM failed to start." -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
    exit 1
}

# ------------------------------------------------------------
# 10. Check exit code
# ------------------------------------------------------------

Write-Host ""
Write-Host "PrintBRM exit code: $PrintBrmExitCode" -ForegroundColor Cyan

if ($PrintBrmExitCode -ne 0) {
    Write-Host ""
    Write-Host "ERROR: PrintBRM export failed." -ForegroundColor Red
    Write-Host ""
    Write-Host "Exit code: $PrintBrmExitCode" -ForegroundColor Red
    exit $PrintBrmExitCode
}

# ------------------------------------------------------------
# 11. Search for the export file
# ------------------------------------------------------------

Write-Host ""
Write-Host "Checking for exported file..." -ForegroundColor Yellow

if (Test-Path -LiteralPath $ExportFile) {
    $File = Get-Item -LiteralPath $ExportFile
}
else {
    # PrintBRM may have created the file in the current
    # working directory. Search for it as a fallback.
    $FileName = Split-Path -Path $ExportFile -Leaf

    $PossibleFiles = @(
        Get-ChildItem -Path "C:\PrinterInventory" -Filter $FileName -File -ErrorAction SilentlyContinue
        Get-ChildItem -Path (Get-Location).Path -Filter $FileName -File -ErrorAction SilentlyContinue
    ) | Sort-Object LastWriteTime -Descending

    if ($PossibleFiles.Count -gt 0) {
        $File = $PossibleFiles[0]
        Write-Host ""
        Write-Host "PrintBRM created the file here instead:" -ForegroundColor Yellow
        Write-Host $File.FullName -ForegroundColor White
    }
    else {
        Write-Host ""
        Write-Host "ERROR: PrintBRM returned exit code 0," -ForegroundColor Red
        Write-Host "but no .printerExport file could be found." -ForegroundColor Red
        Write-Host ""
        Write-Host "This is the PrintBRM output location that was requested:" -ForegroundColor Yellow
        Write-Host $ExportFile
        Write-Host ""
        Write-Host "Please run this command manually to verify PrintBRM:" -ForegroundColor Yellow
        Write-Host ""
        Write-Host "`"$PrintBrm`" -b -f `"$ExportFile`"" -ForegroundColor White
        Write-Host ""
        exit 1
    }
}

# ------------------------------------------------------------
# 12. Verify file size
# ------------------------------------------------------------

if ($File.Length -eq 0) {
    Write-Host ""
    Write-Host "ERROR: Export file exists but is empty." -ForegroundColor Red
    exit 1
}

# ------------------------------------------------------------
# 13. Success
# ------------------------------------------------------------

Write-Host ""
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host "       Printer export completed" -ForegroundColor Green
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Export file:" -ForegroundColor Yellow
Write-Host $File.FullName -ForegroundColor White
Write-Host ""
Write-Host "File size:" -ForegroundColor Yellow
Write-Host "$($File.Length) bytes" -ForegroundColor White
Write-Host ""
Write-Host "Selected printers:" -ForegroundColor Yellow

foreach ($printer in $selected) {
    Write-Host " - $($printer.Name)" -ForegroundColor White
}

Write-Host ""
Write-Host "Export completed successfully." -ForegroundColor Green
Write-Host ""

Create the Intune Package

Create the following folder structure:

C:\Intune\Paketering\Print\ │ ├── Intunewin\ │ └── packages\ └── PrinterDeployment\ ├── PrinterExport-2026.printerExport ├── Install-Printers.ps1 ├── Uninstall-Printers.ps1 └── Detect-Printers.ps1

The PrinterDeployment folder contains all files required for the Intune Win32 package. The Intunewin folder is used as the output directory for the generated .intunewin file.

Install-Printers.ps1

Create Install-Printers.ps1 inside the PrinterDeployment folder:

#requires -Version 5.1

$ErrorActionPreference = 'Stop'

$ExportFileName = 'PrinterExport-2026.printerExport'

$WorkFolder = Join-Path $env:ProgramData 'Intune\PrinterDeployment'
$PackageExport = Join-Path $PSScriptRoot $ExportFileName
$LocalExport = Join-Path $WorkFolder $ExportFileName
$PrintBrm = Join-Path $env:windir 'System32\spool\tools\PrintBrm.exe'


# Create working directory
if (-not (Test-Path -LiteralPath $WorkFolder)) {
    New-Item -Path $WorkFolder -ItemType Directory -Force | Out-Null
}


# Verify PrintBRM
if (-not (Test-Path -LiteralPath $PrintBrm)) {
    Write-Error "PrintBRM was not found: $PrintBrm"
    exit 1
}


# Verify printer export
if (-not (Test-Path -LiteralPath $PackageExport)) {
    Write-Error "Printer export was not found: $PackageExport"
    exit 1
}


# Make sure the Print Spooler is running
$Spooler = Get-Service -Name Spooler -ErrorAction Stop

if ($Spooler.Status -ne 'Running') {
    Start-Service -Name Spooler -ErrorAction Stop
    $Spooler.WaitForStatus(
        [System.ServiceProcess.ServiceControllerStatus]::Running,
        [TimeSpan]::FromSeconds(30)
    )
}


# Copy the printer export locally
Copy-Item -LiteralPath $PackageExport -Destination $LocalExport -Force -ErrorAction Stop


# Restore the printer configuration
& $PrintBrm -r -f $LocalExport

$ExitCode = $LASTEXITCODE


# Check PrintBRM result
if ($ExitCode -ne 0) {
    Write-Error "PrintBRM failed with exit code $ExitCode."
    exit $ExitCode
}


Write-Output 'Printer deployment completed successfully.'

exit 0

Uninstall-Printers.ps1

Create Uninstall-Printers.ps1 inside the same folder:

#requires -Version 5.1

$ErrorActionPreference = 'Stop'

$ManagedPrinters = @(
    'Printer_1st_floor'
    'Printer_2nd_floor'
)

$WorkFolder = Join-Path $env:ProgramData 'Intune\PrinterDeployment'


# Remove managed printer queues
foreach ($PrinterName in $ManagedPrinters) {
    $Printer = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue

    if ($null -eq $Printer) {
        continue
    }

    Remove-Printer -Name $PrinterName -Confirm:$false -ErrorAction Stop
}


# Remove the local printer export
$LocalExport = Join-Path $WorkFolder 'PrinterExport-2026.printerExport'

if (Test-Path -LiteralPath $LocalExport) {
    Remove-Item -LiteralPath $LocalExport -Force
}


# Remove the working directory if it is empty
if (Test-Path -LiteralPath $WorkFolder) {
    $RemainingFiles = Get-ChildItem -LiteralPath $WorkFolder -Force -ErrorAction SilentlyContinue

    if ($RemainingFiles.Count -eq 0) {
        Remove-Item -LiteralPath $WorkFolder -Force
    }
}


Write-Output 'Printer removal completed successfully.'

exit 0

Detect-Printers.ps1

Create Detect-Printers.ps1 in the same folder. The detection script verifies that all printer queues managed by this package are installed:

#requires -Version 5.1

$RequiredPrinters = @(
    'Printer_1st_floor'
    'Printer_2nd_floor'
)

$InstalledPrinters = @(
    Get-Printer -ErrorAction SilentlyContinue |
    Select-Object -ExpandProperty Name
)

$MissingPrinters = @(
    $RequiredPrinters |
    Where-Object { $_ -notin $InstalledPrinters }
)


if ($MissingPrinters.Count -eq 0) {
    Write-Output 'All required printers are installed.'
    exit 0
}


Write-Output 'The following printers are missing:'

foreach ($PrinterName in $MissingPrinters) {
    Write-Output " - $PrinterName"
}

exit 1

Final Package Structure

After creating the scripts and copying the printer export, verify that the folder contains these four files:

C:\Intune\Paketering\Print\packages\PrinterDeployment\ │ ├── PrinterExport-2026.printerExport ├── Install-Printers.ps1 ├── Uninstall-Printers.ps1 └── Detect-Printers.ps1

Create the Win32 Package

Download the Win32 Content Prep Tool

Download Microsoft's Win32 Content Prep Tool and save IntuneWinAppUtil.exe on the packaging computer.

Run the Win32 Content Prep Tool

cd C:\Intune\Tools
.\IntuneWinAppUtil.exe

1. Source Folder

C:\Intune\Paketering\Print\packages\PrinterDeployment

2. Setup File

Install-Printers.ps1

3. Output Folder

C:\Intune\Paketering\Print\Intunewin

4. Catalog Folder

N

Verify the Package

Get-ChildItem "C:\Intune\Paketering\Print\Intunewin"

You should now have a structure similar to:

C:\Intune\Paketering\Print\ │ ├── Intunewin\ │ └── Install-Printers.intunewin │ └── packages\ └── PrinterDeployment\ ├── PrinterExport-2026.printerExport ├── Install-Printers.ps1 ├── Uninstall-Printers.ps1 └── Detect-Printers.ps1
Important: Keep the original PrinterDeployment folder for future package updates.

🚀 Deploy the Win32 Printer Package in Microsoft Intune

Once your .intunewin package is ready, the final step is to publish it in Intune so devices can install the printers automatically or on-demand.

1Open Intune Admin Center

Sign in to intune.microsoft.com using an account that has permission to manage Windows applications.

Navigate to: Apps → Windows → Add

2Select the Win32 App Type

Under App type, choose Windows app (Win32). Click Select to continue.

3Upload Your .intunewin Package

Choose Select app package file. Browse to the file you created earlier:

Deploy_Printers.intunewin

Confirm the selection and proceed.

4Configure App Information

Provide the basic metadata:

  • Application name
  • Publisher
  • Category
  • Description
  • Optional logo/icon

Click Next when ready.

5Define Install and Uninstall Commands

Use your PowerShell deployment scripts:

Install command:

powershell.exe -ExecutionPolicy Bypass -File Deploy_Printers.ps1

Uninstall command:

powershell.exe -ExecutionPolicy Bypass -File Remove_Printers.ps1

Set Install behavior to Device.

6Requirements

Specify the minimum OS version or architecture if needed. Most printer deployments work fine with default settings.

7Detection Rules

Choose Manually configure detection rules. Add a File rule and point it to the marker file your script creates, for example:

C:\ProgramData\Printerdeployment\

This ensures Intune can verify whether the printers are installed.

8Dependencies & Supersedence (Optional)

If you don't need them, simply skip these steps.

9Assign the Application

Choose how the app should be delivered:

  • Available — users install via Company Portal
  • Required — automatic installation on targeted devices

For testing, assign it to a small group — for example All users, or a specific device group (Library, Contact Center, Social Services).

10Create and Publish the App

Click Create. Intune will upload the package, process it, and make it available to assigned devices.


🧪 Testing the Deployment (Available vs Required)

The behavior on the client device depends on how you assigned the Win32 app in Intune. **For this lab, I assigned the application as Required rather than making it available through the Company Portal.**

If the app is Required

The installation will run automatically as soon as the device checks in with Intune. Once the installation completes, open Settings → Bluetooth & devices → Printers & scanners and you should see the printers that were defined.

If the app is Available

  1. Open Company Portal
  2. Find your Win32 app (for example, Deploy Printers)
  3. Select Install

🎉 Deployment complete. You now have a fully functional Intune-managed printer deployment pipeline. You can customize the app name and icon, offer printers as optional installs via Company Portal, or enforce automatic installation using Required assignments.

Kommentarer

Populära inlägg i den här bloggen

🚀 Force Reinstallation of an Intune App

🔵Troubleshooting Intune Device Enrollments: Understanding GUIDs, Registry Paths, and EnterpriseMgmt Tasks

🚀 Windows Autopilot Self-Deploying Mode — Zero-Touch Setup That Feels Like Magic