Ask tenant + PC purpose at the PXE menu; add the missing 3D kiosk display option

PC PURPOSE / TENANT FOR STANDARD AND ENGINEER

The capability was already there - the media carries all six MCL packages
(PERS/SH/LOAN x GCCH/RoW) and Start-BulkEnrollOrchestrator.ps1 picks one from
whatever Select-PCConfig.ps1 returns. The problem was WHEN it asks: a GUI at
first logon, so an otherwise unattended build stops at a dialog until someone
walks over.

Now startnet asks after Standard/Engineer is chosen, while the tech who knows
what the machine is for is standing at it, and writes {Tenant}_{Purpose} to
C:\Enrollment\pc-config.txt. A shim at the path the orchestrator already calls
returns that value and falls through to the vendor dialog when it is absent or
malformed - so "ask at first logon" stays available and bays imaged before this
are unaffected. The vendor script is preserved as Select-PCConfig-vendor.ps1;
replace THAT when the vendor ships a new one.

The tenant is not cosmetic. Insert-BPRTToPPKG.ps1 matches the bulk token on
Tenant AND Purpose, so this selects which token gets injected.

ONLY THE CHOICE IS PRE-SEEDED, deliberately. Injection stays at first logon
because it fetches an encrypted token table from mcl.dwcdn.geaerospace.com,
unreachable from the isolated imaging LAN - which is also why routing MCL
packages through run-enrollment.ps1 would not work: provtool would get a package
whose token is still the Exp_XXXXXXXX placeholder and join nothing.

Standard/engineer previously skipped staging entirely (no ppkg, no PCTYPE), so
the staging gate now also admits PCCONFIG.

3D PARTS KIOSK

The display submenu offered Dashboard and Lobby only. The GE-Enforce display
scope has always had a third entry - 3DPrintRoom -> /shopdb/parts-kiosk - so a
parts kiosk could only be set by editing display-type.txt by hand after imaging.
Added as option 3. The value is a KEY into that map, so the spelling matches
exactly.

Verified: startnet parens balance, every goto resolves, 994 CRLF lines with no
bare LF; shim parses clean. Deployed - boot.wim 4d16c946, shim staged with the
vendor dialog preserved.
This commit is contained in:
cproudlock
2026-08-06 14:37:20 -04:00
parent 68df59e117
commit 9d51c0b987
2 changed files with 166 additions and 3 deletions

View File

