3 Adding-a-Printer
Forgejo Actions edited this page 2026-08-10 15:26:26 +00:00

Adding a new vendor/series

This walks through adding a new driver package, using the layout vendors/<vendor>/<series>/. Follow along with the existing vendors/hp/upd-pcl6/ as a reference.

1. Identify the driver package

Find the vendor's redistributable driver archive (a .zip containing one or more .inf files) and note:

  • The download URL.
  • The .inf file that is the actual entry point (a driver archive often contains several .inf files for different OS versions/architectures — pick the one matching Windows 10/11 x64).
  • The exact driver name string(s) the .inf registers — look at its [<Manufacturer>.NTamd64...] section(s). Some vendors (HP) ship one universal name that works for every model; others (UTAX/Kyocera) bundle a universal name and hundreds of per-model names in the same package, where the model string you pass actually selects a different driver, not just metadata — see Supported Printers for both patterns and how the UTAX list was extracted from the INF itself. Check which kind you have before writing Install.ps1.

2. Scaffold the folder

mkdir -p vendors/<vendor>/<series>/scripts

3. Pin the driver hash

nix develop
nix-prefetch-url --unpack <driver-url>
nix hash convert --hash-algo sha256 --to sri <hash-from-previous-command>

4. Write default.nix

Keep it minimal: fetch, unpack, call mkIntunePackage. See vendors/hp/upd-pcl6/default.nix for the exact shape — src uses fetchzip with the SRI hash from step 3, then lib.mkIntunePackage { name = ...; driver = ...; scripts = ./scripts; }. If the archive ships architectures/utilities you don't need (see vendors/utax/kx-upd/default.nix, which prunes a ~200 MB download down to the ~61 MB x64 driver tree), add a runCommand step that copies just the subfolder you need before passing it through as driver.

Never commit lib.fakeHash — if you don't have the real hash yet, that's a sign step 3 isn't done.

5. Write the scripts

scripts/Install.ps1 needs, at minimum:

  • Parameters: PrinterName (mandatory), IPAddress (mandatory), Model (optional; either purely informational, or — if this vendor bundles per-model driver names — the value that selects which one to install, falling back to the universal name when unset).
  • Dot-source lib/common-scripts/TrustDriverPublisher.ps1 (merged into every package's build tree automatically by mkIntunePackage.nix) and call Import-DriverPublisherTrust -InfPath $infPath right before staging. Without it, pnputil under SYSTEM fails with 0xE0000242 ("publisher not trusted") for any signed third-party driver, because the interactive trust prompt Windows would otherwise show has no one to click it.
  • Idempotent driver staging (pnputil.exe /add-driver ... /install, treating exit codes 0/259/3010 as success) and registration (Add-PrinterDriver).
  • Idempotent port (Add-PrinterPort/Set-PrinterPort) and printer (Add-Printer/Set-Printer) creation — check Get-* first, reconcile rather than fail if the resource already exists.
  • Logging to C:\ProgramData\IntunePrinterPackages\logs.
  • Exit code 0 on success, non-zero on failure.

scripts/Uninstall.ps1 mirrors it: remove the named printer, and remove its port only if no other printer on the box still uses it. Never remove the shared driver — other printers of the same series depend on it.

6. Register the package in flake.nix

Add one entry to vendorPackages:

"<vendor>-<series>" = pkgs.callPackage ./vendors/<vendor>/<series> { inherit mkIntunePackage; };

7. Build and sanity-check

nix build .#<vendor>-<series>
unzip -l result/<vendor>-<series>.intunewin   # expect IntuneWinPackage/Contents/... and .../Metadata/Detection.xml
nix build .#all                               # confirm the aggregate still builds

8. Validate against a real Intune tenant

Upload the built .intunewin to a test Win32 app in Intune, install it on a test device with a real or simulated printer at some IP, and confirm the printer appears and the registry detection key (HKLM\SYSTEM\CurrentControlSet\Control\Print\Printers\<PrinterName>) is created. See architecture.md#format-validation-and-fallback if the upload or install fails in a way that points at the package format itself rather than the driver/script logic.