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

115 lines
4.5 KiB
Markdown

# 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.
```json
{
"_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.
```powershell
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 |