@@ -0,0 +1,84 @@
<#
.SYNOPSIS
Return the tenant + PC purpose, pre-seeded if WinPE already asked.
.DESCRIPTION
Start-BulkEnrollOrchestrator.ps1 calls this at first logon and captures
stdout:
$pcType = powershell.exe -File 'C:\Deploy\Applications\BPRT\Select-PCConfig.ps1' -LogoPath '...'
It uses the answer twice - to pick which MCL package to apply, and to select
which bulk token Insert-BPRTToPPKG.ps1 injects (it matches on Tenant AND
Purpose). The vendor's version asks with a GUI, which means an otherwise
unattended build stops at a dialog until somebody walks over.
startnet.cmd now asks the same question at the PXE menu, while the tech who
knows what the machine is for is standing at it, and writes the answer to
C:\Enrollment\pc-config.txt. This shim returns that value when present and
falls through to the vendor dialog when it is not - so nothing is lost if the
tech picks "ask at first logon", or on a bay imaged before this existed.
ONLY the choice is pre-seeded. Token injection still happens at first logon:
Insert-BPRTToPPKG.ps1 fetches an encrypted token table from
mcl.dwcdn.geaerospace.com, which is unreachable from the isolated imaging
LAN.
.OUTPUTS
"{Tenant}_{PCType}" - Tenant GCCH|RoW, PCType PERS|SH|LOAN.
Identical contract to the vendor script.
.NOTES
The vendor script is kept alongside as Select-PCConfig-vendor.ps1. Replace
THAT file when the vendor ships a new one; this shim only needs changing if
the return contract changes.
#>
param(
[Parameter(Mandatory = $false)]
[string]$LogoPath
)
$seedFile = 'C:\Enrollment\pc-config.txt'
$vendor = Join-Path $PSScriptRoot 'Select-PCConfig-vendor.ps1'
$logDir = 'C:\Logs\BPRT\Orchestrator'
$shimLog = Join-Path $logDir 'Select-PCConfig-shim.log'
function Log {
param([string]$Message)
try {
New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null
Add-Content -Path $shimLog -Value ("{0} {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message)
} catch { }
}
# Valid values, so a typo in the seed file falls back to the dialog instead of
# handing the orchestrator a string that matches no token and no package.
$validTenants = @('GCCH', 'RoW')
$validPurposes = @('PERS', 'SH', 'LOAN')
if (Test-Path $seedFile) {
$seed = (Get-Content $seedFile -First 1 -ErrorAction SilentlyContinue)
if ($seed) { $seed = $seed.Trim() }
$parts = if ($seed) { $seed.Split('_') } else { @() }
if ($parts.Count -eq 2 -and $validTenants -contains $parts[0] -and $validPurposes -contains $parts[1]) {
Log "Pre-seeded value '$seed' from $seedFile - dialog skipped."
Write-Output $seed
exit 0
}
Log "Ignoring malformed pre-seed '$seed' in $seedFile; falling back to the dialog."
}
if (Test-Path $vendor) {
Log "No usable pre-seed; showing the vendor dialog."
if ($LogoPath) { & $vendor -LogoPath $LogoPath }
else { & $vendor }
exit $LASTEXITCODE
}
# Neither a seed nor the vendor dialog. Say so in the log rather than returning
# something invented - the orchestrator would inject the wrong token.
Log "ERROR: no pre-seed at $seedFile and no vendor script at $vendor."
exit 1

View File

@@ -47,9 +47,59 @@ REM --- Shopfloor images (3,6,7) need GCCH enrollment + (for 3) PC-type sub-menu
REM Choice 3 = GEA Shopfloor: drill into sub-menu first to pick the gea-shopfloor-*
REM sub-category, THEN the office menu, THEN machine number for collections+nocollections.
set PPKG=
set PCCONFIG=
if "%choice%"=="3" goto gea_shopfloor_submenu
if "%choice%"=="6" goto enroll_menu
if "%choice%"=="7" goto enroll_menu
REM Standard and engineer enrol through the vendor orchestrator, which asks for
REM tenant + PC purpose with a GUI at first logon. Ask here instead, while the
REM tech who knows what the machine is for is standing at it, and pre-seed the
REM answer so the dialog never appears. See :gea_purpose_submenu.
if "%choice%"=="1" goto gea_purpose_submenu
if "%choice%"=="2" goto gea_purpose_submenu
goto enroll_staged
:gea_purpose_submenu
cls
echo.
echo ========================================
echo PC Purpose - Entra enrollment
echo ========================================
echo.
echo 1. Single-User PC (one assigned person)
echo 2. Shared PC (multiple users)
echo 3. Loaner PC (temporary assignment)
echo.
echo 4. Ask at first logon (show the vendor dialog instead)
echo.
set PCPURPOSE=
set /p purp_choice=Enter your choice (1-4):
if "%purp_choice%"=="1" set PCPURPOSE=PERS
if "%purp_choice%"=="2" set PCPURPOSE=SH
if "%purp_choice%"=="3" set PCPURPOSE=LOAN
if "%purp_choice%"=="4" goto enroll_staged
if "%PCPURPOSE%"=="" goto gea_purpose_submenu
:gea_tenant_submenu
cls
echo.
echo ========================================
echo Entra Tenant
echo ========================================
echo.
echo 1. GCC High (GCCH - US)
echo 2. Rest of World (RoW)
echo.
REM The tenant is not cosmetic: Insert-BPRTToPPKG.ps1 matches the bulk token by
REM Tenant AND Purpose, so this picks which token gets injected.
set PCTENANT=
set /p ten_choice=Enter your choice (1-2):
if "%ten_choice%"=="1" set PCTENANT=GCCH
if "%ten_choice%"=="2" set PCTENANT=RoW
if "%PCTENANT%"=="" goto gea_tenant_submenu
set PCCONFIG=%PCTENANT%_%PCPURPOSE%
echo.
echo PC config: %PCCONFIG%
goto enroll_staged
:gea_shopfloor_submenu
@@ -163,11 +213,21 @@ echo ========================================
echo.
echo 1. Dashboard (shop floor metrics dashboard)
echo 2. Lobby Display (lobby information screen)
echo 3. 3D Parts Kiosk (printed-parts kiosk)
echo.
REM These values are KEYS into the GE-Enforce display scope
REM (shopdb-flask plugins/geenforce/seed_display_scope.py):
REM Dashboard -> /shopdb/shopfloor, Lobby -> /shopdb/tv,
REM 3DPrintRoom -> /shopdb/parts-kiosk
REM They are written to C:\Enrollment\display-type.txt and the dispatcher reads
REM that as its fallback when the server has no IP-based role for the device, so
REM the spelling must match the map exactly. 3DPrintRoom was in the map but had
REM no menu entry, so a parts kiosk could only be set by hand after imaging.
set DISPLAYTYPE=
set /p disp_choice=Enter your choice (1-2):
set /p disp_choice=Enter your choice (1-3):
if "%disp_choice%"=="1" set DISPLAYTYPE=Dashboard
if "%disp_choice%"=="2" set DISPLAYTYPE=Lobby
if "%disp_choice%"=="3" set DISPLAYTYPE=3DPrintRoom
if "%DISPLAYTYPE%"=="" goto display_submenu
goto enroll_menu
@@ -410,7 +470,9 @@ if errorlevel 1 goto wait_start
echo PESetup.exe is running. Waiting for imaging to complete...
REM --- Copy enrollment package and shopfloor setup as soon as Windows partition appears ---
if "%PPKG%"=="" if "%PCTYPE%"=="" goto wait_finish
REM PCCONFIG is here so standard/engineer builds still reach the volume wait:
REM they select no ppkg and no PCTYPE, but they do need pc-config.txt written.
if "%PPKG%"=="" if "%PCTYPE%"=="" if "%PCCONFIG%"=="" goto wait_finish
REM --- Wait for the volume PESetup applies Windows to -----------------------
REM PESetup hardcodes W: in nine places - every copy destination, both DISM
REM offline sessions, bcdboot and reagentc - and CREATES it during its own disk
@@ -472,7 +534,24 @@ REM the >> redirect after the screen echo. robocopy /LOG+ appends its
REM own output directly.
set STAGELOG=%OSDRIVE%\Enrollment\winpe-staging.log
echo [%DATE% %TIME%] WinPE staging started >> "%STAGELOG%"
echo PCTYPE=%PCTYPE% PPKG=%PPKG% MACHINENUM=%MACHINENUM% CMMID=%CMMID% >> "%STAGELOG%"
echo PCTYPE=%PCTYPE% PPKG=%PPKG% MACHINENUM=%MACHINENUM% CMMID=%CMMID% PCCONFIG=%PCCONFIG% >> "%STAGELOG%"
REM --- Pre-seed the tenant + PC purpose for the enrollment orchestrator -----
REM Start-BulkEnrollOrchestrator.ps1 asks for this with a GUI at first logon
REM (Select-PCConfig.ps1) and uses the answer twice: to pick which MCL package
REM to apply, and to select which bulk token Insert-BPRTToPPKG.ps1 injects -
REM it matches on Tenant AND Purpose. Writing the answer here lets the shim
REM return it non-interactively, so an unattended build never stops on a dialog.
REM Format is exactly what the vendor dialog returns: {Tenant}_{Purpose},
REM e.g. GCCH_PERS, GCCH_SH, RoW_LOAN.
REM Only the CHOICE is pre-seeded. Token injection still happens at first logon
REM because it fetches an encrypted token table from mcl.dwcdn.geaerospace.com,
REM which is unreachable from this isolated imaging LAN.
if not "%PCCONFIG%"=="" (
echo %PCCONFIG%> %OSDRIVE%\Enrollment\pc-config.txt
echo Pre-seeded PC config %PCCONFIG% for the enrollment orchestrator.
echo [%TIME%] Wrote pc-config.txt=%PCCONFIG% >> "%STAGELOG%"
)
REM --- Copy site config (drives site-specific values in all setup scripts) ---
if exist "Y:\config\site-config.json" (