# Install-ShopdbPrinterDrivers.ps1 # # Installs a SITE'S WHOLE DRIVER SET from a manifest, so a bay ends up with every # printer driver it might need in one converging run. Wraps # Install-ShopdbPrinterDriver.ps1, which does one driver. # # DESIGNED FOR DSC / Intune / GE-Enforce. It declares state rather than # performing an install: a driver already present is skipped, so this is safe to # run on a schedule and cheap when there is nothing to do. That is what lets a # DSC Script resource call it from TestScript as well as SetScript. # # THE MANIFEST, not arguments, is the contract. drivers.json lists each driver by # the name its INF declares - what Add-PrinterDriver matches on, verbatim - and # where its package lives. Paths are relative to this script, or absolute (a UNC # path on a site's share is normal). # # EXIT CODE: 0 when every driver in the manifest is present at the end, 1 when # one or more could not be installed. DSC needs a real answer here, unlike the # single-driver script which never fails an enforcement run. The per-driver log # says which and why. # # SHARE PATHS: on a GE-Enforce site the packages usually live on the SFLD share, # which is mounted ONLY during the enforcement cycle. Run this as a manifest # entry inside that cycle, not as its own scheduled task. param( # Defaults to drivers.json beside this script. [string]$Manifest = '', # Install only these driver names; everything else in the manifest is # ignored. For a bay that needs one driver out of a site-wide set. [string[]]$Only = @(), # Report what is missing and change nothing. This is what a DSC TestScript # calls: exit 0 means compliant. [switch]$TestOnly ) $ErrorActionPreference = 'Continue' $here = Split-Path -Parent $MyInvocation.MyCommand.Path if (-not $Manifest) { $Manifest = Join-Path $here 'drivers.json' } $logDir = 'C:\Logs\Shopfloor' if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null } $logFile = Join-Path $logDir ('printer-drivers-{0}.log' -f (Get-Date -Format 'yyyyMMdd')) function Log([string]$msg) { $ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' "$ts [set] $msg" | Tee-Object -FilePath $logFile -Append | Out-Null } if (-not (Test-Path $Manifest)) { Log "ERROR manifest not found: $Manifest" exit 1 } try { $config = Get-Content -Raw -Path $Manifest | ConvertFrom-Json } catch { Log "ERROR manifest is not valid JSON: $($_.Exception.Message)" exit 1 } $wanted = @($config.drivers) if ($Only.Count -gt 0) { $wanted = @($wanted | Where-Object { $Only -contains $_.drivername }) } if ($wanted.Count -eq 0) { Log "nothing to do: the manifest selects no drivers" exit 0 } $single = Join-Path $here 'Install-ShopdbPrinterDriver.ps1' if (-not (Test-Path $single)) { Log "ERROR Install-ShopdbPrinterDriver.ps1 is not beside this script" exit 1 } $missing = @() foreach ($driver in $wanted) { $name = $driver.drivername if (-not $name) { continue } if (Get-PrinterDriver -Name $name -ErrorAction SilentlyContinue) { Log "present: $name" continue } if ($TestOnly) { Log "MISSING: $name" $missing += $name continue } # Relative paths are resolved against the package, so the whole thing can be # copied anywhere - a share, C:\ProgramData, an Intune staging folder - and # still find its own payloads. $path = $driver.path if ($path -and -not [System.IO.Path]::IsPathRooted($path)) { $path = Join-Path $here $path } if (-not $path -or -not (Test-Path $path)) { Log "ERROR package not found for '$name': $path" $missing += $name continue } Log "installing: $name" & $single -DriverName $name -Source $path | Out-Null if (Get-PrinterDriver -Name $name -ErrorAction SilentlyContinue) { Log "installed: $name" } else { Log "FAILED: $name (see the per-driver lines above)" $missing += $name } } if ($missing.Count -gt 0) { Log ("not present: {0}" -f ($missing -join ', ')) exit 1 } Log "all $($wanted.Count) driver(s) present" exit 0