The bootstrap looked for C:\Enrollment\kiosk\Install-ShopdbKiosk.ps1, but startnet copies the type-specific tree to C:\Enrollment\shopfloor-setup\gea-shopfloor-display\ - so the payload lands beside the bootstrap, not under C:\Enrollment. As written it would have logged "Installer not staged" on every 15-minute cycle and never installed anything, with no error anywhere. Exactly the silent-failure shape this whole day has been about, and it would have looked like the API being unreachable. Resolves via a -KioskRoot parameter defaulting to kiosk\ next to the script, so the bootstrap and its payload travel together regardless of where the tree is staged. display-type.txt still comes from EnrollmentRoot, which is correct - that one really is at C:\Enrollment.
190 lines
7.9 KiB
PowerShell
190 lines
7.9 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Run the ShopDB kiosk installer as soon as ShopDB becomes reachable.
|
|
|
|
.DESCRIPTION
|
|
A PXE-imaged display currently ends up with NO GE-Enforce client at all.
|
|
Confirmed on 579C144, 2026-08-06:
|
|
|
|
is the client installed? NOT FOUND
|
|
scheduled tasks that would run it? NONE
|
|
|
|
The client is delivered by Install-ShopdbKiosk.ps1, which downloads itself
|
|
from {BaseUrl}/installers/kiosk over HTTPS - and ShopDB is only reachable
|
|
once the bay has joined the AESFMA wifi SSID. So the installer cannot run
|
|
during imaging, and nothing was arranged to run it afterwards.
|
|
|
|
This closes that gap. Imaging stages the installer and the key; this waiter
|
|
runs as SYSTEM on a schedule, does nothing until ShopDB answers, then runs
|
|
the installer once and removes itself.
|
|
|
|
Deliberately does NOT reimplement the installer. It waits, invokes, verifies
|
|
and cleans up - the vendor script keeps doing the actual work, so it can be
|
|
replaced wholesale without touching this.
|
|
|
|
.PARAMETER EnrollmentRoot
|
|
Where imaging staged things. Expects:
|
|
<root>\display-type.txt Dashboard | Lobby | 3DPrintRoom
|
|
and, beside this script (see -KioskRoot):
|
|
kiosk\Install-ShopdbKiosk.ps1
|
|
kiosk\shopdb-key.txt (optional) labelled: collector=..., fetch=...
|
|
|
|
.PARAMETER TaskName
|
|
The scheduled task to remove once installation succeeds.
|
|
|
|
.NOTES
|
|
The key file is deleted after a successful install. The installer writes the
|
|
secrets into HKLM:\SOFTWARE\GE\ShopDB, which is ACLed to SYSTEM and
|
|
Administrators - a better home than a file readable by any local user until
|
|
lockdown. Bounding that exposure to the imaging window is the point.
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$EnrollmentRoot = 'C:\Enrollment',
|
|
|
|
# Where the staged installer and key live. Defaults to kiosk\ NEXT TO THIS
|
|
# SCRIPT, not under EnrollmentRoot: startnet copies the whole type-specific
|
|
# tree to
|
|
# C:\Enrollment\shopfloor-setup\gea-shopfloor-display\
|
|
# so the payload arrives beside this file, not at C:\Enrollment\kiosk.
|
|
# Getting this wrong is silent - the bootstrap just logs "not staged" every
|
|
# cycle and never installs anything.
|
|
[string]$KioskRoot = (Join-Path $PSScriptRoot 'kiosk'),
|
|
[string]$BaseUrl = 'https://tsgwp00525.wjs.geaerospace.net/shopdb',
|
|
[string]$TaskName = 'ShopDB Kiosk Bootstrap',
|
|
[int]$TimeoutSeconds = 30
|
|
)
|
|
|
|
$ErrorActionPreference = 'Continue'
|
|
$logDir = 'C:\Logs\ShopDB'
|
|
$log = Join-Path $logDir 'kiosk-bootstrap.log'
|
|
New-Item -ItemType Directory -Path $logDir -Force -EA SilentlyContinue | Out-Null
|
|
|
|
function Log { param([string]$m)
|
|
$line = "{0} {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $m
|
|
Write-Host $line
|
|
Add-Content -Path $log -Value $line -EA SilentlyContinue
|
|
}
|
|
|
|
function Remove-Self {
|
|
try {
|
|
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -EA Stop
|
|
Log "Unregistered '$TaskName' - nothing further to do."
|
|
} catch { Log "Could not unregister '$TaskName': $_" }
|
|
}
|
|
|
|
Log "=== kiosk bootstrap ==="
|
|
|
|
# --- already done? ------------------------------------------------------
|
|
# Both conditions, not just the registry key: BaseUrl alone can be present from
|
|
# a partial run, and the enforce task is what actually proves an install.
|
|
$reg = 'HKLM:\SOFTWARE\GE\ShopDB'
|
|
$haveCfg = (Test-Path $reg) -and ((Get-ItemProperty $reg -EA SilentlyContinue).BaseUrl)
|
|
$haveTask = [bool](Get-ScheduledTask -EA SilentlyContinue |
|
|
Where-Object { $_.TaskName -match '(?i)shopdb.*enforce' })
|
|
if ($haveCfg -and $haveTask) {
|
|
Log "Already installed (BaseUrl set, enforce task present)."
|
|
Remove-Self
|
|
return
|
|
}
|
|
|
|
# --- what kind of display is this? --------------------------------------
|
|
$dtFile = Join-Path $EnrollmentRoot 'display-type.txt'
|
|
if (-not (Test-Path $dtFile)) {
|
|
Log "No $dtFile - cannot choose a DisplayType. Leaving the task armed."
|
|
return
|
|
}
|
|
$displayType = (Get-Content $dtFile -First 1).Trim()
|
|
if ($displayType -notin @('Dashboard','Lobby','3DPrintRoom')) {
|
|
Log "display-type.txt says '$displayType', which the installer will reject. Leaving armed."
|
|
return
|
|
}
|
|
Log "DisplayType: $displayType"
|
|
|
|
$installer = Join-Path $KioskRoot 'Install-ShopdbKiosk.ps1'
|
|
if (-not (Test-Path $installer)) {
|
|
Log "Installer not staged at $installer. Leaving armed."
|
|
return
|
|
}
|
|
|
|
# --- is ShopDB reachable yet? -------------------------------------------
|
|
# Expected to fail until the bay joins AESFMA. That is the whole reason this
|
|
# script exists, so a failure here is logged quietly and retried, not raised.
|
|
$probe = "$($BaseUrl.TrimEnd('/'))/api/docs"
|
|
try {
|
|
$r = Invoke-WebRequest -Uri $probe -UseBasicParsing -TimeoutSec $TimeoutSeconds -EA Stop
|
|
Log "ShopDB reachable (HTTP $($r.StatusCode))."
|
|
} catch {
|
|
$code = try { $_.Exception.Response.StatusCode.value__ } catch { $null }
|
|
if ($code) {
|
|
# Answered at all = reachable. 401/403 just means no token on this probe.
|
|
Log "ShopDB reachable (HTTP $code)."
|
|
} else {
|
|
Log "Not reachable yet - waiting for AESFMA. ($($_.Exception.Message))"
|
|
return
|
|
}
|
|
}
|
|
|
|
# --- keys ----------------------------------------------------------------
|
|
$keyFile = Join-Path $KioskRoot 'shopdb-key.txt'
|
|
$collectorKey = ''
|
|
$fetchToken = ''
|
|
if (Test-Path $keyFile) {
|
|
# LABELLED format, because the two tokens are not interchangeable and a
|
|
# positional mix-up is silent: a fetch token in the collector slot leaves
|
|
# asset reporting broken while everything looks configured.
|
|
#
|
|
# collector=<collector.ingest PAT> always needed for asset reporting
|
|
# fetch=<geenforce.fetch PAT> optional when the subnet is allowlisted
|
|
#
|
|
# A bare single line is REJECTED rather than guessed at.
|
|
foreach ($line in (Get-Content $keyFile -EA SilentlyContinue)) {
|
|
$t = $line.Trim()
|
|
if (-not $t -or $t.StartsWith('#')) { continue }
|
|
if ($t -match '^(?i)collector\s*=\s*(.+)$') { $collectorKey = $Matches[1].Trim() }
|
|
elseif ($t -match '^(?i)fetch\s*=\s*(.+)$') { $fetchToken = $Matches[1].Trim() }
|
|
else {
|
|
Log "Key file line is not labelled 'collector=' or 'fetch=' - ignoring it rather than guessing which token it is."
|
|
}
|
|
}
|
|
Log ("Key file present (collector={0}, fetch={1})" -f `
|
|
$(if ($collectorKey) { 'yes' } else { 'no' }), $(if ($fetchToken) { 'yes' } else { 'no' }))
|
|
} else {
|
|
# Not fatal. The fetch token is unnecessary when the kiosk subnet is
|
|
# IP-allowlisted; only the asset-report task needs the collector key, and
|
|
# the installer skips that task rather than failing.
|
|
Log "No key file - installing without tokens (allowlisted subnets still work; no asset reporting)."
|
|
}
|
|
|
|
# --- run the vendor installer -------------------------------------------
|
|
$args = @{ DisplayType = $displayType; BaseUrl = $BaseUrl }
|
|
if ($collectorKey) { $args.CollectorKey = $collectorKey }
|
|
if ($fetchToken) { $args.ShopdbToken = $fetchToken }
|
|
|
|
Log "Running $installer ..."
|
|
try {
|
|
& $installer @args 2>&1 | ForEach-Object { Log " $_" }
|
|
} catch {
|
|
Log "Installer threw: $_"
|
|
return
|
|
}
|
|
|
|
# --- verify, then clean up ----------------------------------------------
|
|
$haveCfg = (Test-Path $reg) -and ((Get-ItemProperty $reg -EA SilentlyContinue).BaseUrl)
|
|
$haveTask = [bool](Get-ScheduledTask -EA SilentlyContinue |
|
|
Where-Object { $_.TaskName -match '(?i)shopdb.*enforce' })
|
|
if ($haveCfg -and $haveTask) {
|
|
Log "Install verified: BaseUrl set and enforce task registered."
|
|
if (Test-Path $keyFile) {
|
|
# The installer has moved the secrets into HKLM (SYSTEM/Admins ACL).
|
|
# The staged copy is readable by any local user until lockdown, so it
|
|
# goes now rather than lingering.
|
|
Remove-Item $keyFile -Force -EA SilentlyContinue
|
|
Log "Removed staged key file."
|
|
}
|
|
Remove-Self
|
|
} else {
|
|
Log "Installer ran but verification FAILED (BaseUrl=$haveCfg, enforceTask=$haveTask). Leaving armed to retry."
|
|
}
|