@echo off echo Please wait while 'WinPE' is being processed. This may take a few seconds. wpeinit powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c REM --- Wait for network (DHCP may take a moment after wpeinit) --- echo Waiting for network... :wait_net ping -n 2 172.16.9.1 >NUL 2>&1 if errorlevel 1 goto wait_net echo Network ready. 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) net use B: \\172.16.9.1\winpeapps_bios /user:pxe-upload pxe /persistent:no 2>NUL if not exist B:\check-bios.cmd goto :bios_check_done echo. echo Checking for BIOS updates... call B:\check-bios.cmd :bios_check_done net use B: /delete 2>NUL :menu cls echo. echo ======================================== echo WinPE Setup Menu echo ======================================== echo Firmware: %BIOS_STATUS% echo. echo Please select an option: echo. echo 1. GEA Standard echo 2. GEA Engineer echo 3. GEA Shopfloor (sub-menu) echo. echo ======================================== echo. set /p choice=Enter your choice (1-3): 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 cls set PCTYPE= REM Data-driven PC-type menu: the PXE webapp maintains menu.json on the REM enrollment share; select-shopfloor-type.ps1 renders it and writes the REM chosen PCTYPE to X:\pctype.txt. Falls back to the baked-in menu below if REM the share or the picker is unavailable (mirrors the CMM bay picker). net use Y: \\172.16.9.1\enrollment /user:pxe-upload pxe /persistent:no >NUL 2>NUL del X:\pctype.txt 2>NUL if exist "Y:\shopfloor-setup\select-shopfloor-type.ps1" powershell.exe -NoProfile -ExecutionPolicy Bypass -File "Y:\shopfloor-setup\select-shopfloor-type.ps1" -MenuJson "Y:\shopfloor-setup\menu.json" -OutFile "X:\pctype.txt" if exist X:\pctype.txt set /p PCTYPE=NUL 2>NUL del X:\cmm-bay.txt 2>NUL set CMMID= set CMMVARIANT=standard if not exist "Y:\installers-post\cmm\select-cmm-bay.ps1" goto cmm_picker_skip powershell.exe -NoProfile -ExecutionPolicy Bypass -File "Y:\installers-post\cmm\select-cmm-bay.ps1" -ConfigPath "Y:\installers-post\cmm\cmm-bay-config.csv" -OutFile "X:\cmm-bay.txt" :cmm_picker_skip if exist X:\cmm-bay.txt set /p CMMID= /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-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 :enroll_menu cls echo. echo ======================================== echo GCCH Enrollment Profile echo ======================================== echo. echo 1. No Office echo 2. Standard Office (x86) echo 3. Standard Office (x64) echo 4. Pro Plus Office (x86) with Access echo 5. Pro Plus Office (x64) with Access echo 6. Skip enrollment echo. set /p enroll=Enter your choice (1-6): REM --- PPKG configuration (constructed at menu time, see docs) --- 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 The ACTIVE ppkg is read from \\172.16.9.1\enrollment\ppkg.conf, which the REM PXE webapp writes on upload - so a new ppkg goes live with no startnet edit. REM The baked-in values below are only a fallback if ppkg.conf is missing. net use Y: \\172.16.9.1\enrollment /user:pxe-upload pxe /persistent:no >nul 2>&1 set SOURCE_PPKG= set TARGET_PPKG= set PPKG_VER= set PPKG_EXP= if exist "Y:\ppkg.conf" ( for /f "usebackq tokens=1,* delims==" %%A in ("Y:\ppkg.conf") do ( if /i "%%A"=="SOURCE_PPKG" set "SOURCE_PPKG=%%B" if /i "%%A"=="PPKG_VER" set "PPKG_VER=%%B" if /i "%%A"=="PPKG_EXP" set "PPKG_EXP=%%B" if /i "%%A"=="TARGET_PPKG" set "TARGET_PPKG=%%B" ) ) if "%SOURCE_PPKG%"=="" set SOURCE_PPKG=GCCH_Prod_SFLD_v4.16.ppkg if "%PPKG_VER%"=="" set PPKG_VER=v4.16 if "%PPKG_EXP%"=="" set PPKG_EXP=20260831 set REGION=US set OFFICE= if "%enroll%"=="1" set OFFICE=NoOffice if "%enroll%"=="2" set OFFICE=StdOffice-x86 if "%enroll%"=="3" set OFFICE=StdOffice-x64 if "%enroll%"=="4" set OFFICE=ProPlusOffice-x86 if "%enroll%"=="5" set OFFICE=ProPlusOffice-x64 if "%enroll%"=="6" set OFFICE= if "%enroll%"=="" goto enroll_menu set PPKG= REM Target filename for the staged package. REM REM The SFLD-era scheme encoded an Office variant in the name REM (GCCH_Prod_SFLD___Exp__.ppkg) because one source REM package was renamed per Office choice. MCL packages do not work that way - REM Office is installed by Install-Office.ps1 inside the package - and renaming REM an MCL package into the SFLD scheme just produces a misleading filename. REM REM So: honour TARGET_PPKG from ppkg.conf when present, otherwise keep the REM source filename unchanged. The SFLD construction remains only as a fallback REM for a legacy SFLD source package. if not "%TARGET_PPKG%"=="" ( set PPKG=%TARGET_PPKG% ) else ( REM find, NOT findstr: findstr.exe is not in this WinPE image (find.exe is, REM and startnet already uses it for the PESetup wait). findstr fails with REM "not recognized", leaves errorlevel 9009, and "if errorlevel 1" then REM takes the NON-SFLD branch for an SFLD package - so the staged ppkg keeps REM its source name and never gets the BPRT-tagged one. echo %SOURCE_PPKG% | find /I "_SFLD_" >NUL if errorlevel 1 ( set PPKG=%SOURCE_PPKG% ) else ( if not "%OFFICE%"=="" set PPKG=GCCH_Prod_SFLD_%OFFICE%_%REGION%_Exp_%PPKG_EXP%_%PPKG_VER%.ppkg ) ) REM --- 2026-05-04 rename reorg: PCTYPE is set by gea_shopfloor_submenu above REM (single string, e.g. gea-shopfloor-collections). pc-subtype.txt is no REM longer written. DISPLAYTYPE IS still written (display-type.txt) when REM PCTYPE=gea-shopfloor-display because Install-KioskApp.cmd needs Lobby REM vs Dashboard to choose installer + Get-PCProfile builds Display-{type} REM profile key. set PCSUBTYPE= REM --- Machine number / asset tag (only PC types that key per-machine config) --- REM collections + nocollections = 4-digit machine number for shopfloor identification. REM waxtrace = asset tag like WJRP2335 used to match per-machine cal ISO during REM 09-Setup-WaxAndTrace.ps1. Cal ISO files are named REM CAL-{asset_tag}_serial-*_probe-*.iso. set MACHINENUM=9999 if /i "%PCTYPE%"=="gea-shopfloor-collections" goto prompt_machinenum if /i "%PCTYPE%"=="gea-shopfloor-nocollections" goto prompt_machinenum if /i "%PCTYPE%"=="gea-shopfloor-heattreat" goto prompt_machinenum if /i "%PCTYPE%"=="gea-shopfloor-waxtrace" goto prompt_waxtrace_asset if /i "%PCTYPE%"=="gea-shopfloor-cmm" goto skip_machinenum goto skip_machinenum :prompt_machinenum echo. set /p MACHINENUM=Enter machine number (digits, or Enter for 9999): if "%MACHINENUM%"=="" set MACHINENUM=9999 echo Machine number: %MACHINENUM% goto skip_machinenum :prompt_waxtrace_asset echo. echo Loading Wax/Trace bay list from PXE share... REM Mount enrollment share early so the picker can read INDEX.csv. Later REM net use Y: will be a no-op if Y: is already mapped. net use Y: \\172.16.9.1\enrollment /user:pxe-upload pxe /persistent:no >NUL 2>NUL del X:\waxtrace-asset.txt 2>NUL if not exist "Y:\installers-post\waxtrace\select-waxtrace-asset.ps1" goto waxtrace_picker_skip powershell.exe -NoProfile -ExecutionPolicy Bypass -File "Y:\installers-post\waxtrace\select-waxtrace-asset.ps1" -IndexPath "Y:\installers-post\waxtrace\bay-config.csv" -OutFile "X:\waxtrace-asset.txt" :waxtrace_picker_skip set MACHINENUM= if exist X:\waxtrace-asset.txt set /p MACHINENUM= X:\Boot.tag if "%choice%"=="1" goto gea-standard if "%choice%"=="2" goto gea-engineer if "%choice%"=="3" goto gea-shopfloor echo Invalid choice. Please try again. pause goto menu :gea-standard echo. echo Starting GEA Standard setup... start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe for /l %%i in (1,1,2000000) do rem net use Z: \\172.16.9.1\winpeapps\gea-standard /user:pxe-upload pxe /persistent:no goto end :gea-engineer echo. echo Starting GEA Engineer setup... start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe for /l %%i in (1,1,2000000) do rem net use Z: \\172.16.9.1\winpeapps\gea-engineer /user:pxe-upload pxe /persistent:no goto end :gea-shopfloor echo. echo Starting GEA Shopfloor setup... start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe for /l %%i in (1,1,2000000) do rem REM --- Per-PCTYPE media view ----------------------------------------------- REM PESetup's CopyPackages copies the WHOLE of \Deploy\Applications to REM the target, recursively, with no filtering and no manifest (see REM docs/PESETUP-INTERNALS.md). One shared shopfloor media therefore hands REM every shopfloor payload to every bay whatever its type. REM _media\ is that same media rebuilt out of symlinks by REM scripts/build-pctype-media.py, carrying only this type's payload. It is REM built once, server-side, and never mutated per session: bays image REM concurrently and CopyPackages is fail-fast, so changing a path mid-copy REM would fail some OTHER bay's imaging. if "%PCTYPE%"=="" goto gea_shopfloor_media_shared REM Free Z: first. A leftover mapping makes net use fail with "device already REM in use" and the probe below would then pass against the OLD media. net use Z: /delete /y >NUL 2>&1 net use Z: \\172.16.9.1\winpeapps\_media\%PCTYPE% /user:pxe-upload pxe /persistent:no >NUL 2>&1 REM Probe a file that only resolves when the view exists AND its symlinks are REM intact. A dangling view must not image silently off a half-mapped drive. if exist "Z:\Deploy\Control\HardwareDriver.json" goto end echo. echo WARNING: no usable per-type media at _media\%PCTYPE%. echo Falling back to the shared shopfloor media, so EVERY shopfloor echo payload will be staged on this bay, not just this type's. echo Imaging is otherwise unaffected. Tell the PXE admin. echo. net use Z: /delete /y >NUL 2>&1 :gea_shopfloor_media_shared net use Z: \\172.16.9.1\winpeapps\gea-shopfloor /user:pxe-upload pxe /persistent:no goto end :end echo. REM --- Push initial "WinPE staging" status to PXE webapp --- REM Externalized to Y:\scripts\winpe-status-push.ps1 so future edits don't REM require a boot.wim rebuild. Best-effort; the script logs to REM X:\Windows\Temp\winpe-status-push.log and swallows all errors. if exist "Y:\scripts\winpe-status-push.ps1" ( powershell -NoProfile -ExecutionPolicy Bypass -File "Y:\scripts\winpe-status-push.ps1" ) REM --- Pre-imaging checks --------------------------------------------------- REM Checks the things PESetup fails on, before it fails on them: secure boot REM (hard failure at GatherData), disk >= 120 GB, a driver pack matching THIS REM model, and media age. The driver check is the one that earns its keep - a REM miss is only a WARNING to PESetup, so the bay images with no NIC and the REM symptom shows up much later as a DNS failure during enrollment. REM Advisory: it reports and pauses on a blocker, it does not abort. The tech REM decides. Lives on the share so it can be fixed without a boot.wim rebuild. if exist "Y:\scripts\preflight.ps1" ( powershell -NoProfile -ExecutionPolicy Bypass -File "Y:\scripts\preflight.ps1" -MediaDrive Z: if errorlevel 1 ( echo Press any key to image anyway, or power off the bay to stop. pause >NUL ) ) echo Waiting for PESetup.exe to start... :wait_start ping -n 3 127.0.0.1 >NUL wmic process where "name='PESetup.exe'" get name 2>NUL | find /I "PESetup" >NUL 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 --- 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 REM preparation. See docs/PESETUP-INTERNALS.md. There is nothing to search for. REM REM Do NOT try to locate the volume earlier by scanning for a Windows install. REM Proven on 579C144, 2026-08-06, PESetup 4.0.0.20: REM 11:30:07 PESetup logs the OLD install still mounted at C: REM 11:30:18 operator presses Next REM 11:30:20 PESetup runs DISKPART /S X:\diskpartEFI0.txt - the wipe REM 11:30:24 a volume scan had returned that C:, startnet assigned it W: and REM staged into the partition PESetup was mid-way through erasing. REM Every robocopy died, "The system cannot find the path REM specified", and NOTHING was staged. REM 11:30:30 PESetup creates its own W: and starts copying to it REM The SYSTEM hive is the signal that distinguishes the applied image from the REM one being wiped: it exists only once the WIM apply has written it. Do not REM weaken this test to "the volume exists" or "Windows is present". set OSDRIVE= echo Waiting for the applied Windows volume at W: ... set OSWAIT=0 :wait_enroll if exist W:\Windows\System32\config\system ( set OSDRIVE=W: goto found_os ) set /a OSWAIT+=1 REM 270 polls at ~10s = 45 minutes. Was 120 (20 min), which is fine on NVMe but REM tight on slow media - a WIM apply can run 25+ minutes there, and the failure REM mode is the scary os_not_found banner plus nothing staged. Waiting longer REM costs nothing: the loop exits the moment the SYSTEM hive appears. if %OSWAIT% GEQ 270 goto os_not_found ping -n 11 127.0.0.1 >NUL goto wait_enroll :os_not_found echo. echo ****************************************************************** echo ERROR: no applied Windows volume found. echo NOTHING was staged: pc-type.txt, the enrollment package and echo shopfloor-setup are all missing. Imaging will finish but the echo post-install setup will NOT run. Tell the PXE admin. echo ****************************************************************** echo. goto cleanup_enroll :found_os echo Found Windows at %OSDRIVE% REM No drive-letter reassignment here. PESetup owns W: and runs its own DISKPART REM against the same disk; a second diskpart racing it destroys the volume that REM staging is writing to. The unattend's hardcoded W:\Drivers path is PESetup's REM own destination (CopyDrivers unzips there), so it lines up by construction. mkdir %OSDRIVE%\Enrollment 2>NUL REM Log all copy operations to a file that persists on the target disk. REM Tech can check C:\Enrollment\winpe-staging.log post-imaging. REM Each echo in the copy block below also appends to this file via 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% 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" ( robocopy "Y:\config" "%OSDRIVE%\Enrollment" "site-config.json" /R:1 /W:1 /NFL /NDL /NJH /NJS if errorlevel 8 ( echo WARNING: site-config.json robocopy FAILED - not staged. echo [%TIME%] FAILED: site-config.json robocopy >> "%STAGELOG%" ) else ( echo Copied site-config.json. echo [%TIME%] Copied site-config.json >> "%STAGELOG%" ) ) else ( echo WARNING: site-config.json not found on enrollment share. echo [%TIME%] WARNING: site-config.json not found >> "%STAGELOG%" ) REM --- Copy PPKG if selected (renames from SOURCE to BPRT-tagged filename) --- if "%PPKG%"=="" goto copy_pctype robocopy "Y:\ppkgs" "%OSDRIVE%\Enrollment" "%SOURCE_PPKG%" /R:1 /W:1 /NFL /NDL /NJH /NJS REM robocopy exit codes: 0-7 = success, 8+ = failure (inverse of copy's 0/1). if errorlevel 8 ( echo WARNING: Failed to copy enrollment package. echo [%TIME%] WARNING: Failed to copy PPKG %SOURCE_PPKG% >> "%STAGELOG%" goto copy_pctype ) REM robocopy keeps the source name; rename to the BPRT-tagged target name. if not "%SOURCE_PPKG%"=="%PPKG%" ren "%OSDRIVE%\Enrollment\%SOURCE_PPKG%" "%PPKG%" echo [%TIME%] Copied PPKG %SOURCE_PPKG% as %PPKG% >> "%STAGELOG%" robocopy "Y:\scripts" "%OSDRIVE%\Enrollment" "run-enrollment.ps1" "wait-for-internet.ps1" "migrate-to-wifi.ps1" /R:1 /W:1 /NFL /NDL /NJH /NJS echo [%TIME%] Copied enrollment scripts >> "%STAGELOG%" REM --- Create enroll.cmd at drive root as manual fallback --- > %OSDRIVE%\enroll.cmd ( echo @echo off echo echo Waiting for network... echo :waitnet echo ping -n 2 8.8.8.8 ^>NUL 2^>^&1 echo if errorlevel 1 goto waitnet echo echo Network connected. Running enrollment... echo powershell.exe -ExecutionPolicy Bypass -File "C:\Enrollment\run-enrollment.ps1" ) echo Manual fallback created at %OSDRIVE%\enroll.cmd :copy_pctype REM --- Copy shopfloor PC type setup scripts --- if "%PCTYPE%"=="" goto cleanup_enroll echo %PCTYPE%> %OSDRIVE%\Enrollment\pc-type.txt REM 2026-05-04 rename reorg: pc-subtype.txt no longer written. REM display-type.txt IS still written for gea-shopfloor-display because REM Install-KioskApp.cmd reads it to pick Lobby vs Dashboard installer REM and Get-PCProfile.ps1 reads it to build the Display-{type} profile key. if not "%DISPLAYTYPE%"=="" echo %DISPLAYTYPE%> %OSDRIVE%\Enrollment\display-type.txt if not "%MACHINENUM%"=="" echo %MACHINENUM%> %OSDRIVE%\Enrollment\machine-number.txt REM Keyence model goes to BOTH keyence-model.txt (read by 09-Setup-Keyence) AND REM pc-subtype.txt (read by GE-Enforce for per-model dispatch via existing REM PCSubType wiring: looks for gea-shopfloor-keyence-\manifest.json on share). if not "%KEYENCEMODEL%"=="" ( echo %KEYENCEMODEL%> %OSDRIVE%\Enrollment\keyence-model.txt echo %KEYENCEMODEL%> %OSDRIVE%\Enrollment\pc-subtype.txt ) REM CMM bay ID + resolved config. CMMID comes from the bay picker (e.g. CMM4). REM Written to machine-number.txt (same field collections/waxtrace use) so REM TargetMachineNumbers on the SFLD share manifest gates per-bay entries. REM resolve-cmm-bay-config.ps1 reads the CSV and writes version.txt + doda.txt REM to %OSDRIVE%\Enrollment\cmm\ for 09-Setup-CMM.ps1 to consume. if "%CMMID%"=="" goto cmm_id_done echo %CMMID%> %OSDRIVE%\Enrollment\machine-number.txt mkdir %OSDRIVE%\Enrollment\cmm 2>NUL if exist "Y:\installers-post\cmm\resolve-cmm-bay-config.ps1" ( powershell.exe -NoProfile -ExecutionPolicy Bypass -File "Y:\installers-post\cmm\resolve-cmm-bay-config.ps1" -ConfigPath "Y:\installers-post\cmm\cmm-bay-config.csv" -CmmId "%CMMID%" -OutDir "%OSDRIVE%\Enrollment\cmm" ) REM Read doda flag to set pc-subtype.txt for GE-Enforce manifest gating. REM goto-flow avoids CMD variable scoping issues inside parens blocks. if not exist %OSDRIVE%\Enrollment\cmm\doda.txt goto cmm_id_done set /p CMMDODA=<%OSDRIVE%\Enrollment\cmm\doda.txt if /i "%CMMDODA%"=="yes" echo doda> %OSDRIVE%\Enrollment\pc-subtype.txt :cmm_id_done robocopy "Y:\shopfloor-setup" "%OSDRIVE%\Enrollment" "Run-ShopfloorSetup.ps1" /R:1 /W:1 /NFL /NDL /NJH /NJS REM Post-boot bulk re-fetch. WinPE staging below is best-effort (it can fail if REM the Y: mount went idle-dead during the WIM apply); Fetch-StagingPayload.ps1 REM re-pulls the shopfloor-setup tree + preinstall bundle at first logon on a REM FRESH mount. The unattend FirstLogonCommands runs it before the PowerShell 7 REM MSI + Run-ShopfloorSetup. Stage the script + its source coords here. robocopy "Y:\shopfloor-setup" "%OSDRIVE%\Enrollment" "Fetch-StagingPayload.ps1" /R:1 /W:1 /NFL /NDL /NJH /NJS REM Stage the staging self-heal directly too, so the unattend Order 5 heal step REM runs even if Fetch-StagingPayload itself did not land. Small, reliable copy. robocopy "Y:\shopfloor-setup" "%OSDRIVE%\Enrollment" "Verify-And-Heal-Staging.ps1" /R:1 /W:1 /NFL /NDL /NJH /NJS > %OSDRIVE%\Enrollment\fetch-source.txt ( echo \\172.16.9.1\enrollment echo pxe-upload echo pxe ) REM --- Always copy Shopfloor baseline scripts --- mkdir %OSDRIVE%\Enrollment\shopfloor-setup 2>NUL robocopy "Y:\shopfloor-setup" "%OSDRIVE%\Enrollment\shopfloor-setup" "backup_lockdown.bat" /R:1 /W:1 /NFL /NDL /NJH /NJS if exist "Y:\shopfloor-setup\Shopfloor" ( mkdir %OSDRIVE%\Enrollment\shopfloor-setup\Shopfloor 2>NUL robocopy "Y:\shopfloor-setup\Shopfloor" "%OSDRIVE%\Enrollment\shopfloor-setup\Shopfloor" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" REM Report what actually happened. These echoes used to run unconditionally REM and printed "Copied ..." for six copies that moved zero bytes. REM %ERRORLEVEL% is not printed: inside a parenthesised block it expands at REM PARSE time, so it shows a stale value from before the robocopy ran. if errorlevel 8 ( echo WARNING: shopfloor-setup\Shopfloor robocopy FAILED - nothing copied. echo [%TIME%] FAILED: Shopfloor baseline robocopy >> "%STAGELOG%" ) else ( echo Copied Shopfloor baseline setup files. echo [%TIME%] Copied Shopfloor baseline >> "%STAGELOG%" ) ) REM --- Always copy common/ (cross-PC-type GE-Enforce dispatcher + lib live here post-v2) --- if exist "Y:\shopfloor-setup\common" ( mkdir %OSDRIVE%\Enrollment\shopfloor-setup\common 2>NUL robocopy "Y:\shopfloor-setup\common" "%OSDRIVE%\Enrollment\shopfloor-setup\common" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: shopfloor-setup\common robocopy FAILED - nothing copied. echo [%TIME%] FAILED: common robocopy >> "%STAGELOG%" ) else ( echo Copied common setup files. echo [%TIME%] Copied common >> "%STAGELOG%" ) ) REM --- Copy _ntlars-backups (147 per-bay .reg files restored by gea-shopfloor-{collections,nocollections}\03-RestoreEDncConfig.ps1) --- REM Same root level as common/, referenced by 03-RestoreEDncConfig.ps1 via Join-Path $PSScriptRoot '..\_ntlars-backups'. if exist "Y:\shopfloor-setup\_ntlars-backups" ( mkdir %OSDRIVE%\Enrollment\shopfloor-setup\_ntlars-backups 2>NUL robocopy "Y:\shopfloor-setup\_ntlars-backups" "%OSDRIVE%\Enrollment\shopfloor-setup\_ntlars-backups" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: _ntlars-backups robocopy FAILED - nothing copied. echo [%TIME%] FAILED: _ntlars-backups robocopy >> "%STAGELOG%" ) else ( echo Copied _ntlars-backups. echo [%TIME%] Copied _ntlars-backups >> "%STAGELOG%" ) ) REM --- Copy type-specific scripts on top of baseline --- if exist "Y:\shopfloor-setup\%PCTYPE%" ( mkdir "%OSDRIVE%\Enrollment\shopfloor-setup\%PCTYPE%" 2>NUL robocopy "Y:\shopfloor-setup\%PCTYPE%" "%OSDRIVE%\Enrollment\shopfloor-setup\%PCTYPE%" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: shopfloor-setup\%PCTYPE% robocopy FAILED - nothing copied. echo [%TIME%] FAILED: %PCTYPE% type-specific robocopy >> "%STAGELOG%" ) else ( echo Copied %PCTYPE% setup files. echo [%TIME%] Copied %PCTYPE% type-specific >> "%STAGELOG%" ) ) else ( echo WARNING: No setup files found for PC type %PCTYPE%. echo [%TIME%] WARNING: No setup files for %PCTYPE% >> "%STAGELOG%" ) REM --- Stage preinstall bundle (apps installed locally to save Azure bandwidth) --- if exist "Y:\pre-install\preinstall.json" ( mkdir %OSDRIVE%\PreInstall 2>NUL mkdir %OSDRIVE%\PreInstall\installers 2>NUL robocopy "Y:\pre-install" "%OSDRIVE%\PreInstall" "preinstall.json" /R:1 /W:1 /NFL /NDL /NJH /NJS if exist "Y:\pre-install\installers" ( robocopy "Y:\pre-install\installers" "%OSDRIVE%\PreInstall\installers" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: pre-install\installers robocopy FAILED - nothing staged. ) else ( echo Staged preinstall bundle to %OSDRIVE%\PreInstall. ) echo [%TIME%] Staged preinstall bundle >> "%STAGELOG%" ) else ( echo WARNING: Y:\pre-install\installers not found - preinstall.json staged without installers. ) if exist "Y:\pre-install\udc-backups" ( robocopy "Y:\pre-install\udc-backups" "%OSDRIVE%\PreInstall\udc-backups" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: pre-install\udc-backups robocopy FAILED - nothing staged. ) else ( echo Staged UDC settings backups to %OSDRIVE%\PreInstall\udc-backups. ) echo [%TIME%] Staged UDC backups >> "%STAGELOG%" ) ) else ( echo No preinstall bundle on PXE server - skipping. ) REM --- Stage CMM bootstrap bundle (CMM-type PCs only) --- 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 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 bootstrap exists to get the first-install through. Post-imaging, the REM unified GE-Enforce dispatcher takes over from the share for ongoing updates. if /i not "%PCTYPE%"=="gea-shopfloor-cmm" goto skip_cmm_stage if exist "Y:\installers-post\cmm\cmm-manifest.json" ( mkdir %OSDRIVE%\CMM-Install 2>NUL REM /XD the backups tree: it holds EVERY bay's settings backup (some 240 MB REM each). Copying all of it to every imaged CMM would waste GBs. The bulk REM copy excludes it; the selected bay's backup is staged separately below. robocopy "Y:\installers-post\cmm" "%OSDRIVE%\CMM-Install" /E /XD "Y:\installers-post\cmm\backups" /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: cmm robocopy FAILED - CMM bootstrap NOT staged. ) else ( echo Staged CMM bootstrap to %OSDRIVE%\CMM-Install. ) echo [%TIME%] Staged CMM bootstrap >> "%STAGELOG%" ) else ( echo WARNING: Y:\cmm-installers not found - CMM PC cannot install Hexagon apps at imaging time. ) REM Stage ONLY the selected bay's settings backup. 09-Setup-CMM's Restore-CMM REM reads %OSDRIVE%\CMM-Install\backups\%CMMID%\ to restore PC-DMIS + goCMM settings. if not defined CMMID goto skip_cmm_stage if exist "Y:\installers-post\cmm\backups\%CMMID%" ( robocopy "Y:\installers-post\cmm\backups\%CMMID%" "%OSDRIVE%\CMM-Install\backups\%CMMID%" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: %CMMID% backup robocopy FAILED - settings will NOT restore. echo [%TIME%] FAILED: %CMMID% backup robocopy >> "%STAGELOG%" ) else ( echo Staged %CMMID% settings backup for restore. echo [%TIME%] Staged %CMMID% backup >> "%STAGELOG%" ) ) :skip_cmm_stage REM --- Stage Keyence per-model bootstrap bundle (Keyence PCs only) --- REM Copies only the selected model's MSI + Data cabs from the PXE enrollment REM share to the target disk. installers-post/keyence// contains: REM manifest.json REM installers/{MSI, Data1.cab, [Data11.cab for vr3000], 1033.mst} REM drivers/ (vr6000 only - older external .inf; vr3000/vr5000 drivers REM are embedded in their MSIs) REM 09-Setup-Keyence.ps1 reads C:\Enrollment\keyence-model.txt to know which. if /i not "%PCTYPE%"=="gea-shopfloor-keyence" goto skip_keyence_stage if "%KEYENCEMODEL%"=="" goto skip_keyence_stage if exist "Y:\installers-post\keyence\%KEYENCEMODEL%\manifest.json" ( mkdir %OSDRIVE%\KeyenceInstall 2>NUL mkdir %OSDRIVE%\KeyenceInstall\%KEYENCEMODEL% 2>NUL robocopy "Y:\installers-post\keyence\%KEYENCEMODEL%" "%OSDRIVE%\KeyenceInstall\%KEYENCEMODEL%" /E /MT:16 /R:1 /W:1 /NFL /NDL /LOG+:"%STAGELOG%" if errorlevel 8 ( echo WARNING: keyence\%KEYENCEMODEL% robocopy FAILED - nothing staged. ) else ( echo Staged Keyence %KEYENCEMODEL% bootstrap to %OSDRIVE%\KeyenceInstall\%KEYENCEMODEL%\. ) echo [%TIME%] Staged Keyence %KEYENCEMODEL% >> "%STAGELOG%" ) else ( echo WARNING: Y:\installers-post\keyence\%KEYENCEMODEL% not found - Keyence %KEYENCEMODEL% cannot install at imaging time. ) :skip_keyence_stage REM --- Stage WaxTrace bootstrap bundle (wax/trace gea-shopfloor-waxtrace only) --- REM Three-step process: REM 1. robocopy installers-post\waxtrace -> %OSDRIVE%\WaxTrace-Install (everything REM EXCEPT the formtracepak\ subdir, via /XD - that subdir carries all REM 7 vendor ISOs and we only need one of them) REM 2. resolve-bay-config.ps1: reads bay-config.csv + asset_tag, writes REM %OSDRIVE%\Enrollment\waxtrace\{version,model,userid}.txt for 09-Setup to read REM 3. robocopy ONLY the bay's matching FORMTRACEPAK-V.iso from REM Y:\installers-post\waxtrace\formtracepak\ onto the target disk REM (so 09-Setup-WaxAndTrace.ps1 mounts the right version per bay) if /i not "%PCTYPE%"=="gea-shopfloor-waxtrace" goto skip_waxtrace_stage if not exist "Y:\installers-post\waxtrace\waxtrace-manifest.json" goto skip_waxtrace_missing mkdir %OSDRIVE%\WaxTrace-Install 2>NUL REM robocopy /XD excludes the formtracepak\ dir up front so we don't waste REM disk + time copying 12+ GB of vendor ISOs only to delete them. The REM matched FTPak ISO gets cherry-picked below into the same target dir. REM /NFL/NDL keep the listing manageable; output left visible (no >NUL) so REM any failure is debuggable from the install log. robocopy exit codes REM 0-7 are "OK" (per Microsoft); 8+ is real failure. robocopy "Y:\installers-post\waxtrace" "%OSDRIVE%\WaxTrace-Install" /E /XD formtracepak /MT:16 /R:1 /W:1 /NFL /NDL REM Both tests read the robocopy result, so they must sit before any command REM that sets its own errorlevel - mkdir does, which is why it moved below. if errorlevel 8 echo WARNING: WaxTrace bootstrap robocopy FAILED - nothing staged. if not errorlevel 8 echo Staged WaxTrace bootstrap minus formtracepak\ to %OSDRIVE%\WaxTrace-Install. mkdir %OSDRIVE%\WaxTrace-Install\formtracepak 2>NUL REM Resolve bay-config: writes %OSDRIVE%\Enrollment\waxtrace\{version,model,userid}.txt mkdir %OSDRIVE%\Enrollment\waxtrace 2>NUL if not "%MACHINENUM%"=="" ( powershell.exe -NoProfile -ExecutionPolicy Bypass -File "Y:\installers-post\waxtrace\resolve-bay-config.ps1" -Asset "%MACHINENUM%" -OutDir "%OSDRIVE%\Enrollment\waxtrace" ) else ( echo WARNING: no MACHINENUM set - skipping bay-config resolve. 09-Setup-WaxAndTrace will abort cleanly with no version. ) REM Read the resolved version + robocopy ONLY that FTPak ISO (avoids dumping REM 12 GB of ISOs on the bay disk; only the matched ~2 GB ISO lands locally). set WTVER= if exist %OSDRIVE%\Enrollment\waxtrace\version.txt set /p WTVER=<%OSDRIVE%\Enrollment\waxtrace\version.txt if not "%WTVER%"=="" ( if exist "Y:\installers-post\waxtrace\formtracepak\FORMTRACEPAK-V%WTVER%.iso" ( REM robocopy /J = unbuffered I/O, meaningfully faster than xcopy on REM 2 GB FTPak ISOs over SMB. Single-file syntax: source dir, dest REM dir, file pattern. robocopy "Y:\installers-post\waxtrace\formtracepak" "%OSDRIVE%\WaxTrace-Install\formtracepak" "FORMTRACEPAK-V%WTVER%.iso" /J /R:1 /W:1 /NFL /NDL echo Staged FormTracePak V%WTVER%.iso for %MACHINENUM%. ) else ( echo WARNING: No FORMTRACEPAK-V%WTVER%.iso on share for %MACHINENUM% - 09-Setup-WaxAndTrace will abort cleanly with the version-mismatch error. ) ) else ( echo WARNING: bay-config did not resolve a FTPak version for %MACHINENUM%. ) REM Per-asset backup ZIPs land at %OSDRIVE%\WaxTrace-Install\backups\.zip REM as part of the robocopy bundle above (backups\ is not in the /XD list). REM 09-Setup-WaxAndTrace.ps1 Step 3b reads C:\WaxTrace-Install\backups\ REM .zip post-vendor-MSI and runs Install-FormtracepakSettings.ps1 REM -RestoreData -RestoreConfig. Total ~17 MB on disk for the whole set, no REM cherry-pick needed. if not "%MACHINENUM%"=="" ( if exist "%OSDRIVE%\WaxTrace-Install\backups\%MACHINENUM%.zip" ( echo Backup ZIP %MACHINENUM%.zip is in the bundle - 09-Setup Step 3b will restore. ) else ( echo INFO: No backup ZIP for %MACHINENUM% in %OSDRIVE%\WaxTrace-Install\backups - 09-Setup will skip the restore step. ) ) goto skip_waxtrace_stage :skip_waxtrace_missing echo WARNING: Y:\installers-post\waxtrace not found - WaxTrace PC cannot install FormTracePak at imaging time. :skip_waxtrace_stage :pctype_done if defined STAGELOG echo [%TIME%] WinPE staging complete >> "%STAGELOG%" REM --- Verify what we just staged ------------------------------------------ REM Check the payload BEFORE anything depends on it, while a tech is still REM standing at the machine. A short copy found here costs 30 seconds; the same REM copy found at first logon costs 20 minutes, and found never costs a rebuild. REM Four Display bays sat at "imaging stage 2, green" for weeks because nothing REM checked. Retry once for the small trees; the ppkg is 8 GB so report it REM rather than blindly re-pulling it. if "%PCTYPE%"=="" goto verify_done set STAGEBAD= if not exist "%OSDRIVE%\Enrollment\pc-type.txt" set STAGEBAD=%STAGEBAD% pc-type.txt if not exist "%OSDRIVE%\Enrollment\Run-ShopfloorSetup.ps1" set STAGEBAD=%STAGEBAD% Run-ShopfloorSetup.ps1 if not exist "%OSDRIVE%\Enrollment\shopfloor-setup\common" set STAGEBAD=%STAGEBAD% shopfloor-setup\common if not exist "%OSDRIVE%\Enrollment\shopfloor-setup\%PCTYPE%" set STAGEBAD=%STAGEBAD% shopfloor-setup\%PCTYPE% if "%STAGEBAD%"=="" goto verify_ppkg echo. echo Staging verify: missing%STAGEBAD% - retrying once... echo [%TIME%] VERIFY: missing%STAGEBAD% - retry >> "%STAGELOG%" robocopy "Y:\shopfloor-setup" "%OSDRIVE%\Enrollment" "Run-ShopfloorSetup.ps1" /R:2 /W:2 /NFL /NDL /NJH /NJS robocopy "Y:\shopfloor-setup\common" "%OSDRIVE%\Enrollment\shopfloor-setup\common" /E /MT:16 /R:2 /W:2 /NFL /NDL /NJH /NJS robocopy "Y:\shopfloor-setup\%PCTYPE%" "%OSDRIVE%\Enrollment\shopfloor-setup\%PCTYPE%" /E /MT:16 /R:2 /W:2 /NFL /NDL /NJH /NJS if not exist "%OSDRIVE%\Enrollment\pc-type.txt" echo %PCTYPE%> %OSDRIVE%\Enrollment\pc-type.txt set STAGEBAD= if not exist "%OSDRIVE%\Enrollment\pc-type.txt" set STAGEBAD=%STAGEBAD% pc-type.txt if not exist "%OSDRIVE%\Enrollment\Run-ShopfloorSetup.ps1" set STAGEBAD=%STAGEBAD% Run-ShopfloorSetup.ps1 if not exist "%OSDRIVE%\Enrollment\shopfloor-setup\common" set STAGEBAD=%STAGEBAD% shopfloor-setup\common if not exist "%OSDRIVE%\Enrollment\shopfloor-setup\%PCTYPE%" set STAGEBAD=%STAGEBAD% shopfloor-setup\%PCTYPE% if "%STAGEBAD%"=="" ( echo Staging verify: retry fixed it. echo [%TIME%] VERIFY: retry succeeded >> "%STAGELOG%" goto verify_ppkg ) echo. echo ****************************************************************** echo STAGING INCOMPLETE after retry:%STAGEBAD% echo This bay will image but the post-install setup will NOT run echo properly. Fix the share and re-image. Tell the PXE admin. echo ****************************************************************** echo. echo [%TIME%] VERIFY FAILED after retry:%STAGEBAD% >> "%STAGELOG%" :verify_ppkg REM The enrollment package is the expensive one - 8 GB. Report, do not re-pull. if "%PPKG%"=="" goto verify_done if exist "%OSDRIVE%\Enrollment\%PPKG%" ( echo Staging verify: enrollment package present. echo [%TIME%] VERIFY: %PPKG% present >> "%STAGELOG%" ) else ( echo. echo WARNING: enrollment package %PPKG% is MISSING from %OSDRIVE%\Enrollment. echo Enrollment will not run. Re-image this bay. echo. echo [%TIME%] VERIFY FAILED: %PPKG% missing >> "%STAGELOG%" ) :verify_done REM --- BIOS update sub-stage push (fires AFTER %OSDRIVE% copies complete) --- REM check-bios.cmd drops X:\bios-fired.flag iff it actually flashed or REM staged a firmware update. Reading the flag file is more reliable than REM scanning BIOS_STATUS for substrings (caret escaping inside quoted SET REM is subtle in WinPE cmd.exe). Push must happen before Y: cleanup since REM the push script lives on the share. if exist X:\bios-fired.flag ( if exist "Y:\scripts\winpe-status-push.ps1" ( powershell -NoProfile -ExecutionPolicy Bypass -File "Y:\scripts\winpe-status-push.ps1" -CurrentStage "WinPE: BIOS firmware update" ) ) :cleanup_enroll net use Y: /delete 2>NUL :wait_finish ping -n 11 127.0.0.1 >NUL wmic process where "name='PESetup.exe'" get name 2>NUL | find /I "PESetup" >NUL if not errorlevel 1 goto wait_finish REM --- Harvest the imaging logs to the share -------------------------------- REM Every diagnosis of a failed build so far has meant walking to the bay and REM copying files off by hand. PESetup preserves its log and the generated REM unattend, DISM writes its apply logs, and our staging log sits on the target REM - all of it is thrown away at reboot unless collected now. Runs after REM PESetup exits so the logs are final, which means re-mapping Y: (cleanup REM already dropped it). REM Best-effort throughout: a bay must never fail to reboot because a log copy REM did not work. set SERIAL= for /f "skip=1 tokens=*" %%S in ('wmic bios get serialnumber 2^>NUL') do if not defined SERIAL set SERIAL=%%S for /f "tokens=*" %%a in ("%SERIAL%") do set SERIAL=%%a if "%SERIAL%"=="" set SERIAL=unknown-serial net use Y: \\172.16.9.1\enrollment /user:pxe-upload pxe /persistent:no >NUL 2>&1 if not exist "Y:\" goto harvest_done set LOGDEST=Y:\imaging-logs\%SERIAL% mkdir "Y:\imaging-logs" 2>NUL mkdir "%LOGDEST%" 2>NUL echo Collecting imaging logs to %LOGDEST% ... REM WinPE side: DISM apply logs, the unattend PESetup generated, BIOS flash log. robocopy X:\ "%LOGDEST%" "*.log" "Unattend.xml" /R:1 /W:1 /NFL /NDL /NJH /NJS >NUL 2>&1 REM Target side: PESetup's own log lands in Panther, and our staging log. if defined OSDRIVE ( robocopy "%OSDRIVE%\Windows\Panther" "%LOGDEST%" "PESetup*.log" /R:1 /W:1 /NFL /NDL /NJH /NJS >NUL 2>&1 robocopy "%OSDRIVE%\Enrollment" "%LOGDEST%" "winpe-staging.log" /R:1 /W:1 /NFL /NDL /NJH /NJS >NUL 2>&1 ) REM A breadcrumb of what this build actually was, so the logs are readable REM without cross-referencing the webapp. > "%LOGDEST%\build-context.txt" ( echo Serial=%SERIAL% echo PCTYPE=%PCTYPE% echo PPKG=%PPKG% echo MACHINENUM=%MACHINENUM% echo CMMID=%CMMID% echo KEYENCEMODEL=%KEYENCEMODEL% echo DISPLAYTYPE=%DISPLAYTYPE% echo OSDRIVE=%OSDRIVE% echo Media=%MEDIAPATH% echo Date=%DATE% %TIME% ) echo Imaging logs collected. net use Y: /delete /y >NUL 2>&1 :harvest_done echo. echo Imaging complete. Rebooting in 15 seconds... echo Press Ctrl+C to cancel. ping -n 16 127.0.0.1 >NUL wpeutil reboot