Fix v2 imaging: copy common/ at imaging time + use $setupDir not $PSScriptRoot

Two bugs that have been silently masking GE-Enforce registration since
Stage 2a landed 2026-04-22, surfaced when v1 enforcers were retired
(commit 0badfc1) and could no longer cover for the missing v2 registration.

Bug 1: startnet.cmd at imaging time only xcopied Shopfloor\ and the
PCTYPE-specific dir from the imaging share to W:\Enrollment\shopfloor-setup\.
common\ was never copied. v1 dispatchers lived per-pctype and rode in via
the %PCTYPE% xcopy, so this was never noticed. v2's GE-Enforce.ps1 +
Register-GEEnforce.ps1 + lib\Install-FromManifest.ps1 all live in common\
and got skipped at imaging entirely.

Fix: add a third xcopy block for common\, mirroring the Shopfloor\ block
above it. Applies to playbook/startnet.cmd and startnet-template.cmd.

Bug 2: Run-ShopfloorSetup.ps1 line 288 set $commonSetupDir via
'Join-Path $PSScriptRoot common'. Run-ShopfloorSetup.ps1 lives at
C:\Enrollment\Run-ShopfloorSetup.ps1 (xcopied by startnet.cmd), so
$PSScriptRoot resolves to C:\Enrollment, and $commonSetupDir resolved
to C:\Enrollment\common - which is NOT where common\ lives even after
the bug 1 fix (correct path is C:\Enrollment\shopfloor-setup\common\).
The Test-Path -LiteralPath check on Register-GEEnforce.ps1 returned
false silently and GE-Enforce never registered.

Same bug existed for Register-MapSfldShare on line 321.

Fix: $PSScriptRoot -> $setupDir for both. $setupDir was already defined
on line 51 as Join-Path $enrollDir "shopfloor-setup", which is the path
the rest of the script uses consistently.

Pre-v1-cleanup, v1's per-pctype enforcer registrations on lines 322-357
(now deleted) ran independently and covered the gap, so PCs ended up
with v1 enforcers and the user thought v2 was running. Post-cleanup,
this bug means nothing gets registered.

PXE server has been patched directly: boot.wim re-baked with the new
startnet.cmd, /srv/samba/enrollment/shopfloor-setup/Run-ShopfloorSetup.ps1
replaced. New PXE-imaged PCs from this point forward will register
GE-Enforce correctly.

For PCs imaged before this fix: run Deploy-GEEnforce.ps1 from the SFLD
share's _meta/runtime/ to retrofit. Same one-liner used for promoting
v1 PCs to v2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-04-29 14:31:15 -04:00
parent 26ecd0da0a
commit 7be5518fd7
3 changed files with 56 additions and 38 deletions

View File

