Files
shopdb-flask/plugins/printers/client/DEPLOYING-DRIVERS.md
Cam Proudlock d267813999 Printer assignment end to end, and a container that keeps a site's files
Assign printers to a MACHINE and let whichever PC controls it install them, with
the observed half kept strictly apart from the assigned half: a collector report
can never write an assignment row. Adoption is explicit. Adds a singular
relationship type so defaultprinter can mean exactly one, client scripts that
converge a bay's queues and correct a drifted one in place, and a wave gate so a
fleet-wide driver rollout cannot stampede its own share.

On the container side: the compose stack never persisted the instance directory,
so a rebuild discarded plugins.json and every upload. It is a volume now, the
image ships instance/ owned by the app user so the mountpoint is writable, and
the database moves to MySQL 8.4 LTS, which the Windows runbook has recommended
since 8.0 went end of life. The image also could not build at all: the frontend
stage never copied the plugin staging script it runs.

Adds a topology migration guide covering IIS to Docker and back.
2026-08-20 07:46:35 -04:00

5.7 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

The other half: assigning printers

Staging drivers is only delivery. Set-ShopdbPrinters.ps1 is what makes a bay's queues match ShopDB, and Apply-ShopdbDefaultPrinter.ps1 applies the default in the user's context. Two manifest entries, both DetectionMethod: Always:

{
  "_comment": "Create the queues this bay is assigned. Converges: existing queues are left alone, and nothing is ever removed.",
  "Name": "ShopDB printers",
  "Type": "PS1",
  "Script": "scripts/Set-ShopdbPrinters.ps1",
  "DetectionMethod": "Always"
}

The default printer is per-user, so SYSTEM cannot set it for the person logged on. Set-ShopdbPrinters.ps1 records it in HKLM:\SOFTWARE\GE\ShopDB DefaultPrinter, and Apply-ShopdbDefaultPrinter.ps1 runs as the user - at logon, and on a repeat if the site wants drift corrected.

Order matters on a new bay: drivers, then queues, then the default. Each step is a no-op once satisfied, so running all three every cycle costs a few registry reads.

Verified end to end on Windows 11 against a live ShopDB: printers assigned to a MACHINE, a PC controlling it and holding no rows of its own, and the bay created both queues with the right universal driver, recorded the default, and set it - then a second run changed nothing.