diff --git a/playbook/shopfloor-setup/BPRT/Select-PCConfig.ps1 b/playbook/shopfloor-setup/BPRT/Select-PCConfig.ps1 new file mode 100644 index 0000000..bff9ea6 --- /dev/null +++ b/playbook/shopfloor-setup/BPRT/Select-PCConfig.ps1 @@ -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 diff --git a/playbook/startnet.cmd b/playbook/startnet.cmd index fde408d..1890f20 100644 --- a/playbook/startnet.cmd +++ b/playbook/startnet.cmd @@ -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" (