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.
115 lines
4.5 KiB
Markdown
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 |
|