printers low-toner alerts; geenforce display readiness + token scope-binding; air-gap docker deploy kit

This commit is contained in:
2026-07-23 14:37:00 -04:00
parent 73b05e5b1c
commit c355eaae43
32 changed files with 2358 additions and 46 deletions

View File

@@ -24,6 +24,17 @@
.PARAMETER ShadowMode
Fetch + compare + report, but install from the share (no behavior change).
.PARAMETER IncludeCommon
Also fetch the fleet-wide CommonScope and merge it into -Scope, so the PC
enforces its own scope entries PLUS common's (on a Name conflict the -Scope
entry wins). OFF by default: a scope is enforced ALONE (self-sufficient).
Displays are self-sufficient and do NOT set this. Only a share-less non-display
PC that genuinely needs the fleet-wide common entries over HTTPS turns it on.
.PARAMETER CommonScope
Name of the fleet-wide scope merged when -IncludeCommon is set (default
'common'). Ignored without -IncludeCommon.
.NOTES
Fail-safe: any error exits 0 so a bad web app never blocks or breaks a PC.
Config comes from HKLM:\SOFTWARE\GE\ShopDB (BaseUrl, ApiToken) - see the psm1.
@@ -34,6 +45,8 @@ param(
[Parameter(Mandatory)] [string]$EnginePath,
[string]$ShareManifestPath,
[switch]$ShadowMode,
[switch]$IncludeCommon,
[string]$CommonScope = 'common',
[string]$BaseUrl,
[string]$ApiToken,
[string]$LogFile = "C:\Logs\Shopfloor\shopdb-enforce-$(Get-Date -Format yyyyMMdd).log"
@@ -46,6 +59,27 @@ function Write-Log {
Write-Host $line
}
function Write-ShopdbEventLog {
<#
Write a Windows Application event-log entry under source 'ShopdbEnforce'.
Used to make an otherwise silent fail-safe (no manifest and an empty cache)
observable to whoever watches the display. Best-effort: registering the
source needs admin, which the SYSTEM scheduled task has; any failure is
swallowed so it can never break the fail-safe.
#>
param([string]$Message,
[string]$EntryType = 'Warning',
[int]$EventId = 1001)
$source = 'ShopdbEnforce'
try {
if (-not [System.Diagnostics.EventLog]::SourceExists($source)) {
New-EventLog -LogName Application -Source $source -ErrorAction Stop
}
Write-EventLog -LogName Application -Source $source -EntryType $EntryType `
-EventId $EventId -Message $Message -ErrorAction Stop
} catch {}
}
try {
Import-Module (Join-Path $PSScriptRoot 'ShopdbEnforceClient.psm1') -Force
@@ -57,7 +91,21 @@ try {
$sync = Sync-ShopdbManifest -Scope $Scope -Config $config
if (-not $sync.Path) {
Write-Log "No manifest available for $Scope (shopdb unreachable, no cache)." 'WARN'
# Fail-safe stays (exit 0), but a fresh display with an empty cache would
# otherwise enforce nothing SILENTLY. Surface it: a Windows event-log
# entry plus a best-effort report ping so it shows in Enforcement Reports.
$reason = if ($sync.Error) { $sync.Error } else { 'shopdb unreachable and no cached manifest' }
Write-Log "No manifest available for $Scope ($reason)." 'WARN'
Write-ShopdbEventLog -Message ("GE-Enforce could not fetch a manifest for scope '$Scope' and has no cached copy; nothing was enforced this cycle. Reason: $reason") -EntryType 'Error' -EventId 1001
try {
$failReport = New-ShopdbReport -Scope $Scope -AppliedVersion 0 -Summary @{
Installed = 0; Skipped = 0; Failed = 1; Filtered = 0; EnforcerVersion = '2.6'
Results = @(@{ Name = '(manifest-fetch)'; Action = 'failed'; Message = $reason })
}
if (Send-ShopdbReport -Config $config -Report $failReport) {
Write-Log 'Reported empty-cache fetch failure to shopdb.'
}
} catch {}
exit 0
}
Write-Log "Manifest for $Scope from $($sync.Source) (v$($sync.Version))."
@@ -75,29 +123,50 @@ try {
# Which manifest the engine actually runs against.
if ($ShadowMode -and $ShareManifestPath) {
# Shadow: install from the share exactly as today (no payload resolve).
# Shadow: install from the share exactly as today (no payload resolve,
# no common merge - the share already carries its own common scope).
$manifestToRun = $ShareManifestPath
} else {
# Optional common-scope inheritance (OFF by default; displays are
# self-sufficient). Only when -IncludeCommon is set do we fetch the
# fleet-wide common scope (best-effort, same fail-safe cache) and merge
# it in with the pctype winning on conflict. Skipped when this run IS
# the common scope.
$manifestToMerge = $sync.Path
if ($IncludeCommon -and $CommonScope -and ($CommonScope -ine $Scope)) {
$commonSync = Sync-ShopdbManifest -Scope $CommonScope -Config $config
if ($commonSync.Path) {
$manifestToMerge = Merge-ShopdbManifests -PrimaryManifestPath $sync.Path -CommonManifestPath $commonSync.Path
if ($manifestToMerge -ne $sync.Path) {
Write-Log "Merged common scope '$CommonScope' (from $($commonSync.Source), v$($commonSync.Version)) into $Scope."
}
} else {
Write-Log "Common scope '$CommonScope' unavailable (no fetch, no cache) - enforcing $Scope alone." 'WARN'
}
}
# Cutover: stage any http/inline payloads to local files and rewrite the
# manifest to point at them, so the UNCHANGED engine installs from local
# (no SMB needed for share-less PCs).
$manifestToRun = Resolve-ShopdbPayloads -ManifestPath $sync.Path -Config $config
if ($manifestToRun -ne $sync.Path) {
$manifestToRun = Resolve-ShopdbPayloads -ManifestPath $manifestToMerge -Config $config
if ($manifestToRun -ne $manifestToMerge) {
Write-Log "Resolved http/inline payloads to local files: $manifestToRun"
}
}
# --- INTEGRATION POINT ---------------------------------------------------
# Run the engine. Install-FromManifest.ps1 is expected to return (or you
# adapt it to return) a summary carrying Installed/Skipped/Failed/Filtered
# and a Results list of @{Name;Action;SelfHealed;ExitCode;Message}. Wire this
# to your engine's actual return/parse; the shape below is the contract.
# Run the engine. EXPECTED ENGINE CONTRACT: Install-FromManifest.ps1 returns
# a summary object (hashtable or PSCustomObject) carrying integer counts
# Installed / Skipped / Failed / Filtered
# a string EnforcerVersion, and a Results list of per-entry outcomes
# @{ Name; Action; SelfHealed; ExitCode; Message }.
# The engine may not honor that yet: it might return $null, a bare return
# code, or emit several objects. ConvertTo-ShopdbSummary adapts whatever it
# returns into a well-formed summary hashtable so the report stage always
# gets clean input (we do NOT assume the engine was fixed).
Write-Log "Running engine against $manifestToRun"
$summary = & $EnginePath -ManifestPath $manifestToRun -PCType $Scope
if (-not $summary) {
$summary = @{ Installed = 0; Skipped = 0; Failed = 0; Filtered = 0; Results = @() }
}
if (-not $summary.EnforcerVersion) { $summary.EnforcerVersion = '2.6' }
$engineResult = & $EnginePath -ManifestPath $manifestToRun -PCType $Scope
$summary = ConvertTo-ShopdbSummary -EngineResult $engineResult
# Report the result (best-effort).
$appliedVersion = 0