Files
shopdb-flask/plugins/printers/client/DEPLOYING-DRIVERS.md
cproudlock 03d0754fdc Stage printer drivers as a deployable set, for the common scope
Assigning a printer to a bay is useless if the bay cannot install it, and the
fleet data says why that mattered: 42 of 44 printers could not resolve a driver.
This is the delivery half - the drivers themselves, staged once per bay, so that
creating a queue never waits on a download.

Install-ShopdbPrinterDriver.ps1 does one driver: trust the package's signer, then
pnputil /add-driver, then Add-PrinterDriver. Install-ShopdbPrinterDrivers.ps1
does a site's whole set from drivers.json, and answers a compliance question with
-TestOnly, which is what makes it a clean DSC Script resource rather than a
fire-and-forget install.

Deliberately SEPARATE from assignment. Drivers are large, near-identical across a
fleet and change rarely; assignments are small, per-bay and change often. Staging
the set in the GE-Enforce common scope means the assignment client only ever
creates a queue - it never fetches a 48 MB package while somebody is waiting to
print, or discovers the share is unmounted at the worst moment.

THE SIGNER TRUST STEP IS THE WHOLE TRICK, and it took a real driver to find it.
certutil -addstore on the .cat file satisfied the Xerox package and failed every
HP INF with "The publisher of an Authenticode(tm) signed catalog has not yet been
established as trusted" - a coin toss, not a mechanism. The certificate is now
extracted with Get-AuthenticodeSignature and added to Trusted Publishers, for
every catalog under the package rather than the first INF's neighbours. On a
locked bay there is no prompt to answer, so the old failure was silent.

Verified on Windows against real packages, not by reading: all six drivers this
site needs install through the script, a second run is a no-op, a wrong driver
name fails with the names the package actually offers, and the DSC cycle behaves
- TestOnly exits 1 on a clean box, install exits 0, TestOnly then exits 0.

The packages themselves stay out of git: they are licensed vendor binaries, and
they belong on the share beside the other imaging payloads.

DEPLOYING-DRIVERS.md carries the GE-Enforce entry, the DSC configuration and the
Intune shape, plus the constraint that has cost a session before: the SFLD share
is mounted only during the enforcement cycle, so this runs as a manifest entry
and never as its own scheduled task.
2026-08-19 09:33:05 -04:00

4.5 KiB

Deploying the printer driver set

The driver set is a package: Install-ShopdbPrinterDrivers.ps1, the single-driver worker it wraps, a drivers.json naming each driver and where its files are, and the driver packages themselves.

Staging drivers is deliberately SEPARATE from assigning printers. Drivers are large, change rarely and are identical across a fleet; assignments are small, per-bay and change often. Keeping them apart means creating a queue never waits on a download, and a driver never has to be fetched at the moment someone is trying to print.

The shape

ShopdbPrinterDrivers\
  Install-ShopdbPrinterDrivers.ps1     the whole set, manifest driven
  Install-ShopdbPrinterDriver.ps1      one driver (this does the work)
  drivers.json                          what this site deploys
  drivers\
    hp_upd_ps\  xerox_gpd\  hp_designjet\  zebra_zt411\  ...

drivers.json paths may be relative to the package or absolute. A site whose packages already live on a share points at the share and ships only the two scripts and the manifest.

GE-Enforce, in the common scope

Every shop-floor PC gets every driver, once. After the first cycle each run is a Get-PrinterDriver check per driver and nothing else, so the cost is a few milliseconds, not a re-install.

{
  "_comment": "Stage the site's printer drivers. Runs in-cycle because the share is only mounted then. Idempotent: a driver already present is skipped.",
  "Name": "ShopDB printer drivers",
  "Type": "PS1",
  "Script": "scripts/Install-ShopdbPrinterDrivers.ps1",
  "DetectionMethod": "Always"
}

It must be a manifest entry, not its own scheduled task. The SFLD share is mounted only for the duration of the enforcement cycle; off-cycle the paths simply do not exist and every run logs "package not found" forever.

Azure Machine Configuration / DSC

The script answers a compliance question, which is what makes it a clean Script resource: -TestOnly reports whether every driver in the manifest is present and exits 0 or 1 without changing anything.

Configuration ShopdbPrinterDrivers
{
    Import-DscResource -ModuleName PSDesiredStateConfiguration

    Node localhost
    {
        Script PrinterDrivers
        {
            GetScript  = {
                @{ Result = (Get-PrinterDriver | Select-Object -ExpandProperty Name) -join ', ' }
            }
            TestScript = {
                $p = Start-Process -FilePath 'powershell.exe' -PassThru -Wait -WindowStyle Hidden `
                    -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File',
                                  'C:\ProgramData\ShopDB\Drivers\Install-ShopdbPrinterDrivers.ps1','-TestOnly'
                return ($p.ExitCode -eq 0)
            }
            SetScript  = {
                Start-Process -FilePath 'powershell.exe' -Wait -WindowStyle Hidden `
                    -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File',
                                  'C:\ProgramData\ShopDB\Drivers\Install-ShopdbPrinterDrivers.ps1'
            }
        }
    }
}

Deliver the package to C:\ProgramData\ShopDB\Drivers however that estate already delivers files - a Win32 app, a File resource, or the imaging step.

Intune

Package the folder as a Win32 app.

  • Install: powershell.exe -NoProfile -ExecutionPolicy Bypass -File Install-ShopdbPrinterDrivers.ps1
  • Detection: a script running the same file with -TestOnly, exit 0 = detected
  • Run as SYSTEM. Adding a printer driver has required administrator rights since the 2021 print hardening, and SYSTEM satisfies it.

Why not have the assignment client fetch drivers

It was considered and rejected. A bay would then download a driver at the moment a printer is assigned, which is the worst time: someone is waiting, the share may be unmounted, and a 48 MB package would be pulled per bay per change. Staging the set in common makes assignment a queue creation and nothing more.

One driver per package, named exactly

drivers.json carries the driver name as its INF declares it - Add-PrinterDriver matches that string and nothing else. The names verified on Windows for the reference site's fleet:

driver covers
HP Universal Printing PS HP office printers
Xerox Global Print Driver PCL6 Xerox office printers
HP DesignJet T1700dr V4 DesignJet plotters (a v4 class driver)
ZDesigner ZT411-300dpi ZPL Zebra ZT411 labels
EPSON TM-C3500 Epson ColorWorks labels
DTC4500e Card Printer HID FARGO card printer