@@ -285,7 +285,7 @@ Unregister-ScheduledTask -TaskName 'GE Re-enable Wired NICs' -Confirm:$false -Er
Write-Warning "Failed to register NIC re-enable task: $_" Write-Warning "Failed to register NIC re-enable task: $_"
} }
$commonSetupDir = Join-Path $PSScriptRoot 'common' $commonSetupDir = Join-Path $setupDir 'common'
# --- Register the unified GE-Enforce scheduled task --- # --- Register the unified GE-Enforce scheduled task ---
# Single dispatcher for all PC-type ongoing-update enforcement. Reads # Single dispatcher for all PC-type ongoing-update enforcement. Reads
@@ -318,7 +318,7 @@ if (Test-Path -LiteralPath $registerGE) {
# vendor 'SFLD - Consume Credentials' task is principal-restricted and # vendor 'SFLD - Consume Credentials' task is principal-restricted and
# does not fire for the ShopFloor end-user, so this parallel task fills # does not fire for the ShopFloor end-user, so this parallel task fills
# the gap. Cross-PC-type because every shopfloor account needs S:. # the gap. Cross-PC-type because every shopfloor account needs S:.
$registerMapShare = Join-Path $PSScriptRoot 'Shopfloor\Register-MapSfldShare.ps1' $registerMapShare = Join-Path $setupDir 'Shopfloor\Register-MapSfldShare.ps1'
if (Test-Path -LiteralPath $registerMapShare) { if (Test-Path -LiteralPath $registerMapShare) {
Write-Host "" Write-Host ""
Write-Host "=== Registering S: drive logon mapper ===" Write-Host "=== Registering S: drive logon mapper ==="

View File

@@ -11,15 +11,18 @@ if errorlevel 1 goto wait_net
echo Network ready. echo Network ready.
REM --- BIOS update check (runs before imaging menu) --- REM --- BIOS update check (runs before imaging menu) ---
REM Mounts [winpeapps_bios] share. CRITICAL: do NOT call check-bios.cmd
REM inside an `if exist (...)` parens block - CMD's variable scoping for
REM CALLed scripts inside parens does not propagate BIOS_STATUS back to
REM this script reliably. Use goto-flow instead so the CALL runs at the
REM top scope and BIOS_STATUS persists.
set BIOS_STATUS=No BIOS check (share unavailable) set BIOS_STATUS=No BIOS check (share unavailable)
net use B: \\10.9.100.1\winpeapps\_shared /user:pxe-upload pxe /persistent:no 2>NUL net use B: \\10.9.100.1\winpeapps_bios /user:pxe-upload pxe /persistent:no 2>NUL
if exist B:\BIOS\check-bios.cmd ( if not exist B:\check-bios.cmd goto :bios_check_done
echo. echo.
echo Checking for BIOS updates... echo Checking for BIOS updates...
call B:\BIOS\check-bios.cmd call B:\check-bios.cmd
REM If BIOS was flashed, check-bios.cmd reboots and we never reach here. :bios_check_done
echo.
)
net use B: /delete 2>NUL net use B: /delete 2>NUL
:menu :menu
@@ -71,9 +74,9 @@ REM --- PPKG configuration (constructed at menu time, see docs) ---
REM Vendor ships one source PPKG; we construct the BPRT-tagged filename REM Vendor ships one source PPKG; we construct the BPRT-tagged filename
REM by filling in Office, Region, Expiry, Version on the target copy. REM by filling in Office, Region, Expiry, Version on the target copy.
REM Update SOURCE_PPKG + PPKG_VER when a new PPKG is released. REM Update SOURCE_PPKG + PPKG_VER when a new PPKG is released.
set SOURCE_PPKG=GCCH_Prod_SFLD_v4.11.ppkg set SOURCE_PPKG=GCCH_Prod_SFLD_v4.12.ppkg
set PPKG_VER=v4.11 set PPKG_VER=v4.12
set PPKG_EXP=20270430 set PPKG_EXP=20260831
set REGION=US set REGION=US
set OFFICE= set OFFICE=
@@ -129,12 +132,12 @@ echo ========================================
echo Standard PC Sub-Type echo Standard PC Sub-Type
echo ======================================== echo ========================================
echo. echo.
echo 1. Timeclock (WJ Shopfloor only) echo 1. Machine (WJ Shopfloor, Plant Apps, eDNC, UDC)
echo 2. Machine (WJ Shopfloor, Plant Apps, eDNC, UDC) echo 2. Timeclock (WJ Shopfloor only)
echo. echo.
set /p standard_choice=Enter your choice (1-2): set /p standard_choice=Enter your choice (1-2):
if "%standard_choice%"=="1" set PCSUBTYPE=Timeclock if "%standard_choice%"=="1" set PCSUBTYPE=Machine
if "%standard_choice%"=="2" set PCSUBTYPE=Machine if "%standard_choice%"=="2" set PCSUBTYPE=Timeclock
if "%PCSUBTYPE%"=="" goto standard_menu if "%PCSUBTYPE%"=="" goto standard_menu
REM --- Machine number (Standard-Machine only; Timeclock PCs do not use one) --- REM --- Machine number (Standard-Machine only; Timeclock PCs do not use one) ---
@@ -155,14 +158,14 @@ echo ========================================
echo Display Type echo Display Type
echo ======================================== echo ========================================
echo. echo.
echo 1. Lobby Display echo 1. Dashboard
echo 2. Dashboard echo 2. Lobby Display
echo. echo.
set /p display_choice=Enter your choice (1-2): set /p display_choice=Enter your choice (1-2):
if "%display_choice%"=="1" set DISPLAYTYPE=Lobby if "%display_choice%"=="1" set DISPLAYTYPE=Dashboard
if "%display_choice%"=="1" set PCSUBTYPE=Lobby if "%display_choice%"=="1" set PCSUBTYPE=Dashboard
if "%display_choice%"=="2" set DISPLAYTYPE=Dashboard if "%display_choice%"=="2" set DISPLAYTYPE=Lobby
if "%display_choice%"=="2" set PCSUBTYPE=Dashboard if "%display_choice%"=="2" set PCSUBTYPE=Lobby
if "%PCSUBTYPE%"=="" goto display_menu if "%PCSUBTYPE%"=="" goto display_menu
:skip_display_menu :skip_display_menu
@@ -312,6 +315,12 @@ if exist "Y:\shopfloor-setup\Shopfloor" (
xcopy /E /Y /I "Y:\shopfloor-setup\Shopfloor" "W:\Enrollment\shopfloor-setup\Shopfloor\" xcopy /E /Y /I "Y:\shopfloor-setup\Shopfloor" "W:\Enrollment\shopfloor-setup\Shopfloor\"
echo Copied Shopfloor baseline setup files. echo Copied Shopfloor baseline setup files.
) )
REM --- Always copy common/ (cross-PC-type GE-Enforce dispatcher + lib live here post-v2) ---
if exist "Y:\shopfloor-setup\common" (
mkdir W:\Enrollment\shopfloor-setup\common 2>NUL
xcopy /E /Y /I "Y:\shopfloor-setup\common" "W:\Enrollment\shopfloor-setup\common\"
echo Copied common setup files.
)
REM --- Copy type-specific scripts on top of baseline --- REM --- Copy type-specific scripts on top of baseline ---
if exist "Y:\shopfloor-setup\%PCTYPE%" ( if exist "Y:\shopfloor-setup\%PCTYPE%" (
mkdir "W:\Enrollment\shopfloor-setup\%PCTYPE%" 2>NUL mkdir "W:\Enrollment\shopfloor-setup\%PCTYPE%" 2>NUL
@@ -345,8 +354,8 @@ REM Copies the Hexagon installer bundle (~1.9 GB) from the PXE server enrollment
REM share onto the target disk so 09-Setup-CMM.ps1 can install from local disk. REM share onto the target disk so 09-Setup-CMM.ps1 can install from local disk.
REM The tsgwp00525 SFLD share that holds the canonical copy is not yet reachable REM The tsgwp00525 SFLD share that holds the canonical copy is not yet reachable
REM during shopfloor-setup (Azure DSC provisions those creds later), so this REM during shopfloor-setup (Azure DSC provisions those creds later), so this
REM bootstrap exists to get the first-install through. Post-imaging, the logon- REM bootstrap exists to get the first-install through. Post-imaging, the
REM triggered CMM-Enforce.ps1 takes over from the share. REM unified GE-Enforce dispatcher takes over from the share for ongoing updates.
if /i not "%PCTYPE%"=="CMM" goto skip_cmm_stage if /i not "%PCTYPE%"=="CMM" goto skip_cmm_stage
if exist "Y:\installers-post\cmm\cmm-manifest.json" ( if exist "Y:\installers-post\cmm\cmm-manifest.json" (
mkdir W:\CMM-Install 2>NUL mkdir W:\CMM-Install 2>NUL

View File

@@ -11,15 +11,18 @@ if errorlevel 1 goto wait_net
echo Network ready. echo Network ready.
REM --- BIOS update check (runs before imaging menu) --- REM --- BIOS update check (runs before imaging menu) ---
REM Mounts [winpeapps_bios] share. CRITICAL: do NOT call check-bios.cmd
REM inside an `if exist (...)` parens block - CMD's variable scoping for
REM CALLed scripts inside parens does not propagate BIOS_STATUS back to
REM this script reliably. Use goto-flow instead so the CALL runs at the
REM top scope and BIOS_STATUS persists.
set BIOS_STATUS=No BIOS check (share unavailable) set BIOS_STATUS=No BIOS check (share unavailable)
net use B: \\10.9.100.1\winpeapps\_shared /user:pxe-upload pxe /persistent:no 2>NUL net use B: \\10.9.100.1\winpeapps_bios /user:pxe-upload pxe /persistent:no 2>NUL
if exist B:\BIOS\check-bios.cmd ( if not exist B:\check-bios.cmd goto :bios_check_done
echo. echo.
echo Checking for BIOS updates... echo Checking for BIOS updates...
call B:\BIOS\check-bios.cmd call B:\check-bios.cmd
REM If BIOS was flashed, check-bios.cmd reboots and we never reach here. :bios_check_done
echo.
)
net use B: /delete 2>NUL net use B: /delete 2>NUL
:menu :menu
@@ -71,9 +74,9 @@ REM --- PPKG configuration (constructed at menu time, see docs) ---
REM Vendor ships one source PPKG; we construct the BPRT-tagged filename REM Vendor ships one source PPKG; we construct the BPRT-tagged filename
REM by filling in Office, Region, Expiry, Version on the target copy. REM by filling in Office, Region, Expiry, Version on the target copy.
REM Update SOURCE_PPKG + PPKG_VER when a new PPKG is released. REM Update SOURCE_PPKG + PPKG_VER when a new PPKG is released.
set SOURCE_PPKG=GCCH_Prod_SFLD_v4.11.ppkg set SOURCE_PPKG=GCCH_Prod_SFLD_v4.12.ppkg
set PPKG_VER=v4.11 set PPKG_VER=v4.12
set PPKG_EXP=20270430 set PPKG_EXP=20260831
set REGION=US set REGION=US
set OFFICE= set OFFICE=
@@ -312,6 +315,12 @@ if exist "Y:\shopfloor-setup\Shopfloor" (
xcopy /E /Y /I "Y:\shopfloor-setup\Shopfloor" "W:\Enrollment\shopfloor-setup\Shopfloor\" xcopy /E /Y /I "Y:\shopfloor-setup\Shopfloor" "W:\Enrollment\shopfloor-setup\Shopfloor\"
echo Copied Shopfloor baseline setup files. echo Copied Shopfloor baseline setup files.
) )
REM --- Always copy common/ (cross-PC-type GE-Enforce dispatcher + lib live here post-v2) ---
if exist "Y:\shopfloor-setup\common" (
mkdir W:\Enrollment\shopfloor-setup\common 2>NUL
xcopy /E /Y /I "Y:\shopfloor-setup\common" "W:\Enrollment\shopfloor-setup\common\"
echo Copied common setup files.
)
REM --- Copy type-specific scripts on top of baseline --- REM --- Copy type-specific scripts on top of baseline ---
if exist "Y:\shopfloor-setup\%PCTYPE%" ( if exist "Y:\shopfloor-setup\%PCTYPE%" (
mkdir "W:\Enrollment\shopfloor-setup\%PCTYPE%" 2>NUL mkdir "W:\Enrollment\shopfloor-setup\%PCTYPE%" 2>NUL
@@ -345,8 +354,8 @@ REM Copies the Hexagon installer bundle (~1.9 GB) from the PXE server enrollment
REM share onto the target disk so 09-Setup-CMM.ps1 can install from local disk. REM share onto the target disk so 09-Setup-CMM.ps1 can install from local disk.
REM The tsgwp00525 SFLD share that holds the canonical copy is not yet reachable REM The tsgwp00525 SFLD share that holds the canonical copy is not yet reachable
REM during shopfloor-setup (Azure DSC provisions those creds later), so this REM during shopfloor-setup (Azure DSC provisions those creds later), so this
REM bootstrap exists to get the first-install through. Post-imaging, the logon- REM bootstrap exists to get the first-install through. Post-imaging, the
REM triggered CMM-Enforce.ps1 takes over from the share. REM unified GE-Enforce dispatcher takes over from the share for ongoing updates.
if /i not "%PCTYPE%"=="CMM" goto skip_cmm_stage if /i not "%PCTYPE%"=="CMM" goto skip_cmm_stage
if exist "Y:\installers-post\cmm\cmm-manifest.json" ( if exist "Y:\installers-post\cmm\cmm-manifest.json" (
mkdir W:\CMM-Install 2>NUL mkdir W:\CMM-Install 2>NUL