18 Commits

Author SHA1 Message Date
cproudlock
b240cfea12 collections: add eDNC detection diagnostic scripts 2026-07-23 09:21:49 -04:00
cproudlock
e7be0f5391 gea-engineer: pre-stage .NET 3.5 + VC++ redists via FlatUnattend
Add RunSynchronous Orders 39-49: enable NetFx3 from staged sxs + install the VC++ 2005-2022 redistributables at specialize, before the first-logon engineering installer. Each command is if-exist guarded against the extra_engineering\prereqs payload, so gea-standard (which shares this template) skips them. Payloads staged on the share under gea-engineer Deploy\Applications\extra_engineering\prereqs.
2026-07-23 09:21:49 -04:00
cproudlock
0c4c2a245d Remove legacy ge-* image types (gea-only fleet)
Drop ge-standard/ge-engineer/ge-shopfloor-lockdown/ge-shopfloor-mce from image_types + standard_types, both startnet boot menus (choices + labels + net use), and the webapp IMAGE_TYPES/FRIENDLY_NAMES. Fleet is gea-* only; the empty ge-* winpeapps stub dirs were removed on the live share.
2026-07-23 09:21:49 -04:00
cproudlock
fdf63ab32f Shopfloor self-heal: bootstrap recovery scripts + Keyence/WaxTrace heal
Fixes Keyence (and CMM/WaxTrace) imaging where the target got only partial
data: the Y: SMB mount goes idle-dead during WIM apply, so WinPE staging dies
early (often just after site-config.json + ppkg). The first-logon self-heal
was meant to recover but its scripts were themselves staged past the death
point, so nothing ran.

- FlatUnattendW10-shopfloor.xml: new FirstLogonCommands Order-4 bootstrap that
  mounts the enrollment share fresh and pulls Fetch-StagingPayload /
  Verify-And-Heal-Staging / enrollment scripts into C:\Enrollment before the
  Order 5/6 heal runs. Recovery no longer depends on WinPE staging surviving.
  Keep CommandLine <=320 and Description <=252 chars: exceeding the unattend
  schema length limits makes the whole oobeSystem pass invalid (OOBE prompts,
  no autologon).

- Verify-And-Heal-Staging.ps1: add Keyence and WaxTrace heavy-payload heal
  branches (previously only CMM). Keyence re-pulls installers-post\keyence\
  <model> -> C:\KeyenceInstall\<model>; WaxTrace re-pulls the bundle (minus
  formtracepak) plus the bay-matched FORMTRACEPAK-V<ver>.iso.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:12:43 -04:00
cproudlock
64e1f7e088 Report-AssetToShopDB: log OS + boot time in the POST line
Adds os= and boot= to the POST log line so report-asset-*.log shows whether
the PC actually grabbed the OS version and LastBootUpTime (uptime source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:31:12 -04:00
cproudlock
287ec86c12 Report-AssetToShopDB: fleet-wide reporting, more fields, dual NIC
- collect logged-in user (console user via Win32_ComputerSystem, bare
  username), pc-type (C:\Enrollment\pc-type.txt), make/model, OS version
  (caption + DisplayVersion + build), last boot time (for uptime)
- report BOTH corp and controller NICs (physical only), each with MAC,
  tagged IsMachineNetwork; was corp-only before
- machine-number sourcing adds C:\Enrollment\cmm\cmmid.txt (CMM bay id)
  and skips the 9999 placeholder everywhere
- intended to run from common\ (every pc-type), not collections-only;
  api.asp patch-style update keeps it from clobbering other types

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:41:36 -04:00
cproudlock
85e7d91b1a CMM: imaging manifest installs goCMM 2.12.3 (was 1.1)
Future PXE images now install goCMM 2.12.3 (NSIS, /S silent, -> C:\Program Files
\goCMM, runs no-admin). Detection switched to File on goCMM.exe (new install
path). The tsgwp00525 SFLD enforce manifest is deliberately left on goCMM 1.1 so
already-deployed bays are NOT auto-upgraded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:45:25 -04:00
cproudlock
2461804fc8 CMM: Backup-CMM also captures the whole C:\Program Files\WAI tree
Adds a wai_backup_<PC>_<ts>.zip (robocopy /E of C:\Program Files\WAI + the x86
path) alongside the goCMM + PC-DMIS backups, indexed in cmm-backup-index.json.
Captures machine/controller content beyond the per-version PC-DMIS grab. Can be
multi-GB if WAI holds the full PC-DMIS 2016 install.

NOTE: this only CAPTURES it. sync-cmm-backups.sh + Restore-CMM still handle only
gocmm/pcdmis zips - staging/restoring the WAI zip needs those updated too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:40:01 -04:00
cproudlock
48bc609eb5 CMM/DODA: fix DODA-bay profile resolution + goCMM 2.12 DataFolder + settings converter
- Get-PCProfile: subtype-strip fallback. DODA bays set pc-subtype.txt=doda, so
  the profile key became "gea-shopfloor-cmm-doda" which matched NO profile/alias
  -> Get-PCProfile returned null -> callers fell to hardcoded defaults (no
  PC-DMIS desktop icons; Defect Tracker / WJ Shopfloor / Plant Apps force-started).
  Now an unmatched compound key falls back to the bare pc-type (-> CMM). VM-tested:
  gea-shopfloor-cmm/doda + CMM/doda resolve to CMM (7 apps, PC-DMIS present);
  non-CMM unaffected.
- 09-Setup-CMM Step 2.5c: Active Setup seed for goCMM 2.12 DataFolder. goCMM 2.12
  stores its shared-data-dir in HKCU\Software\General Electric\goCMM\DataFolder
  (decompiled: RegistrySettings uses Registry.CurrentUser - per-user). Imaging as
  SupportUser wouldn't reach the ShopFloor operator's HKCU. Active Setup runs the
  StubPath once per user at first logon -> every user gets DataFolder=C:\geaofi\.
  VM-tested: StubPath writes the value with the trailing backslash intact.
- Convert-goCMMSettings.ps1: converts legacy goCMM 1.1 ApplicationSettings.xml ->
  goCMM 2.12 goCMMSettings.xml schema. VM-tested: output byte-identical to a real
  goCMM-2.12-produced CMM10 goCMMSettings.xml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:59:08 -04:00
cproudlock
132c57ab39 CMM: create C:\GE PC-DMIS FRONT END at imaging + move regserver helper to Public desktop
- Ensure-PCDMISFrontEnd.ps1/.bat: standalone fix for already-imaged bays -
  create C:\GE PC-DMIS FRONT END + grant Users/Auth Users Modify. PCDToIGES.exe
  writes its error log there in its catch block; on a fresh bay the dir is absent
  (legacy front-end setup isn't part of imaging), so ANY PCDToIGES error becomes
  an unhandled DirectoryNotFoundException that crashes the export and masks the
  real cause (confirmed live on a CMM bay).
- 09-Setup-CMM Step 2.5b: create that dir + ACL at imaging for every CMM bay.
- 09-Setup-CMM Step 2.5: Register-PCDMIS-COM.bat now lands on the Public desktop
  (visible to operator or SupportUser) instead of SupportUser-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 11:56:00 -04:00
cproudlock
99deaa6882 CMM: add PC-DMIS 2026.1 desktop shortcut + taskbar pin (site-config)
The CMM pcProfile listed PC-DMIS 2016 and 2019 R2 in desktopApps/taskbarPins but
not 2026.1, so freshly imaged 2026 bays (CMM11/12) got no PC-DMIS shortcut. Add
the 2026.1 entries pointing at C:\Program Files\Hexagon\PC-DMIS 2026.1 64-bit\
PCDLRN.exe. Test-Path gated by the shortcut generator, so it is a no-op on
2016/2019 bays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 20:49:55 -04:00
cproudlock
948d013539 preinstall: add VC++ 2015-2022 x64 redist (PC-DMIS 2026 vcruntime140_1.dll)
The x64 redist ladder in preinstall stopped at 2013, so 64-bit PC-DMIS 2026 had
no vcruntime140_1.dll (introduced in VC++ 2019/14.20) and PCDLRN.exe failed to
launch: "vcruntime140_1.dll was not found". The patched PC-DMIS MSI bypasses
Hexagon's Burn bundle, which would otherwise have pulled the runtime in - and the
existing 2010/2012 x64 entries (added for PC-DMIS 2016/2019) never got a
2015-2022 successor.

Add VC++ 2015-2022 x64 (vc_redist.x64.exe 14.44.35211, /install /quiet
/norestart), PCTypes ["*"], File-detected on vcruntime140_1.dll so it is
version-independent. Installer staged on the share at
pre-install/installers/vcredist/2022-x64/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 20:43:35 -04:00
cproudlock
6e218f3daf CMM/DODA: Install-DODA does the full unattended runtime+app install
The vendor GE_DODA_build5_Installer.exe is a multi-app NSIS bundler whose /S
silent mode is broken (nested sub-install -> MSI 1619); VM-confirmed it only
works interactively. So Install-DODA now drives the bundled components directly,
all silent:
  python-2.7.16 msi /qn  -> C:\Python27
  openjdk-1.8.0.232 msi /qn -> C:\Program Files\RedHat
  gs927w64.exe /S        -> Ghostscript 9.27
  vc_redist /quiet
  doda_build*.zip        -> C:\Apps\DODA  (+ writes .settings the zip lacks)
Then sets AFDA_INSTALL_DIR + Java PATH, precompiles .py->.pyc as admin, and
grants Users+Auth Users Full on C:\Apps\DODA so the locked-down operator can run
DovetailAnalysis.exe (writes .pyc/PreProcess/$TMP). Each step detects-and-skips;
idempotent. VM-validated end to end incl a non-admin operator-paths test.

Runtime binaries (python/jdk/gs/vc msis + doda zip) are staged on the share, not
committed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 19:54:15 -04:00
cproudlock
b5b644a360 CMM/DODA: restore on DODA bays + grant Full on whole C:\Apps\DODA
- Restore-CMM: drop the skip-on-doda gate. DODA bays now restore the
  config-version PC-DMIS + goCMM settings like any other bay (they have backups
  now; DODA itself installs separately to C:\Apps\DODA and is unaffected).
- sync-cmm-backups.sh: update the stale "do not back up DODA bays" note.
- Install-DODA.ps1: grant Users + Authenticated Users Full on the WHOLE
  C:\Apps\DODA (was PreProcess only) - DODA writes output/temp throughout the
  folder as the locked-down operator. /T covers PreProcess.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:22:06 -04:00
cproudlock
bc89ba1cf3 CMM: add Register-PCDMIS-COM.bat desktop helper for post-license COM regserver
PC-DMIS COM must be registered for goCMM to connect, but Pcdlrn.exe /regserver
no-ops until PC-DMIS is licensed - which is a manual post-image step
(clmadmin.exe). So we cannot register at imaging time. Instead 09-Setup-CMM
drops a self-elevating one-click helper on the SupportUser desktop (a
pre-existing profile; avoids 06-OrganizeDesktop's Public-desktop sweep). The
tech runs it after activating the license; safe to re-run. Falls back to Public
Desktop if the SupportUser profile is absent. Copy happens before the Step 3
staging cleanup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:10:44 -04:00
cproudlock
1487abdba5 CMM/DODA: enable on bays 4/10/11/12, fix PCTypes match, grant PreProcess ACL
Three fixes so DODA actually deploys on the four bays that need it:

- cmm-bay-config.csv: doda=yes for CMM4, CMM10, CMM11, CMM12 (was no on all
  bays). Drives doda.txt -> startnet pc-subtype.txt=doda -> the cmm-doda path.
- cmm-manifest.json: DODA entry PCTypes "cmm-doda" -> "gea-shopfloor-cmm-doda".
  The old value never matched: Test-PCTypeMatches builds the PC identity set as
  {gea-shopfloor-cmm, gea-shopfloor-cmm-doda, CMM} (the alias of the cmm type is
  bare CMM, no subtype variant), so "cmm-doda" was in no set and the entry was
  silently skipped. Must be the exact Type-SubType string.
- Install-DODA.ps1: grant Users + Authenticated Users Full on C:\Apps\DODA\
  PreProcess (icacls, SIDs, OI/CI inherit), applied every run. MergeFiles.exe
  writes there as the locked-down operator; without it the merge step fails.

Not yet pushed to the SFLD/enrollment share (server unreachable at commit time).
Not smoke-tested - no pwsh available here; logic-traced only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 16:35:39 -04:00
cproudlock
7af66575d0 CMM: gocmm-debug - capture the real startup NRE (PC-DMIS COM not registered)
debug.log from the bay shows the part group enumerates fine (14 .geop files
opened), then goCMM connects to PC-DMIS over COM and throws:

  System.ArgumentNullException: Value cannot be null. Parameter name: type
     at System.Activator.CreateInstance(Type type)
     at GEAE.Common.CMM.CMMInterfaces.PCDMIS.PCDMIS.<ConnectToSoftware>b__43_0()
  -> System.NullReferenceException at ConnectToSoftware(...)

Root cause: PC-DMIS automation server is not COM-registered, so
Type.GetTypeFromProgID returns null -> CreateInstance(null) throws -> the NRE
is the downstream symptom. Not part-group, not permissions, not calibration.

- PROBE 4: pull the .NET Runtime / Application Error crash stack for goCMM from
  the Application log so the next run captures the null in one shot.
- PROBE 5: (a) part-group UNC reachability; (b) PC-DMIS COM registration check
  (PCDLRN.* ProgID -> CLSID -> LocalServer32) that names the missing registration
  and the Pcdlrn.exe /regserver fix; plus install presence for context.
- .bat header documents the COM root cause.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 08:31:11 -04:00
cproudlock
8e11f0824a CMM: gocmm-debug probes the startup NRE (part-group match), checks real exe
goCMM matches the registry 'Selected Part Group' against the
ApplicationSettings.xml <PartGroup FullName> entries with a case-sensitive
compare. No match -> SelectedPartGroup null -> "Object reference not set to
an instance of an object" at start. This is a different failure from the
registry SecurityException the script already probes.

- PROBE 3: read the reg value (32-bit view) + every FullName in the XML,
  Ordinal-compare, and report exact / case-only / no-match / missing-XML.
  Case-only and no-match name the d441abd canonicalization fix as the remedy.
- Version check now looks for the real goCMM.exe, not the nonexistent
  GEAOperatorFriendlyInterface.exe (was a false MISSING).
- .bat header documents both failure modes it now diagnoses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 08:08:00 -04:00
26 changed files with 1569 additions and 512 deletions

View File

@@ -156,36 +156,41 @@
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>4</Order>
<CommandLine>powershell.exe -ep Bypass -Command "net use Z: \\172.16.9.1\enrollment /user:pxe-upload pxe; robocopy Z:\shopfloor-setup C:\Enrollment Fetch-StagingPayload.ps1 Verify-And-Heal-Staging.ps1 /R:2 /W:2; robocopy Z:\scripts C:\Enrollment run-enrollment.ps1 wait-for-internet.ps1 migrate-to-wifi.ps1 /R:2 /W:2"</CommandLine>
<Description>Bootstrap self-heal: pull Fetch/Verify-Heal + enrollment scripts from the PXE share so Order 5/6 run after an early WinPE staging failure.</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>5</Order>
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\Enrollment\Fetch-StagingPayload.ps1"</CommandLine>
<Description>Fetch bulk staging (shopfloor-setup tree + preinstall bundle) from the PXE share on a fresh mount, BEFORE the production-network switch takes the bay off the imaging LAN. Detailed log at C:\Logs\Fetch\.</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>5</Order>
<Order>6</Order>
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\Enrollment\Verify-And-Heal-Staging.ps1"</CommandLine>
<Description>Verify every imaging payload arrived and re-pull anything missing from the PXE share (incl the CMM bundle + selected-bay backup) while still on the imaging LAN, BEFORE wait-for-internet switches the bay to the production network. Log at C:\Logs\Fetch\.</Description>
<Description>Verify + re-pull any missing imaging payload from the PXE share (CMM/Keyence/WaxTrace bundles + bay backup) on the imaging LAN before the production-network switch. Log C:\Logs\Fetch.</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>6</Order>
<Order>7</Order>
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\Enrollment\wait-for-internet.ps1"</CommandLine>
<Description>Prompt to connect production network then wait for TCP 443 connectivity</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>7</Order>
<Order>8</Order>
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\Enrollment\migrate-to-wifi.ps1"</CommandLine>
<Description>Migrate from wired to WiFi if WiFi adapter present, else stay on wired</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>8</Order>
<Order>9</Order>
<CommandLine>msiexec.exe /i "C:\PreInstall\installers\powershell7\PowerShell-7.5.4-win-x64.msi" /qn /norestart ADD_PATH=1 USE_MU=0 ENABLE_MU=0 DISABLE_TELEMETRY=1</CommandLine>
<Description>Install PowerShell 7 BEFORE PPKG so Intune SetupCredentials Win32App finds pwsh.exe (race fix)</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>9</Order>
<Order>10</Order>
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\run-enrollment.ps1"</CommandLine>
<Description>Run GCCH Enrollment</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>10</Order>
<Order>11</Order>
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\Enrollment\Run-ShopfloorSetup.ps1"</CommandLine>
<Description>Run shopfloor PC type setup</Description>
</SynchronousCommand>

View File

@@ -1,307 +1,363 @@
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- 1. windowsPE is intentionally empty -->
<settings pass="windowsPE" />
<!-- 2. Offline servicing (drivers) -->
<settings pass="offlineServicing">
<component name="Microsoft-Windows-PnpCustomizationsNonWinPE"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<DriverPaths>
<PathAndCredentials wcm:action="add" wcm:keyValue="1">
<Path>W:\Drivers</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="2">
<Path>W:\Deploy\Applications\extra\printdrivers\BROTHER\UNIV-PS-01181\PS\64</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="3">
<Path>W:\Deploy\Applications\extra\printdrivers\HP</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="4">
<Path>W:\Deploy\Applications\extra\printdrivers\XEROX\UNIV_5.1035.2.0_PS_x64_Driver</Path>
</PathAndCredentials>
</DriverPaths>
</component>
</settings>
<!-- 3. specialize: computer naming + RunSynchronous all in ONE component -->
<settings pass="specialize">
<!-- 3a. Shell-Setup for naming/owner/org -->
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<ComputerName>H%serialnumber%</ComputerName>
<RegisteredOrganization>GE Aerospace</RegisteredOrganization>
<RegisteredOwner>GE</RegisteredOwner>
<TimeZone>Eastern Standard Time</TimeZone>
</component>
<!-- 3b. RunSynchronous for all of your installers, copies, etc. -->
<component name="Microsoft-Windows-Deployment"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<RunSynchronous>
<!-- EAP-PEAP MSI -->
<RunSynchronousCommand wcm:action="add">
<Order>1</Order>
<Path>msiexec /i "C:\Deploy\Applications\extra\wireless\EAP-PEAP.msi" /quiet /norestart</Path>
<Description>Install EAP-PEAP</Description>
</RunSynchronousCommand>
<!-- Wi-Fi profiles -->
<RunSynchronousCommand wcm:action="add">
<Order>2</Order>
<Path>cmd /c netsh wlan add profile filename="C:\Deploy\Applications\extra\wireless\BLUESSO.xml" user=all</Path>
<Description>Add BLUESSO WiFi profile</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>3</Order>
<Path>cmd /c netsh wlan add profile filename="C:\Deploy\Applications\extra\wireless\WiFi-Profile.xml" user=all</Path>
<Description>Add generic WiFi profile</Description>
</RunSynchronousCommand>
<!-- Certificates -->
<RunSynchronousCommand wcm:action="add">
<Order>4</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_External_Root_CA_2_1.cer"</Path>
<Description>Install External Root Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>5</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_External_Intermediate_CA_2_1.cer"</Path>
<Description>Install External Intermediate Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>6</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_Enterprise_Root_CA_2_1.cer"</Path>
<Description>Install Enterprise Root Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>7</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Device_Issuing_CA_2_1.cer"</Path>
<Description>Install Enterprise Device Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>8</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Server_Issuing_CA_2_1.cer"</Path>
<Description>Install Enterprise Server Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>9</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Smart_Card_Issuing_CA_2_1.cer"</Path>
<Description>Install SmartCard Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>10</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_User_Issuing_CA_2_1.cer"</Path>
<Description>Install User Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>11</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_Aerospace_Enterprise_Root_CA_1.cer"</Path>
<Description>Install Aerospace Enterprise Root CA</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>12</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\ZscalerCommercialCertificate-2048-SHA256.crt"</Path>
<Description>Install Zscaler Certificate</Description>
</RunSynchronousCommand>
<!-- Fonts -->
<RunSynchronousCommand wcm:action="add">
<Order>13</Order>
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\3OF9.TTF" "%WINDIR%\Fonts\" /Y</Path>
<Description>Copy 3OF9 Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>14</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "3 of 9 Barcode" /t REG_SZ /d "3OF9.TTF" /f</Path>
<Description>Register 3OF9 Barcode Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>15</Order>
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\Code39AzaleaNarrow3.ttf" "%WINDIR%\Fonts\" /Y</Path>
<Description>Copy Code39 Azalea Narrow Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>16</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "Code39AzaleaNarrow3" /t REG_SZ /d "Code39AzaleaNarrow3.ttf" /f</Path>
<Description>Register Code39 Azalea Narrow Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>17</Order>
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\Code39Azalea.ttf" "%WINDIR%\Fonts\" /Y</Path>
<Description>Copy Code39 Azalea Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>18</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "Code39Azalea" /t REG_SZ /d "Code39Azalea.ttf" /f</Path>
<Description>Register Code39 Azalea Font</Description>
</RunSynchronousCommand>
<!-- OpenText installers -->
<RunSynchronousCommand wcm:action="add">
<Order>19</Order>
<Path>C:\Deploy\Applications\extra\opentext\opentext_hostexplorer_sp1_15.0_v01.exe /quiet /norestart</Path>
<Description>Install OpenText HostExplorer SP1</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>20</Order>
<Path>C:\Deploy\Applications\extra\opentext\J2SE_Runtime_Environment_1.6.0_22_Static_Config_V2_Co-Exist.EXE /silent /norestart</Path>
<Description>Install J2SE Runtime Environment 1.6.0_22</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>21</Order>
<Path>C:\Deploy\Applications\extra\opentext\unattended.bat</Path>
<Description>Install J2SE Runtime Environment 1.6.0_22</Description>
</RunSynchronousCommand>
<!-- Adobe -->
<RunSynchronousCommand wcm:action="add">
<Order>22</Order>
<Path>msiexec /i "C:\Deploy\Applications\extra\adobe\AcroRead.msi" TRANSFORMS="C:\Deploy\Applications\extra\adobe\AcroRead.mst" /quiet /norestart</Path>
<Description>Install Adobe</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>23</Order>
<Path>msiexec /p "C:\Deploy\Applications\extra\adobe\AcroRdrDCUpd2500120531.msp" /quiet /norestart</Path>
<Description>Apply Adobe Reader Update</Description>
</RunSynchronousCommand>
<!-- Java JRE 8 Enterprise Installation -->
<RunSynchronousCommand wcm:action="add">
<Order>26</Order>
<Path>C:\Deploy\Applications\extra\java\jre-8u441-windows-i586.exe /s INSTALLDIR="C:\Program Files (x86)\Java\jre1.8.0_441" STATIC=1 AUTO_UPDATE=0 REBOOT=0 SPONSORS=0 WEB_JAVA=0 /L "C:\Windows\Logs\java-install.log"</Path>
<Description>Install Java JRE 8 Update 441 with Enterprise Settings and Logging</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Updates via Registry (64-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>27</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\JavaSoft\Java Update\Policy" /v EnableJavaUpdate /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Updates (64-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Updates via Registry (32-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>28</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Wow6432Node\JavaSoft\Java Update\Policy" /v EnableJavaUpdate /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Updates (32-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Update Check (64-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>29</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\JavaSoft\Java Update\Policy" /v EnableAutoUpdateCheck /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Update Check (64-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Update Check (32-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>30</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Wow6432Node\JavaSoft\Java Update\Policy" /v EnableAutoUpdateCheck /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Update Check (32-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Cortana -->
<RunSynchronousCommand wcm:action="add">
<Order>31</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v AllowCortana /t REG_DWORD /d 0 /f</Path>
<Description>Disable Cortana</Description>
</RunSynchronousCommand>
<!-- Disable Web Search in Start Menu -->
<RunSynchronousCommand wcm:action="add">
<Order>33</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v DisableWebSearch /t REG_DWORD /d 1 /f</Path>
<Description>Disable Web Search in Start Menu</Description>
</RunSynchronousCommand>
<!-- Disable Bing / web search suggestions in Start menu.
BingSearchEnabled is documented at HKCU\Software\Microsoft\Windows\CurrentVersion\Search
(not HKLM), so the old HKLM policy placement was a no-op. The current documented
suppression is DisableSearchBoxSuggestions=1 at Software\Policies\Microsoft\Windows\Explorer.
Writing it into the Default User hive so every newly-created account inherits it. -->
<RunSynchronousCommand wcm:action="add">
<Order>34</Order>
<Path>cmd /c reg load HKU\TempDU C:\Users\Default\NTUSER.DAT &amp; reg add "HKU\TempDU\Software\Policies\Microsoft\Windows\Explorer" /v DisableSearchBoxSuggestions /t REG_DWORD /d 1 /f &amp; reg unload HKU\TempDU</Path>
<Description>Disable Bing/web search suggestions (Default User hive)</Description>
</RunSynchronousCommand>
<!-- Disable Search Web when searching Windows -->
<RunSynchronousCommand wcm:action="add">
<Order>35</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v ConnectedSearchUseWeb /t REG_DWORD /d 0 /f</Path>
<Description>Disable Connected Search Use Web</Description>
</RunSynchronousCommand>
<!-- Disable Cortana Speech Recognition -->
<RunSynchronousCommand wcm:action="add">
<Order>36</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\InputPersonalization" /v AllowInputPersonalization /t REG_DWORD /d 0 /f</Path>
<Description>Disable Cortana Speech Recognition</Description>
</RunSynchronousCommand>
<!-- Disable Search Highlights (news/trending) -->
<RunSynchronousCommand wcm:action="add">
<Order>38</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v EnableDynamicContentInWSB /t REG_DWORD /d 0 /f</Path>
<Description>Disable Search Highlights</Description>
</RunSynchronousCommand>
</RunSynchronous>
</component>
</settings>
<!-- 4. oobeSystem: hide OEM/EULA screens -->
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>false</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>false</HideWirelessSetupInOOBE>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
<SkipUserOOBE>false</SkipUserOOBE>
<SkipMachineOOBE>false</SkipMachineOOBE>
</OOBE>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<Order>1</Order>
<CommandLine>shutdown -a</CommandLine>
<Description>Cancel any scheduled shutdown from Office installation</Description>
</SynchronousCommand>
<!-- Install Zscaler -->
<SynchronousCommand wcm:action="add">
<Order>2</Order>
<CommandLine>C:\Deploy\Applications\extra\zscaler\zscaler.bat</CommandLine>
<Description>Install Zscaler Client Connector</Description>
</SynchronousCommand>
<!-- Install Microsoft Office -->
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>cmd /c "cd /d C:\Deploy\Applications\extra\office\ &amp;&amp; install.bat"</CommandLine>
<Description>Install Microsoft Office</Description>
</SynchronousCommand>
</FirstLogonCommands>
</component>
</settings>
</unattend>
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- 1. windowsPE is intentionally empty -->
<settings pass="windowsPE" />
<!-- 2. Offline servicing (drivers) -->
<settings pass="offlineServicing">
<component name="Microsoft-Windows-PnpCustomizationsNonWinPE"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<DriverPaths>
<PathAndCredentials wcm:action="add" wcm:keyValue="1">
<Path>W:\Drivers</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="2">
<Path>W:\Deploy\Applications\extra\printdrivers\BROTHER\UNIV-PS-01181\PS\64</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="3">
<Path>W:\Deploy\Applications\extra\printdrivers\HP</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="4">
<Path>W:\Deploy\Applications\extra\printdrivers\XEROX\UNIV_5.1035.2.0_PS_x64_Driver</Path>
</PathAndCredentials>
</DriverPaths>
</component>
</settings>
<!-- 3. specialize: computer naming + RunSynchronous all in ONE component -->
<settings pass="specialize">
<!-- 3a. Shell-Setup for naming/owner/org -->
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<ComputerName>H%serialnumber%</ComputerName>
<RegisteredOrganization>GE Aerospace</RegisteredOrganization>
<RegisteredOwner>GE</RegisteredOwner>
<TimeZone>Eastern Standard Time</TimeZone>
</component>
<!-- 3b. RunSynchronous for all of your installers, copies, etc. -->
<component name="Microsoft-Windows-Deployment"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<RunSynchronous>
<!-- EAP-PEAP MSI -->
<RunSynchronousCommand wcm:action="add">
<Order>1</Order>
<Path>msiexec /i "C:\Deploy\Applications\extra\wireless\EAP-PEAP.msi" /quiet /norestart</Path>
<Description>Install EAP-PEAP</Description>
</RunSynchronousCommand>
<!-- Wi-Fi profiles -->
<RunSynchronousCommand wcm:action="add">
<Order>2</Order>
<Path>cmd /c netsh wlan add profile filename="C:\Deploy\Applications\extra\wireless\BLUESSO.xml" user=all</Path>
<Description>Add BLUESSO WiFi profile</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>3</Order>
<Path>cmd /c netsh wlan add profile filename="C:\Deploy\Applications\extra\wireless\WiFi-Profile.xml" user=all</Path>
<Description>Add generic WiFi profile</Description>
</RunSynchronousCommand>
<!-- Certificates -->
<RunSynchronousCommand wcm:action="add">
<Order>4</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_External_Root_CA_2_1.cer"</Path>
<Description>Install External Root Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>5</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_External_Intermediate_CA_2_1.cer"</Path>
<Description>Install External Intermediate Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>6</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_Enterprise_Root_CA_2_1.cer"</Path>
<Description>Install Enterprise Root Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>7</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Device_Issuing_CA_2_1.cer"</Path>
<Description>Install Enterprise Device Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>8</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Server_Issuing_CA_2_1.cer"</Path>
<Description>Install Enterprise Server Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>9</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Smart_Card_Issuing_CA_2_1.cer"</Path>
<Description>Install SmartCard Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>10</Order>
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_User_Issuing_CA_2_1.cer"</Path>
<Description>Install User Issuing Certificate</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>11</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_Aerospace_Enterprise_Root_CA_1.cer"</Path>
<Description>Install Aerospace Enterprise Root CA</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>12</Order>
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\ZscalerCommercialCertificate-2048-SHA256.crt"</Path>
<Description>Install Zscaler Certificate</Description>
</RunSynchronousCommand>
<!-- Fonts -->
<RunSynchronousCommand wcm:action="add">
<Order>13</Order>
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\3OF9.TTF" "%WINDIR%\Fonts\" /Y</Path>
<Description>Copy 3OF9 Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>14</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "3 of 9 Barcode" /t REG_SZ /d "3OF9.TTF" /f</Path>
<Description>Register 3OF9 Barcode Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>15</Order>
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\Code39AzaleaNarrow3.ttf" "%WINDIR%\Fonts\" /Y</Path>
<Description>Copy Code39 Azalea Narrow Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>16</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "Code39AzaleaNarrow3" /t REG_SZ /d "Code39AzaleaNarrow3.ttf" /f</Path>
<Description>Register Code39 Azalea Narrow Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>17</Order>
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\Code39Azalea.ttf" "%WINDIR%\Fonts\" /Y</Path>
<Description>Copy Code39 Azalea Font</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>18</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "Code39Azalea" /t REG_SZ /d "Code39Azalea.ttf" /f</Path>
<Description>Register Code39 Azalea Font</Description>
</RunSynchronousCommand>
<!-- OpenText installers -->
<RunSynchronousCommand wcm:action="add">
<Order>19</Order>
<Path>C:\Deploy\Applications\extra\opentext\opentext_hostexplorer_sp1_15.0_v01.exe /quiet /norestart</Path>
<Description>Install OpenText HostExplorer SP1</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>20</Order>
<Path>C:\Deploy\Applications\extra\opentext\J2SE_Runtime_Environment_1.6.0_22_Static_Config_V2_Co-Exist.EXE /silent /norestart</Path>
<Description>Install J2SE Runtime Environment 1.6.0_22</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>21</Order>
<Path>C:\Deploy\Applications\extra\opentext\unattended.bat</Path>
<Description>Install J2SE Runtime Environment 1.6.0_22</Description>
</RunSynchronousCommand>
<!-- Adobe -->
<RunSynchronousCommand wcm:action="add">
<Order>22</Order>
<Path>msiexec /i "C:\Deploy\Applications\extra\adobe\AcroRead.msi" TRANSFORMS="C:\Deploy\Applications\extra\adobe\AcroRead.mst" /quiet /norestart</Path>
<Description>Install Adobe</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>23</Order>
<Path>msiexec /p "C:\Deploy\Applications\extra\adobe\AcroRdrDCUpd2500120531.msp" /quiet /norestart</Path>
<Description>Apply Adobe Reader Update</Description>
</RunSynchronousCommand>
<!-- Java JRE 8 Enterprise Installation -->
<RunSynchronousCommand wcm:action="add">
<Order>26</Order>
<Path>C:\Deploy\Applications\extra\java\jre-8u441-windows-i586.exe /s INSTALLDIR="C:\Program Files (x86)\Java\jre1.8.0_441" STATIC=1 AUTO_UPDATE=0 REBOOT=0 SPONSORS=0 WEB_JAVA=0 /L "C:\Windows\Logs\java-install.log"</Path>
<Description>Install Java JRE 8 Update 441 with Enterprise Settings and Logging</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Updates via Registry (64-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>27</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\JavaSoft\Java Update\Policy" /v EnableJavaUpdate /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Updates (64-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Updates via Registry (32-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>28</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Wow6432Node\JavaSoft\Java Update\Policy" /v EnableJavaUpdate /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Updates (32-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Update Check (64-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>29</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\JavaSoft\Java Update\Policy" /v EnableAutoUpdateCheck /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Update Check (64-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Java Auto Update Check (32-bit) -->
<RunSynchronousCommand wcm:action="add">
<Order>30</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Wow6432Node\JavaSoft\Java Update\Policy" /v EnableAutoUpdateCheck /t REG_DWORD /d 0 /f</Path>
<Description>Disable Java Auto Update Check (32-bit)</Description>
</RunSynchronousCommand>
<!-- Disable Cortana -->
<RunSynchronousCommand wcm:action="add">
<Order>31</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v AllowCortana /t REG_DWORD /d 0 /f</Path>
<Description>Disable Cortana</Description>
</RunSynchronousCommand>
<!-- Disable Web Search in Start Menu -->
<RunSynchronousCommand wcm:action="add">
<Order>33</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v DisableWebSearch /t REG_DWORD /d 1 /f</Path>
<Description>Disable Web Search in Start Menu</Description>
</RunSynchronousCommand>
<!-- Disable Bing / web search suggestions in Start menu.
BingSearchEnabled is documented at HKCU\Software\Microsoft\Windows\CurrentVersion\Search
(not HKLM), so the old HKLM policy placement was a no-op. The current documented
suppression is DisableSearchBoxSuggestions=1 at Software\Policies\Microsoft\Windows\Explorer.
Writing it into the Default User hive so every newly-created account inherits it. -->
<RunSynchronousCommand wcm:action="add">
<Order>34</Order>
<Path>cmd /c reg load HKU\TempDU C:\Users\Default\NTUSER.DAT &amp; reg add "HKU\TempDU\Software\Policies\Microsoft\Windows\Explorer" /v DisableSearchBoxSuggestions /t REG_DWORD /d 1 /f &amp; reg unload HKU\TempDU</Path>
<Description>Disable Bing/web search suggestions (Default User hive)</Description>
</RunSynchronousCommand>
<!-- Disable Search Web when searching Windows -->
<RunSynchronousCommand wcm:action="add">
<Order>35</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v ConnectedSearchUseWeb /t REG_DWORD /d 0 /f</Path>
<Description>Disable Connected Search Use Web</Description>
</RunSynchronousCommand>
<!-- Disable Cortana Speech Recognition -->
<RunSynchronousCommand wcm:action="add">
<Order>36</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\InputPersonalization" /v AllowInputPersonalization /t REG_DWORD /d 0 /f</Path>
<Description>Disable Cortana Speech Recognition</Description>
</RunSynchronousCommand>
<!-- Disable Search Highlights (news/trending) -->
<RunSynchronousCommand wcm:action="add">
<Order>38</Order>
<Path>cmd /c reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v EnableDynamicContentInWSB /t REG_DWORD /d 0 /f</Path>
<Description>Disable Search Highlights</Description>
</RunSynchronousCommand>
<!-- Engineering prereqs: .NET 3.5 + VC++ redistributables (staged in extra_engineering\prereqs; if-exist guarded so non-engineering images skip) -->
<RunSynchronousCommand wcm:action="add">
<Order>39</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\sxs\microsoft-windows-netfx3-ondemand-package~31bf3856ad364e35~amd64~~.cab" dism /online /enable-feature /featurename:NetFx3 /all /source:"C:\Deploy\Applications\extra_engineering\prereqs\sxs" /limitaccess</Path>
<Description>Enable .NET Framework 3.5 from staged sxs</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>40</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v10.0.40219.325_x64\InstallMSVC2010Redist10.0.40219.325_x64.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v10.0.40219.325_x64\InstallMSVC2010Redist10.0.40219.325_x64.exe"</Path>
<Description>VC++ 2010 x64</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>41</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v10.0.40219.325\InstallMSVC2010Redist10.0.40219.325.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v10.0.40219.325\InstallMSVC2010Redist10.0.40219.325.exe"</Path>
<Description>VC++ 2010 x86</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>42</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v11.0.60610.1_x64\InstallMSVC2012Redist11.0.60610.1_x64.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v11.0.60610.1_x64\InstallMSVC2012Redist11.0.60610.1_x64.exe"</Path>
<Description>VC++ 2012 x64</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>43</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v11.0.60610.1\InstallMSVC2012Redist11.0.60610.1.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v11.0.60610.1\InstallMSVC2012Redist11.0.60610.1.exe"</Path>
<Description>VC++ 2012 x86</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>44</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v9.0.30729.5026_x64\InstallMSVC2008Redist.5026_x64.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v9.0.30729.5026_x64\InstallMSVC2008Redist.5026_x64.exe"</Path>
<Description>VC++ 2008 x64</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>45</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v9.0.30729.5026\InstallMSVC2008Redist.5026.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v9.0.30729.5026\InstallMSVC2008Redist.5026.exe"</Path>
<Description>VC++ 2008 x86</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>46</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v8.0.50727.6195_x64\InstallMSVC2005Redist.6195_x64.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v8.0.50727.6195_x64\InstallMSVC2005Redist.6195_x64.exe"</Path>
<Description>VC++ 2005 x64</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>47</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v8.0.50727.6195\InstallMSVC2005Redist.6195.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v8.0.50727.6195\InstallMSVC2005Redist.6195.exe"</Path>
<Description>VC++ 2005 x86</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>48</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v12.0.30501.0_x64\InstallMSVC2013Redist12.0.30501.0_x64.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v12.0.30501.0_x64\InstallMSVC2013Redist12.0.30501.0_x64.exe"</Path>
<Description>VC++ 2013 x64</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>49</Order>
<Path>cmd /c if exist "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v14.40.33816_x64\InstallMSVC20152022Redist14.40.33816_x64.exe" "C:\Deploy\Applications\extra_engineering\prereqs\redistrib\v14.40.33816_x64\InstallMSVC20152022Redist14.40.33816_x64.exe"</Path>
<Description>VC++ 2015-2022 x64</Description>
</RunSynchronousCommand>
</RunSynchronous>
</component>
</settings>
<!-- 4. oobeSystem: hide OEM/EULA screens -->
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>false</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>false</HideWirelessSetupInOOBE>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
<SkipUserOOBE>false</SkipUserOOBE>
<SkipMachineOOBE>false</SkipMachineOOBE>
</OOBE>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<Order>1</Order>
<CommandLine>shutdown -a</CommandLine>
<Description>Cancel any scheduled shutdown from Office installation</Description>
</SynchronousCommand>
<!-- Install Zscaler -->
<SynchronousCommand wcm:action="add">
<Order>2</Order>
<CommandLine>C:\Deploy\Applications\extra\zscaler\zscaler.bat</CommandLine>
<Description>Install Zscaler Client Connector</Description>
</SynchronousCommand>
<!-- Install Microsoft Office -->
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>cmd /c "cd /d C:\Deploy\Applications\extra\office\ &amp;&amp; install.bat"</CommandLine>
<Description>Install Microsoft Office</Description>
</SynchronousCommand>
</FirstLogonCommands>
</component>
</settings>
</unattend>

View File

@@ -147,6 +147,16 @@
"DetectionPath": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{C18FB403-1E88-43C8-AD8A-CED50F23DE8B}",
"PCTypes": ["*"]
},
{
"_comment": "VC++ 2015-2022 x64 - required by PC-DMIS 2026 (PCDLRN.exe links vcruntime140_1.dll, added in VC++ 2019 / v14.20). The x64 redist line above previously stopped at 2013, so 64-bit PC-DMIS 2026 was missing its runtime and PCDLRN.exe failed to start with 'vcruntime140_1.dll was not found'. The patched PC-DMIS MSI bypasses Hexagon's Burn bundle, which is what would otherwise have pulled this in. Full vc_redist.x64.exe (14.44.35211, 2015-2022 shared CRT). Modern bootstrapper honors /norestart. Detect on the DLL itself so it is version-independent.",
"Name": "VC++ Redistributable 2015-2022 x64",
"Installer": "vcredist/2022-x64/vc_redist.x64.exe",
"Type": "EXE",
"InstallArgs": "/install /quiet /norestart",
"DetectionMethod": "File",
"DetectionPath": "C:\\Windows\\System32\\vcruntime140_1.dll",
"PCTypes": ["*"]
},
{
"_comment": "Adobe Acrobat Reader DC - same version as GEA-Engineering image. Two-step install via Install-AcroReader.cmd wrapper: base MSI with enterprise transform (AcroRead.mst) then DC update patch (AcroRdrDCUpd2500120531.msp). Source files on PXE server at /srv/samba/winpeapps/gea-engineer/Deploy/Applications/extra/adobe/ - copy to /srv/samba/enrollment/preinstall/installers/adobe/.",
"Name": "Adobe Acrobat Reader DC",

View File

@@ -38,10 +38,6 @@
- gea-standard
- gea-engineer
- gea-shopfloor
- ge-standard
- ge-engineer
- ge-shopfloor-lockdown
- ge-shopfloor-mce
shopfloor_types:
- gea-shopfloor
# Image variants that share the generic Win10/Win11 unattend
@@ -50,8 +46,6 @@
standard_types:
- gea-standard
- gea-engineer
- ge-standard
- ge-engineer
deploy_subdirs:
- Applications
- Control

View File

@@ -113,6 +113,28 @@ if ($siteConfig -and $siteConfig.pcProfiles -and $profileKey) {
}
}
}
# Subtype-strip fallback: a compound key like "gea-shopfloor-cmm-doda" (DODA
# CMM bays set pc-subtype.txt=doda) has no profile of its own. Fall back to
# the BARE pc-type ("gea-shopfloor-cmm" -> "CMM"). Without this the profile
# resolves to $null and callers silently use hardcoded defaults (wrong
# desktop icons + unwanted startup items).
if (-not $pcProfile -and $pcSubtype) {
$bare = $pcType
$pcProfile = $siteConfig.pcProfiles.$bare
if (-not $pcProfile) {
foreach ($g in $pcProfileAliasGroups) {
if ($g -icontains $bare) {
foreach ($alias in $g) {
if ($alias -ieq $bare) { continue }
$candidate = $siteConfig.pcProfiles.$alias
if ($candidate) { $pcProfile = $candidate; break }
}
break
}
}
}
if ($pcProfile) { $profileKey = $bare }
}
}
if ($pcProfile) {

View File

@@ -7,10 +7,12 @@ enrollment share. Runs in full Windows (reliable network), so it is immune to th
WinPE samba-idle-drop that loses copies during the WIM apply.
Covers the generic Fetch payload (shopfloor-setup tree + preinstall bundle) AND
the heavy per-type payload that Fetch-StagingPayload does NOT pull today: the CMM
bundle (C:\CMM-Install) and the selected bay's backup set
(C:\CMM-Install\backups\<cmmid>). That is the one that silently goes missing when
WinPE staging runs out of time before reboot.
the heavy per-type payloads that Fetch-StagingPayload does NOT pull today:
- CMM C:\CMM-Install (+ selected bay's backup C:\CMM-Install\backups\<cmmid>)
- Keyence C:\KeyenceInstall\<model> (MSI + Data*.cab)
- WaxTrace C:\WaxTrace-Install (bundle + bay-matched FormTracePak ISO)
These are the ones that silently go missing when WinPE staging runs out of time
(idle-dead Y: mount) before reboot.
Designed to be:
- run manually on a problem PC (Verify-And-Heal-Staging.bat), or
@@ -89,6 +91,31 @@ if ($pcType -eq 'gea-shopfloor-cmm') {
Add-Item "CMM backup ($cmmid)" "installers-post\cmm\backups\$cmmid" "C:\CMM-Install\backups\$cmmid" 'Dir' "C:\CMM-Install\backups\$cmmid" $null $true
}
}
# --- heavy Keyence payload (same gap as CMM: WinPE-only staged, never re-pulled
# by Fetch-StagingPayload). Only the selected model bundle lands under
# C:\KeyenceInstall\<model>. Verify on the model manifest so a missing/partial
# Data1.cab (the 700 MB - 2 GB payload msiexec SECREPAIR-hashes) gets re-pulled. ---
if ($pcType -eq 'gea-shopfloor-keyence') {
$kmodel = ReadTxt 'C:\Enrollment\keyence-model.txt'
if (-not $kmodel) { $kmodel = 'vr6000' }
Add-Item "Keyence bundle ($kmodel)" "installers-post\keyence\$kmodel" "C:\KeyenceInstall\$kmodel" 'Dir' "C:\KeyenceInstall\$kmodel\manifest.json"
}
# --- heavy WaxTrace payload (same gap as CMM/Keyence). Two parts, mirroring the
# three-step WinPE stage: (1) the bundle minus the formtracepak\ ISO dir, and
# (2) ONLY the bay's matched FORMTRACEPAK-V<ver>.iso, keyed on the version
# resolve-bay-config wrote to C:\Enrollment\waxtrace\version.txt during WinPE.
# If version.txt is missing (mount died before the resolver ran) the ISO cannot
# be re-pulled here - the bundle+resolver still heal, and resolve-bay-config can
# be re-run manually to regenerate version.txt then re-run this heal. ---
if ($pcType -eq 'gea-shopfloor-waxtrace') {
Add-Item 'WaxTrace bundle' 'installers-post\waxtrace' 'C:\WaxTrace-Install' 'Dir' 'C:\WaxTrace-Install\waxtrace-manifest.json' $null $false 'formtracepak'
$wtver = ReadTxt 'C:\Enrollment\waxtrace\version.txt'
if ($wtver) {
Add-Item "WaxTrace FTPak V$wtver" 'installers-post\waxtrace\formtracepak' 'C:\WaxTrace-Install\formtracepak' 'File' "C:\WaxTrace-Install\formtracepak\FORMTRACEPAK-V$wtver.iso" @("FORMTRACEPAK-V$wtver.iso")
} else {
Log 'WaxTrace: version.txt absent - cannot heal the bay-specific FormTracePak ISO (re-run resolve-bay-config then re-run heal)' 'WARN'
}
}
# --- robocopy-based verify/heal -----------------------------------------------
# Presence alone is NOT trusted: a partially transferred file (e.g. a truncated

View File

@@ -175,6 +175,45 @@ foreach ($dir in $pcdmisDirs) {
}
}
# ============================================================================
# Step 2.5b: GE PC-DMIS FRONT END dir (PCDToIGES.exe error-log path)
# ============================================================================
# PCDToIGES.exe (in every bay's C:\geaofi\Scripts) writes its error log to
# C:\GE PC-DMIS FRONT END\PCDToIGES.ERR inside its catch block. The legacy
# "GE PC-DMIS FRONT END" front-end setup that created this dir is not part of
# imaging, so on a fresh bay it is absent - and then ANY PCDToIGES failure
# becomes an unhandled DirectoryNotFoundException that crashes the export and
# MASKS the real error. Create it + grant the operator Modify so errors log.
$frontEnd = 'C:\GE PC-DMIS FRONT END'
try {
if (-not (Test-Path -LiteralPath $frontEnd)) { New-Item -ItemType Directory -Path $frontEnd -Force | Out-Null; Write-CMMLog "Created $frontEnd" }
foreach ($sid in '*S-1-5-32-545','*S-1-5-11') { # BUILTIN\Users, NT AUTHORITY\Authenticated Users
& icacls "$frontEnd" /grant "${sid}:(OI)(CI)M" /T /C 2>&1 | Out-Null
}
Write-CMMLog "Granted Users + Authenticated Users Modify on $frontEnd"
} catch { Write-CMMLog "Failed to ensure ${frontEnd}: $_" "WARN" }
# ============================================================================
# Step 2.5c: Active Setup - seed goCMM 2.12 DataFolder per-user for ALL users
# ============================================================================
# goCMM 2.12 stores its shared-data-dir in HKCU\Software\General Electric\goCMM
# \DataFolder (decompiled: RegistrySettings uses Registry.CurrentUser - PER USER,
# not HKLM). Setting it at imaging only touches the imaging user (SupportUser);
# the ShopFloor operator's HKCU would be empty -> goCMM prompts them to pick the
# folder on first launch. Active Setup runs StubPath once in EACH user's context
# at first logon, so every user (SupportUser, ShopFloor, future) gets
# DataFolder=C:\geaofi\. Bump Version to re-push. (Uses Set-ItemProperty in the
# stub, not reg.exe, to avoid the trailing-backslash quoting trap.)
$asKey = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{8F3B2A10-7C4D-4E9A-B1F6-0A9C5D2E7B43}'
try {
New-Item -Path $asKey -Force | Out-Null
Set-ItemProperty -Path $asKey -Name '(default)' -Value 'goCMM DataFolder (per-user)'
Set-ItemProperty -Path $asKey -Name 'Version' -Value '1'
$stub = 'powershell.exe -NoProfile -WindowStyle Hidden -Command "New-Item -Path ''HKCU:\SOFTWARE\General Electric\goCMM'' -Force | Out-Null; Set-ItemProperty -Path ''HKCU:\SOFTWARE\General Electric\goCMM'' -Name DataFolder -Value ''C:\geaofi\''"'
Set-ItemProperty -Path $asKey -Name 'StubPath' -Value $stub
Write-CMMLog "Seeded Active Setup: goCMM DataFolder=C:\geaofi\ (per-user, all users)"
} catch { Write-CMMLog "Failed to seed Active Setup goCMM DataFolder: $_" 'WARN' }
# ============================================================================
# Step 2.6: First-run-as-admin for each installed PC-DMIS version
# ============================================================================
@@ -314,6 +353,28 @@ if (Test-Path -LiteralPath $restoreScript) {
Write-CMMLog "Restore-CMM.ps1 not found at $restoreScript - skipping settings restore" 'WARN'
}
# ============================================================================
# Step 2.5: drop Register-PCDMIS-COM.bat on the Public desktop
# ============================================================================
# PC-DMIS COM must be registered for goCMM to connect, but /regserver no-ops
# until PC-DMIS is licensed - which happens manually post-image (clmadmin.exe).
# So we cannot register at imaging time; instead leave a one-click helper the
# tech runs AFTER activating the license. Public Desktop so it is visible to
# whoever logs in (operator or SupportUser). NOTE: 06-OrganizeDesktop sweeps the
# Public Desktop root into category subfolders, so it may end up under
# "Shopfloor Tools" rather than the bare desktop - still on the Public desktop.
$regBat = Join-Path $stagingRoot 'Register-PCDMIS-COM.bat'
if (Test-Path -LiteralPath $regBat) {
$d = 'C:\Users\Public\Desktop'
try {
if (-not (Test-Path $d)) { New-Item -Path $d -ItemType Directory -Force | Out-Null }
Copy-Item -LiteralPath $regBat -Destination (Join-Path $d 'Register-PCDMIS-COM.bat') -Force
Write-CMMLog "Staged Register-PCDMIS-COM.bat -> $d"
} catch { Write-CMMLog "Failed to stage Register-PCDMIS-COM.bat to ${d}: $_" 'WARN' }
} else {
Write-CMMLog "Register-PCDMIS-COM.bat not in staging ($regBat) - desktop helper NOT placed" 'WARN'
}
# ============================================================================
# Step 3: Conditional cleanup of the bootstrap staging dir
# ============================================================================

View File

@@ -1,48 +1,165 @@
# Install-DODA.ps1 - Extract DODA zip to C:\Apps\DODA\.
# Install-DODA.ps1 - full unattended DODA install (runtimes + app + config + ACLs).
#
# Called by Install-FromManifest as a Type=PS1 entry. The zip is staged
# alongside this script in C:\CMM-Install\ by startnet.cmd.
# The vendor GE_DODA_build5_Installer.exe is a multi-app NSIS bundler. Its /S
# silent mode is BROKEN (a nested sub-install gets a bad package path -> MSI
# error 1619), so it cannot be used for imaging. Each bundled component installs
# cleanly on its own, so we drive them directly here. (VM-validated 2026-06-17.)
#
# Components staged in C:\CMM-Install (robocopied from the SFLD/enrollment share
# by startnet.cmd), all installed SILENTLY:
# python-2.7.16.amd64.msi -> C:\Python27 (msiexec /qn)
# java-1.8.0-openjdk-*.msi -> C:\Program Files\RedHat (msiexec /qn)
# gs927w64.exe -> C:\Program Files\gs\gs9.27 (/S)
# vc_redist.x64.exe -> VC++ runtime (/quiet)
# doda_build*.zip -> C:\Apps\DODA (app files)
#
# DODA at runtime is DovetailAnalysis.exe -> JVM (OpenJDK) + C:\Python27 python +
# Ghostscript (PS->PDF). .settings (NOT in the zip - the NSIS installer wrote it)
# hardcodes those paths; we write it here.
#
# LOCKDOWN: DovetailAnalysis.exe runs as the locked-down OPERATOR (not admin) and
# writes throughout C:\Apps\DODA (.pyc, PreProcess) + $TMP. We (a) grant Users +
# Authenticated Users Full on C:\Apps\DODA and (b) PRECOMPILE the .py to .pyc as
# admin so the operator never needs to write bytecode into a protected path.
#
# Run as administrator / SYSTEM (imaging or GE-Enforce). Idempotent: each step
# detects-and-skips if already present, and ACL/.settings/env re-assert every run
# (so a post-lockdown re-run repairs a stripped ACE).
$ErrorActionPreference = 'Continue'
# The doda zip + runtime installers are staged ALONGSIDE this script (same dir).
# When the manifest runs it, that dir is C:\CMM-Install (= $PSScriptRoot).
$stagingRoot = $PSScriptRoot
if (-not $stagingRoot -or -not (Test-Path $stagingRoot)) { $stagingRoot = 'C:\CMM-Install' }
$installDir = 'C:\Apps\DODA'
$zipPattern = 'doda_build*.zip'
$stagingRoot = Split-Path $PSScriptRoot -ErrorAction SilentlyContinue
if (-not $stagingRoot) { $stagingRoot = 'C:\CMM-Install' }
$python27 = 'C:\Python27\python.exe'
$gsExe = 'C:\Program Files\gs\gs9.27\bin\gswin64c.exe'
$zip = Get-ChildItem -Path $stagingRoot -Filter $zipPattern -File -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $zip) {
Write-Host "DODA zip not found in $stagingRoot (pattern: $zipPattern)"
exit 1
$logDir = 'C:\Logs\CMM'
New-Item -ItemType Directory -Path $logDir -Force -EA SilentlyContinue | Out-Null
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
$log = Join-Path $logDir "doda-install-$ts.log"
function Log($m){ $line = "[{0}] {1}" -f (Get-Date -Format 'HH:mm:ss'), $m; Write-Host $line; Add-Content -Path $log -Value $line -EA SilentlyContinue }
function Find-One($pattern) {
Get-ChildItem -Path $stagingRoot -Filter $pattern -File -EA SilentlyContinue | Select-Object -First 1
}
if (-not (Test-Path $installDir)) {
New-Item -Path $installDir -ItemType Directory -Force | Out-Null
}
Log "==== DODA install on $env:COMPUTERNAME (staging=$stagingRoot) ===="
Write-Host "Extracting $($zip.Name) to $installDir..."
try {
Expand-Archive -LiteralPath $zip.FullName -DestinationPath $installDir -Force -ErrorAction Stop
Write-Host "DODA extracted to $installDir"
} catch {
Write-Host "ERROR: Extract failed - $_"
exit 1
}
# MergeFiles.exe (cmm-utilities toolchain) reads C:\Apps\DODA\PreProcess\ as
# its working dir. The DODA zip extracts flat without it, so create it here -
# a missing PreProcess dir is the known cause of MergeFiles.GetDoDAFolder
# throwing DirectoryNotFoundException (see cmm-utilities dotNET event.txt).
$preProcess = Join-Path $installDir 'PreProcess'
if (-not (Test-Path $preProcess)) {
New-Item -Path $preProcess -ItemType Directory -Force | Out-Null
Write-Host "Created $preProcess"
}
if (Test-Path (Join-Path $installDir 'DovetailAnalysis.exe')) {
Write-Host "DovetailAnalysis.exe verified present"
exit 0
# --- 1. Python 2.7 ----------------------------------------------------------
if (Test-Path $python27) {
Log "Python 2.7 already present - skip"
} else {
Write-Host "ERROR: DovetailAnalysis.exe not found after extract"
$msi = Find-One 'python-2.7*.msi'
if ($msi) {
Log "Installing Python: $($msi.Name)"
$p = Start-Process msiexec.exe -Wait -PassThru -ArgumentList @('/i', "`"$($msi.FullName)`"", '/qn', '/norestart', 'ADDLOCAL=ALL')
Log " Python msiexec exit $($p.ExitCode)"
} else { Log "WARN: python-2.7*.msi not found in $stagingRoot" }
}
# --- 2. OpenJDK 8 -----------------------------------------------------------
$jdk = Get-ChildItem 'C:\Program Files\RedHat','C:\Program Files\Java' -Directory -EA SilentlyContinue |
Where-Object Name -match 'openjdk|jdk|jre' | Select-Object -First 1
if ($jdk) {
Log "OpenJDK already present ($($jdk.Name)) - skip"
} else {
$msi = Find-One 'java-*openjdk*.msi'
if ($msi) {
Log "Installing OpenJDK: $($msi.Name)"
$p = Start-Process msiexec.exe -Wait -PassThru -ArgumentList @('/i', "`"$($msi.FullName)`"", '/qn', '/norestart')
Log " OpenJDK msiexec exit $($p.ExitCode)"
$jdk = Get-ChildItem 'C:\Program Files\RedHat','C:\Program Files\Java' -Directory -EA SilentlyContinue |
Where-Object Name -match 'openjdk|jdk|jre' | Select-Object -First 1
} else { Log "WARN: java-*openjdk*.msi not found in $stagingRoot" }
}
# --- 3. Ghostscript 9.27 ----------------------------------------------------
if (Test-Path $gsExe) {
Log "Ghostscript 9.27 already present - skip"
} else {
$gs = Find-One 'gs927w64.exe'
if ($gs) {
Log "Installing Ghostscript: $($gs.Name)"
$p = Start-Process $gs.FullName -Wait -PassThru -ArgumentList '/S'
Log " Ghostscript exit $($p.ExitCode)"
} else { Log "WARN: gs927w64.exe not found in $stagingRoot" }
}
# --- 4. VC++ redist (best-effort) ------------------------------------------
$vc = Find-One 'vc_redist.x64.exe'
if ($vc) {
Log "Installing VC++ redist: $($vc.Name)"
$p = Start-Process $vc.FullName -Wait -PassThru -ArgumentList @('/quiet', '/norestart')
Log " VC++ exit $($p.ExitCode) (1638/3010 = already-present/ok)"
} else { Log "VC++ redist not staged - skipping (usually already present)" }
# --- 5. DODA app files ------------------------------------------------------
$zip = Find-One 'doda_build*.zip'
if (-not $zip) {
Log "ERROR: doda_build*.zip not found in $stagingRoot - cannot place DODA app"
} else {
New-Item -ItemType Directory -Path $installDir -Force -EA SilentlyContinue | Out-Null
Log "Extracting $($zip.Name) -> $installDir"
try { Expand-Archive -LiteralPath $zip.FullName -DestinationPath $installDir -Force -EA Stop }
catch { Log "ERROR: extract failed - $_" }
}
# --- 6. .settings (the NSIS installer wrote this; the zip does NOT contain it) ---
$settings = Join-Path $installDir '.settings'
$settingsBody = @'
WORKING_DIR=$TMP
PYTHONEXE=C:\Python27\python.exe
OPTIONS_FILE="$WORKING_DIR/dvt_run_options.py"
RPT_GUI=$PYTHONEXE $AFDA_INSTALL_DIR/dvtRptGui.py -file=$OPTIONS_FILE
OUTPUT_PROCESSOR=$PYTHONEXE $AFDA_INSTALL_DIR/processOutput.py $OPTIONS_FILE
PS2PDF="C:\Program Files\gs\gs9.27\bin\gswin64c.exe" -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$$out $$in
'@
if (Test-Path $installDir) {
Set-Content -Path $settings -Value $settingsBody -Encoding ascii -Force
Log "Wrote $settings"
}
# --- 7. PreProcess dir (MergeFiles GetDoDAFolder reads it) ------------------
$preProcess = Join-Path $installDir 'PreProcess'
if (-not (Test-Path $preProcess)) { New-Item -ItemType Directory -Path $preProcess -Force | Out-Null; Log "Created $preProcess" }
# --- 8. Env: AFDA_INSTALL_DIR + Java on PATH (machine scope) ----------------
[Environment]::SetEnvironmentVariable('AFDA_INSTALL_DIR', $installDir, 'Machine')
Log "Set AFDA_INSTALL_DIR=$installDir (Machine)"
if ($jdk) {
$machPath = [Environment]::GetEnvironmentVariable('PATH','Machine')
foreach ($add in @((Join-Path $jdk.FullName 'bin'), (Join-Path $jdk.FullName 'jre\bin'))) {
if (Test-Path $add) {
$parts = $machPath -split ';'
if ($parts -notcontains $add) { $machPath = "$machPath;$add"; Log "PATH += $add" }
}
}
[Environment]::SetEnvironmentVariable('PATH', $machPath, 'Machine')
}
# --- 9. Precompile .py -> .pyc as admin (so the locked-down operator never
# needs write access to compile bytecode at runtime) ------------------
if ((Test-Path $python27) -and (Test-Path $installDir)) {
Log "Precompiling DODA .py with $python27"
$p = Start-Process $python27 -Wait -PassThru -ArgumentList @('-m','compileall', "`"$installDir`"") -WindowStyle Hidden
Log " compileall exit $($p.ExitCode)"
}
# --- 10. ACLs: Users + Authenticated Users Full on C:\Apps\DODA so the
# locked-down operator can run DODA (writes .pyc/PreProcess/output) ---
if (Test-Path $installDir) {
foreach ($sid in '*S-1-5-32-545','*S-1-5-11') { # BUILTIN\Users, NT AUTHORITY\Authenticated Users
& icacls $installDir /grant "${sid}:(OI)(CI)F" /T /C 2>&1 | Out-Null
}
Log "Granted Users + Authenticated Users Full on $installDir (recursive)"
}
# --- Verify -----------------------------------------------------------------
$ok = (Test-Path (Join-Path $installDir 'DovetailAnalysis.exe')) -and (Test-Path $python27) -and (Test-Path $gsExe) -and $jdk
if ($ok) { Log "==== DODA install OK (DovetailAnalysis + Python27 + Ghostscript + OpenJDK present) ===="; exit 0 }
else {
Log "==== DODA install INCOMPLETE - DovetailAnalysis=$([bool](Test-Path (Join-Path $installDir 'DovetailAnalysis.exe'))) Python=$([bool](Test-Path $python27)) GS=$([bool](Test-Path $gsExe)) JDK=$([bool]$jdk) ===="
exit 1
}

View File

@@ -0,0 +1,57 @@
@echo off
REM ============================================================================
REM Register-PCDMIS-COM.bat - register the PC-DMIS COM / automation server.
REM
REM WHY: goCMM connects to PC-DMIS over COM. If the COM server is not
REM registered, goCMM throws "Object reference not set to an instance of an
REM object" at startup (it calls Type.GetTypeFromProgID on the PC-DMIS ProgID,
REM gets null, then Activator.CreateInstance(null) -> ArgumentNullException).
REM
REM IMPORTANT: PC-DMIS must be LICENSED FIRST. /regserver does NOT register the
REM COM server until a valid license is present. Activate the license with
REM clmadmin.exe, THEN run this. Safe to re-run any time goCMM stops connecting.
REM
REM Needs admin - this script self-elevates via UAC.
REM ============================================================================
REM --- self-elevate if not already running as admin ---
net session >nul 2>&1
if %errorlevel% neq 0 (
echo Requesting administrator rights...
powershell -NoProfile -Command "Start-Process -FilePath '%~f0' -Verb RunAs"
exit /b
)
echo ================================================================
echo Register PC-DMIS COM server (run AFTER licensing PC-DMIS)
echo ================================================================
echo.
setlocal enabledelayedexpansion
set "FOUND="
for %%D in (
"C:\Program Files\Hexagon"
"C:\Program Files (x86)\Hexagon"
"C:\Program Files\WAI"
"C:\Program Files (x86)\WAI"
) do (
if exist %%~D (
for /f "delims=" %%F in ('dir /b /s "%%~D\Pcdlrn.exe" 2^>nul') do (
set "FOUND=1"
echo Registering: %%F
"%%F" /regserver
if !errorlevel! equ 0 (echo OK) else (echo regserver returned !errorlevel!)
)
)
)
echo.
if not defined FOUND (
echo ERROR: Pcdlrn.exe not found. Is PC-DMIS installed?
) else (
echo Done.
echo If goCMM still errors, confirm PC-DMIS is LICENSED ^(clmadmin.exe^) and
echo that you ran this AFTER activating the license, then re-run.
)
echo.
pause

View File

@@ -2,12 +2,12 @@ cmm_id,pcdmis_version,doda,part_group
CMM1,2019,no,S:\CMM\CMM1\HPTCMM1
CMM2,2019,no,S:\CMM\CMM2\HPT
CMM3,2019,no,S:\CMM\CMM3\VENTURE_CMM3
CMM4,2016,no,S:\CMM\CMM4\Spool
CMM4,2016,yes,S:\CMM\CMM4\Spool
CMM5,2019,no,S:\CMM\CMM5\BLISKCMM5
CMM6,2019,no,S:\CMM\CMM6\BLISKCMM6
CMM7,2019,no,S:\CMM\CMM7\VENTURE_CMM7
CMM8,2019,no,S:\CMM\CMM8\Venture CMM8
CMM9,2019,no,S:\CMM\CMM9\BLISKCMM9
CMM10,2016,no,S:\CMM\CMM10\Spool
CMM11,2026,no,S:\CMM\CMM11\Spool
CMM12,2026,no,S:\CMM\CMM12\Spool
CMM10,2016,yes,S:\CMM\CMM10\Spool
CMM11,2026,yes,S:\CMM\CMM11\Spool
CMM12,2026,yes,S:\CMM\CMM12\Spool
1 cmm_id pcdmis_version doda part_group
2 CMM1 2019 no S:\CMM\CMM1\HPTCMM1
3 CMM2 2019 no S:\CMM\CMM2\HPT
4 CMM3 2019 no S:\CMM\CMM3\VENTURE_CMM3
5 CMM4 2016 no yes S:\CMM\CMM4\Spool
6 CMM5 2019 no S:\CMM\CMM5\BLISKCMM5
7 CMM6 2019 no S:\CMM\CMM6\BLISKCMM6
8 CMM7 2019 no S:\CMM\CMM7\VENTURE_CMM7
9 CMM8 2019 no S:\CMM\CMM8\Venture CMM8
10 CMM9 2019 no S:\CMM\CMM9\BLISKCMM9
11 CMM10 2016 no yes S:\CMM\CMM10\Spool
12 CMM11 2026 no yes S:\CMM\CMM11\Spool
13 CMM12 2026 no yes S:\CMM\CMM12\Spool

View File

@@ -57,19 +57,18 @@
"DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{a55fecde-0776-474e-a5b3-d57ea93d6a9f}"
},
{
"_comment": "goCMM - Hexagon CMM job launcher. Install for all versions.",
"Name": "goCMM",
"Installer": "goCMM_1.1.6718.31289.exe",
"_comment": "goCMM 2.12.3 - Hexagon/Renishaw CMM job launcher. NSIS installer; silent switch is /S (NOT /quiet - that was the WiX 1.1 bundle). Installs to C:\\Program Files\\goCMM and runs no-admin (asInvoker, VM-verified). This is the IMAGING manifest only, so NEW images get 2.12; the tsgwp00525 SFLD enforce manifest stays on goCMM 1.1 so already-deployed bays are NOT auto-upgraded. Install for all versions.",
"Name": "goCMM 2.12.3",
"Installer": "goCMM_2.12.3.0_Release.exe",
"Type": "EXE",
"InstallArgs": "/quiet /norestart /log \"C:\\Logs\\CMM\\goCMM.log\"",
"LogFile": "C:\\Logs\\CMM\\goCMM.log",
"DetectionMethod": "Registry",
"DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{94f02b85-bbca-422e-9b8b-0c16a769eced}"
"InstallArgs": "/S",
"DetectionMethod": "File",
"DetectionPath": "C:\\Program Files\\goCMM\\goCMM.exe"
},
{
"_comment": "DODA - Dovetail Digital Analysis. Deployed as a flat file extract to C:\\Apps\\DODA\\. Only installed when doda.txt=yes (pc-subtype.txt=doda gates this via PCTypes).",
"_comment": "DODA - Dovetail Digital Analysis. Deployed as a flat file extract to C:\\Apps\\DODA\\. Only installed when doda.txt=yes -> startnet writes pc-subtype.txt=doda -> 09-Setup passes PCType=gea-shopfloor-cmm + PCSubType=doda -> matcher builds 'gea-shopfloor-cmm-doda'. PCTypes MUST be that exact Type-SubType string ('cmm-doda' never matched - the alias of gea-shopfloor-cmm is bare 'CMM', no subtype variant).",
"Name": "DODA",
"PCTypes": ["cmm-doda"],
"PCTypes": ["gea-shopfloor-cmm-doda"],
"Type": "PS1",
"Script": "Install-DODA.ps1",
"DetectionMethod": "File",

View File

@@ -63,6 +63,30 @@ foreach ($s in 'Backup-goCMMSettings.ps1','Backup-PCDMISSettings.ps1') {
catch { Log " ERROR in $s : $($_.Exception.Message)" }
}
# --- C:\Program Files\WAI (+ x86): whole vendor tree, captured complete.
# Holds machine/controller content beyond what the per-version PC-DMIS backup
# grabs. NOTE: can be multi-GB if WAI contains the full PC-DMIS 2016 install. ---
$waiRoots = @("$env:ProgramFiles\WAI", "${env:ProgramFiles(x86)}\WAI")
$waiPresent = @($waiRoots | Where-Object { Test-Path $_ })
if ($waiPresent.Count -gt 0) {
Log "---- capturing WAI tree (whole) ----"
$waiStage = Join-Path $env:TEMP "wai-bk-$ts"
New-Item -ItemType Directory -Path $waiStage -Force | Out-Null
foreach ($wr in $waiPresent) {
$label = if ($wr -like '*(x86)*') { 'WAI-x86' } else { 'WAI' }
robocopy $wr (Join-Path $waiStage $label) /E /R:1 /W:1 /NFL /NDL /NJH /NJS | Out-Null
Log " copied $wr -> $label"
}
$waiZip = Join-Path $dest "wai_backup_${env:COMPUTERNAME}_$ts.zip"
if (Test-Path $waiZip) { Remove-Item $waiZip -Force }
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory($waiStage, $waiZip)
Remove-Item $waiStage -Recurse -Force -EA SilentlyContinue
Log " WAI backup: $waiZip ($([math]::Round((Get-Item $waiZip).Length/1MB)) MB)"
} else {
Log "No C:\Program Files\WAI (or x86) - skipping WAI backup"
}
# index of what we captured
$zips = Get-ChildItem $dest -Filter '*.zip' -File -ErrorAction SilentlyContinue
[pscustomobject]@{
@@ -71,6 +95,7 @@ $zips = Get-ChildItem $dest -Filter '*.zip' -File -ErrorAction SilentlyContinue
Timestamp = (Get-Date -Format o)
goCMM = @($zips | Where-Object { $_.Name -like 'gocmm_backup_*' } | Select-Object -Expand Name)
PCDMIS = @($zips | Where-Object { $_.Name -like 'pcdmis_backup_*' } | Select-Object -Expand Name)
WAI = @($zips | Where-Object { $_.Name -like 'wai_backup_*' } | Select-Object -Expand Name)
} | ConvertTo-Json | Out-File (Join-Path $dest 'cmm-backup-index.json') -Encoding ascii
Log "================ DONE ================"

View File

@@ -0,0 +1,195 @@
<#
Convert-goCMMSettings.ps1
Convert a legacy goCMM 1.1 ApplicationSettings.xml (C:\geaofi\) to the goCMM
2.12.3 goCMMSettings.xml schema.
The two are different SettingsModel schemas (different filename too). This is a
TEMPLATE-based converter: new-only fields get defaults, the per-bay values are
carried over from the old XML (+ optional overrides). Decisions baked in
(2026-06-19): access-control left blank, operator key normalized to "Operator",
cal intervals/restart use template defaults (old values NOT carried), Slack/Andon
/Quindos/Emx dropped.
Program paths default to the goCMM 2.12 convention (C:\geaofi\goCMM PC-DMIS
Programs\) - override with -ResetProgram / -ProbeCalibrationProgram if the .PRG
files live elsewhere. Those .PRG files MUST exist at the path written here.
Usage:
.\Convert-goCMMSettings.ps1 -OldXml C:\geaofi\ApplicationSettings.xml `
-OutFile C:\geaofi\goCMMSettings.xml -PcdmisVersion 2016 -CmmId CMM10
#>
param(
[Parameter(Mandatory=$true)][string]$OldXml,
[Parameter(Mandatory=$true)][string]$OutFile,
[string]$PcdmisVersion, # 2016 | 2019 | 2026 (or full "PC-DMIS ... 64-bit")
[string]$CmmId, # e.g. CMM10 - used for the cal-program name default
[string]$PartGroup, # optional override (friendly S:\ or UNC); else from old XML
[string]$ResetProgram, # optional override
[string]$ProbeCalibrationProgram # optional override
)
$ErrorActionPreference = 'Stop'
if (-not (Test-Path -LiteralPath $OldXml)) { throw "Old XML not found: $OldXml" }
[xml]$old = Get-Content -LiteralPath $OldXml -Raw
# --- part-group share canonicalization (FQDN + \SHARED, matches goCMM 2.12) ---
function Canon([string]$pg) {
if (-not $pg) { return $pg }
$pg = $pg -replace '(?i)^S:\\', '\\tsgwp00525.wjs.geaerospace.net\SHARED\'
$pg = [regex]::Replace($pg, '(?i)\\\\tsgwp00525(?:\.[A-Za-z0-9.\-]+)?\\shared(?=\\|$)', '\\tsgwp00525.wjs.geaerospace.net\SHARED')
return $pg
}
# --- resolve per-bay values (override -> old XML -> default) ---
# NOTE: read attributes with GetAttribute() - .Name collides with XmlElement.Name.
$pg = $PartGroup
if (-not $pg) {
$pgNode = $old.SettingsModel.PartGroups.PartGroup
if ($pgNode) { $pg = $pgNode.GetAttribute('FullName'); if (-not $pg) { $pg = $pgNode.FullName } }
}
$pg = Canon $pg
# NB: PowerShell vars are case-insensitive - do NOT name this $cmmid (collides with the $CmmId param).
$machineCmmId = $old.SettingsModel.MachineDefinition.CMMID
if (-not $machineCmmId -and $CmmId) { $machineCmmId = "WJRP/$CmmId" }
# SelectedVersionString from -PcdmisVersion
$verMap = @{ '2016' = 'PC-DMIS 2016.0 64-bit'; '2019' = 'PC-DMIS 2019 R2 64-bit'; '2026' = 'PC-DMIS 2026.1 64-bit' }
$selVer = ''
if ($PcdmisVersion) { $selVer = if ($verMap.ContainsKey($PcdmisVersion)) { $verMap[$PcdmisVersion] } else { $PcdmisVersion } }
# bool carryovers (old flat flags -> new IsEnabled)
function OldBool($node, $default='false') { $v = $node; if ($null -eq $v -or $v -eq '') { $default } else { ([string]$v).ToLower() } }
$pcdmisEnabled = OldBool $old.SettingsModel.UsingPcDmis 'true'
$modusEnabled = OldBool $old.SettingsModel.UsingModus 'false'
$winState = if ($old.SettingsModel.DefaultWindowState) { $old.SettingsModel.DefaultWindowState } else { 'Maximized' }
$allowSel = OldBool $old.SettingsModel.AllowOperationProgramSelection 'true'
$visExec = OldBool $old.SettingsModel.PcDmisSettings.VisibleDuringExecution 'true'
$cmmMode = if ($old.SettingsModel.PcDmisSettings.CMMMode) { $old.SettingsModel.PcDmisSettings.CMMMode } else { 'Online' }
$sizeTol = if ($old.SettingsModel.MachineDefinition.ProbeCalSizeTol) { $old.SettingsModel.MachineDefinition.ProbeCalSizeTol } else { '0.0005' }
$formTol = if ($old.SettingsModel.MachineDefinition.ProbeCalFormTol) { $old.SettingsModel.MachineDefinition.ProbeCalFormTol } else { '0.0005' }
$hasRotary = OldBool $old.SettingsModel.MachineDefinition.MachineHasRotaryTable 'false'
# program paths (override -> goCMM 2.12 convention default)
$progDir = 'C:\geaofi\goCMM PC-DMIS Programs'
if (-not $ResetProgram) { $ResetProgram = "$progDir\MachineResetProgram.prg" }
if (-not $ProbeCalibrationProgram) {
$calName = if ($CmmId) { "${CmmId}_Cal_All_Probes_rev2.PRG" } else { 'Cal_All_Probes_rev2.PRG' }
$ProbeCalibrationProgram = "$progDir\$calName"
}
# --- operator inputs (old KeyLabel + NonTBIInputs -> new OperatorInput; OPER -> Operator) ---
$inputs = @()
$kl = $old.SettingsModel.OperatorInputs.KeyLabel
if ($kl) { $inputs += [pscustomobject]@{ Key = $kl.GetAttribute('Name'); Display = $kl.GetAttribute('Display') } }
foreach ($oi in $old.SettingsModel.OperatorInputs.NonTBIInputs.OperatorInput) {
if ($oi -is [System.Xml.XmlElement]) { $inputs += [pscustomobject]@{ Key = $oi.GetAttribute('Name'); Display = $oi.GetAttribute('Display') } }
}
if (-not $inputs) {
$inputs = @([pscustomobject]@{Key='SERNO';Display='Serial Number'}, [pscustomobject]@{Key='Operator';Display='Operator'})
}
$opXml = ($inputs | ForEach-Object {
$k = if ($_.Key -ieq 'OPER') { 'Operator' } else { $_.Key } # decision #4
$d = if ($_.Key -ieq 'OPER') { 'Operator' } else { $_.Display }
" <OperatorInput ClearInput=`"true`">`n <Key>$k</Key>`n <DisplayName>$d</DisplayName>`n <ValidationRules />`n </OperatorInput>"
}) -join "`n"
function X([string]$s){ [System.Security.SecurityElement]::Escape($s) }
$xml = @"
<?xml version="1.0" encoding="utf-8"?>
<SettingsModel xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" LockInterface="false" AdminGroupName="" PowerUsersGroupName="" PasswordHash="" PasswordSalt="" LockMachineReset="false">
<MaintainFolderStructureWhenCopying>true</MaintainFolderStructureWhenCopying>
<DefaultSoftware>PCDMIS</DefaultSoftware>
<PartGroups>
<PartGroup>
<FullName>$(X $pg)</FullName>
<Children />
</PartGroup>
</PartGroups>
<OperatorInputs>
$opXml
</OperatorInputs>
<UseBladeGrid>false</UseBladeGrid>
<DefaultWindowState>$(X $winState)</DefaultWindowState>
<AllowOperationProgramSelection>$allowSel</AllowOperationProgramSelection>
<BladesDatFile />
<PcDmisSettings>
<IsEnabled>$pcdmisEnabled</IsEnabled>
<VisibleDuringExecution>$visExec</VisibleDuringExecution>
<ResetProgram>$(X $ResetProgram)</ResetProgram>
<RotaryTableCalibrationProgram />
<TrackProbeCalibration>false</TrackProbeCalibration>
<TrackRotaryTableCalbiration>false</TrackRotaryTableCalbiration>
<ProbeCalibrationProgram>$(X $ProbeCalibrationProgram)</ProbeCalibrationProgram>
<RotaryTableCalibration />
<IPPServerExe />
<IPPArguments />
<SaveAndClose>false</SaveAndClose>
<DelayMeasurementRoutines>false</DelayMeasurementRoutines>
<MeasurementRoutineDelay>1000</MeasurementRoutineDelay>
<Restart>true</Restart>
<RestartMode>ProgramCount</RestartMode>
<UseIPPServer>false</UseIPPServer>
<SelectedVersionString>$(X $selVer)</SelectedVersionString>
<NumberOfProgramsBeforeRestart>5</NumberOfProgramsBeforeRestart>
<RestartDelay>5</RestartDelay>
<StartupDelay>0</StartupDelay>
<UseOtherSoftwareForRotaryCal>false</UseOtherSoftwareForRotaryCal>
<UseOtherSoftwareForProbeCalibration>false</UseOtherSoftwareForProbeCalibration>
<RotaryCalSoftware />
<ProbeCalSoftware />
</PcDmisSettings>
<ModusSettings>
<IsEnabled>$modusEnabled</IsEnabled>
<VisibleDuringExecution>true</VisibleDuringExecution>
<ResetProgram />
<RotaryTableCalibrationProgram />
<TrackProbeCalibration>false</TrackProbeCalibration>
<TrackRotaryTableCalbiration>false</TrackRotaryTableCalbiration>
<ProbeCalibrationProgram />
<RotaryTableCalibration />
<IPPServerExe />
<IPPArguments />
<ModusArguments>-Organiser</ModusArguments>
</ModusSettings>
<MachineDefinition>
<CMMID>$(X $machineCmmId)</CMMID>
<MachineType />
<HeadType />
<ForceProbeCalibration>false</ForceProbeCalibration>
<ProbeCalSizeTol>$sizeTol</ProbeCalSizeTol>
<ProbeCalFormTolerance>$formTol</ProbeCalFormTolerance>
<ProbeCalibrationInterval>
<Hours>72</Hours>
<Minutes>0</Minutes>
</ProbeCalibrationInterval>
<RotaryTableCalibrationInterval>
<Hours>24</Hours>
<Minutes>0</Minutes>
</RotaryTableCalibrationInterval>
<MachineHasRotary>$hasRotary</MachineHasRotary>
<ForceRotaryCalibration>false</ForceRotaryCalibration>
<CMMMode>$(X $cmmMode)</CMMMode>
</MachineDefinition>
<Notifications UseAndon="false">
<Notifications />
</Notifications>
<ProgramConstants />
<CommonFiles />
</SettingsModel>
"@
$dir = Split-Path -Parent $OutFile
if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
# validate well-formed before writing
[void][xml]$xml
Set-Content -LiteralPath $OutFile -Value $xml -Encoding UTF8
Write-Host "Wrote $OutFile"
Write-Host " PartGroup = $pg"
Write-Host " CMMID = $machineCmmId"
Write-Host " SelectedVersion = $selVer"
Write-Host " ResetProgram = $ResetProgram"
Write-Host " ProbeCalProgram = $ProbeCalibrationProgram"
Write-Host "NOTE: confirm those .PRG files exist at the paths above (-ResetProgram / -ProbeCalibrationProgram to override)."

View File

@@ -0,0 +1,21 @@
@echo off
REM Ensure-PCDMISFrontEnd.bat - launcher for Ensure-PCDMISFrontEnd.ps1
REM
REM Creates C:\GE PC-DMIS FRONT END + grants the operator write access, so
REM PCDToIGES.exe can write its error log there instead of crashing with
REM "Could not find a part of the path 'C:\GE PC-DMIS FRONT END\PCDToIGES.ERR'".
REM
REM Needs admin - self-elevates.
net session >nul 2>&1
if %errorlevel% neq 0 (
echo Requesting administrator rights...
powershell -NoProfile -Command "Start-Process -FilePath '%~f0' -Verb RunAs"
exit /b
)
set "HERE=%~dp0"
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%HERE%Ensure-PCDMISFrontEnd.ps1"
echo.
pause

View File

@@ -0,0 +1,47 @@
# Ensure-PCDMISFrontEnd.ps1 - create C:\GE PC-DMIS FRONT END + grant operator write.
#
# WHY: PCDToIGES.exe (geaofi\Scripts) writes its error log to
# C:\GE PC-DMIS FRONT END\PCDToIGES.ERR in its catch block. If that dir does
# not exist, ANY PCDToIGES failure becomes an unhandled DirectoryNotFoundException
# that CRASHES the export and MASKS the real error (observed on freshly imaged CMM
# bays - the legacy "GE PC-DMIS FRONT END" front-end setup is not part of imaging,
# so the dir is absent). Creating it + making it operator-writable lets the real
# error log instead of crashing, and gives the front-end a working dir.
#
# Run as administrator. Idempotent - safe to re-run; re-asserts the ACL.
$ErrorActionPreference = 'Continue'
$dir = 'C:\GE PC-DMIS FRONT END'
$logDir = 'C:\Logs\CMM'
New-Item -ItemType Directory -Path $logDir -Force -EA SilentlyContinue | Out-Null
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
$log = Join-Path $logDir "pcdmis-frontend-$ts.log"
function Log($m){ $line = "[{0}] {1}" -f (Get-Date -Format 'HH:mm:ss'), $m; Write-Host $line; Add-Content -Path $log -Value $line -EA SilentlyContinue }
Log "==== Ensure-PCDMISFrontEnd on $env:COMPUTERNAME ===="
if (Test-Path -LiteralPath $dir) {
Log "Dir already exists: $dir"
} else {
try { New-Item -ItemType Directory -Path $dir -Force -EA Stop | Out-Null; Log "Created $dir" }
catch { Log "ERROR: could not create $dir - $($_.Exception.Message)"; exit 1 }
}
# Operator (locked-down, non-admin) must be able to write PCDToIGES.ERR here.
# Modify = read/write/create/delete. SIDs, not names, to stay locale-independent.
foreach ($sid in '*S-1-5-32-545','*S-1-5-11') { # BUILTIN\Users, NT AUTHORITY\Authenticated Users
& icacls "$dir" /grant "${sid}:(OI)(CI)M" /T /C 2>&1 | Out-Null
}
Log "Granted Users + Authenticated Users Modify on $dir"
# Verify an operator-style write works
$probe = Join-Path $dir '.write-test'
try { Set-Content -LiteralPath $probe -Value 'ok' -EA Stop; Remove-Item -LiteralPath $probe -EA SilentlyContinue; Log "Write-test OK" }
catch { Log "WARN: write-test failed - $($_.Exception.Message)" }
Log "==== DONE ===="
Write-Host ""
Write-Host "C:\GE PC-DMIS FRONT END ready. Re-run the measurement; if PCDToIGES still" -ForegroundColor Green
Write-Host "errors, the real cause now logs to $dir\PCDToIGES.ERR" -ForegroundColor Green
exit 0

View File

@@ -11,8 +11,10 @@ resolve-cmm-bay-config.ps1 at the WinPE picker):
version it ever had (2016.0 + 2019 R2); we restore ONLY the
one the bay-config pins, matched by substring (2019 -> the
'2019 R2' zip, 2016 -> the '2016.0' zip).
doda.txt - 'no' to proceed; anything else SKIPS restore (DODA bays are
handled separately and must not get a settings overlay).
doda.txt - read for logging only. DODA bays now restore like any other:
once they are properly backed up + staged, the config-version
zip is restored the same way. (The old skip-on-doda policy was
dropped 2026-06-17; DODA bays had no backups when it was added.)
partgroup.txt - optional goCMM Selected Part Group (friendly S:\ form), used
as the authoritative per-bay override even if the backup is stale.
@@ -50,7 +52,9 @@ $doda = (ReadTxt 'doda.txt').ToLower()
$pgRaw = ReadTxt 'partgroup.txt'
if (-not $cmmid) { Log "no cmmid.txt (manual CMM id, or not a bay-config bay) - nothing to restore. Skipping."; exit 0 }
if ($doda -eq 'yes') { Log "DODA bay ($cmmid) - skipping settings restore by policy."; exit 0 }
# DODA bays restore like any other now (skip-on-doda dropped 2026-06-17). DODA
# itself installs separately via the manifest to C:\Apps\DODA - independent of
# the goCMM (C:\geaofi) + PC-DMIS settings this restore lays back.
$bdir = Join-Path $BackupRoot $cmmid
if (-not (Test-Path -LiteralPath $bdir)) { Log "no staged backup at $bdir - skipping (stage it with sync-cmm-backups.sh)."; exit 0 }

View File

@@ -4,9 +4,17 @@ REM
REM *** RUN THIS AS THE OPERATOR (the locked-down shop-floor user). ***
REM *** DO NOT right-click "Run as administrator" - that hides the bug. ***
REM
REM Reproduces the goCMM "Requested registry access is not allowed" error and
REM dumps the goCMM registry key's ACL so we can confirm the lockdown stripped
REM the BUILTIN\Users write grant. Output lands in C:\Logs\CMM\.
REM Diagnoses goCMM startup failures:
REM 1. "Requested registry access is not allowed" - dumps the goCMM key ACL
REM to confirm lockdown stripped the BUILTIN\Users write grant (PROBE 1-2).
REM 2. "Object reference not set to an instance of an object" at start:
REM - PROBE 3: part-group match (reg vs ApplicationSettings.xml, case-sensitive).
REM - PROBE 4: pulls the actual .NET crash stack from the Application log.
REM - PROBE 5: part-group UNC reachability + PC-DMIS COM registration.
REM Confirmed root cause to date: PC-DMIS COM server NOT registered ->
REM goCMM's Activator.CreateInstance gets a null type -> NRE. Fix: run
REM Pcdlrn.exe once elevated (/regserver). PROBE 5 reports this directly.
REM Output lands in C:\Logs\CMM\.
setlocal
set "HERE=%~dp0"

View File

@@ -93,14 +93,155 @@ W " exported -> $regOut"
W ""
W "================ goCMM version + install ================"
# The goCMM MSI installs goCMM.exe (1.1.6718.x) to this dir and points the Start
# Menu shortcut at it - goCMM.exe IS the launcher. (An earlier version of this
# script checked for GEAOperatorFriendlyInterface.exe, which this product does
# NOT install - that produced a false MISSING. Check the real exe.)
foreach ($p in @(
'C:\Program Files (x86)\General Electric\goCMM\GEAOperatorFriendlyInterface.exe',
'C:\Program Files (x86)\General Electric\goCMM\goCMM.exe',
'C:\Program Files (x86)\General Electric\goCMM\GEA_OFI_Common.dll')) {
if (Test-Path $p) { $vi = (Get-Item $p).VersionInfo; W (" {0} FileVer={1} ProductVer={2}" -f (Split-Path $p -Leaf), $vi.FileVersion, $vi.ProductVersion) }
else { W " MISSING: $p" }
}
W ""
W "================ PROBE 3: Selected Part Group vs ApplicationSettings.xml (the startup NRE) ================"
# goCMM at start matches the registry 'Selected Part Group' against the
# <PartGroup FullName> entries in ApplicationSettings.xml with a CASE-SENSITIVE
# (Ordinal) compare. No match -> SelectedPartGroup is null -> a startup deref
# throws "Object reference not set to an instance of an object". This is a
# DIFFERENT failure from the registry SecurityException probed above.
# Mismatch forms seen: host (bare vs FQDN) and share segment (\shared vs \SHARED).
try {
$base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32')
$kpg = $base32.OpenSubKey($key32native, $false)
$regPg = $null; $sharedDir = 'C:\geaofi'
if ($kpg) {
$regPg = [string]$kpg.GetValue('Selected Part Group','')
$sd = [string]$kpg.GetValue('Shared Data Directory','')
if ($sd) { $sharedDir = $sd.TrimEnd('\') }
$kpg.Close()
}
W (" reg 'Selected Part Group' = [{0}]" -f $regPg)
W (" Shared Data Directory = {0}" -f $sharedDir)
$xml = Join-Path $sharedDir 'ApplicationSettings.xml'
if (-not (Test-Path $xml)) {
W " *** ApplicationSettings.xml NOT FOUND at $xml - no part groups to match -> null SelectedPartGroup -> NRE likely. <<"
} else {
$txt = [System.IO.File]::ReadAllText($xml)
# Capture FullName in both attribute (FullName="...") and element (<FullName>...</FullName>) forms.
$names = New-Object System.Collections.Generic.List[string]
foreach ($m in [regex]::Matches($txt, 'FullName\s*=\s*"([^"]*)"')) { $names.Add($m.Groups[1].Value) }
foreach ($m in [regex]::Matches($txt, '<FullName>([^<]*)</FullName>')) { $names.Add($m.Groups[1].Value) }
$names = $names | Select-Object -Unique
W (" ApplicationSettings.xml part-group FullName entries ({0}):" -f @($names).Count)
foreach ($n in $names) { W (" [{0}]" -f $n) }
if (-not $regPg) {
W " >> reg 'Selected Part Group' is EMPTY - goCMM has no part group pinned -> null -> NRE likely. <<"
} else {
$exact = $false; $caseOnly = $false
foreach ($n in $names) {
if ([string]::Equals($n, $regPg, [StringComparison]::Ordinal)) { $exact = $true; break }
if ([string]::Equals($n, $regPg, [StringComparison]::OrdinalIgnoreCase)) { $caseOnly = $true }
}
if ($exact) {
W " >> MATCH: reg value matches an XML FullName exactly (case-sensitive). Part group is NOT the NRE cause. <<"
} elseif ($caseOnly) {
W " >> *** CASE-ONLY MATCH *** reg value matches an XML entry only when case is ignored."
W " >> goCMM's compare is case-sensitive -> Find returns null -> SelectedPartGroup null -> NRE. <<"
W " >> FIX: re-run Install-goCMMSettings.ps1 (>= commit d441abd canonicalizes host + \SHARED case). <<"
} else {
W " >> *** NO MATCH *** reg 'Selected Part Group' is absent from ApplicationSettings.xml (any case)."
W " >> Find returns null -> SelectedPartGroup null -> NRE. Wrong per-bay override, or XML from a different bay. <<"
}
}
}
} catch { W " ERROR in part-group probe: $($_.Exception.Message)" }
W ""
W "================ PROBE 4: goCMM crash events (Application log) - the actual stack ================"
# The NRE reproduces even for admin, so it is NOT a rights problem. The faulting
# stack names the null object. .NET pushes it to the Application log under
# '.NET Runtime' / 'Application Error' / 'Windows Error Reporting'.
try {
$since = (Get-Date).AddDays(-14)
$ev = Get-WinEvent -FilterHashtable @{ LogName='Application'; StartTime=$since } -ErrorAction SilentlyContinue |
Where-Object { $_.ProviderName -match '\.NET Runtime|Application Error|Windows Error Reporting|Application Hang' -and "$($_.Message)" -match 'goCMM' }
if (-not $ev) {
W " (no goCMM-related crash events in the last 14 days - launch goCMM once, then re-run this)"
} else {
foreach ($e in ($ev | Select-Object -First 5)) {
W (" ---- {0} [{1}] EventId={2} ----" -f $e.TimeCreated, $e.ProviderName, $e.Id)
foreach ($line in ("$($e.Message)" -split "`r?`n")) { W (" " + $line) }
W ""
}
W " >> The topmost goCMM / GEA_OFI frame in the stack above is where the null is dereferenced. <<"
}
} catch { W " ERROR reading event log: $($_.Exception.Message)" }
W ""
W "================ PROBE 5: part-group UNC reachability + PC-DMIS install ================"
# (a) Can THIS bay open the Selected Part Group share? An unreachable / unauthenticated
# UNC -> goCMM enumerates null -> startup NRE, regardless of local rights (admin too).
try {
$base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32')
$kpg = $base32.OpenSubKey($key32native, $false)
$pg = if ($kpg) { [string]$kpg.GetValue('Selected Part Group','') } else { '' }
if ($kpg) { $kpg.Close() }
if ($pg) {
W (" part group UNC: $pg")
if (Test-Path -LiteralPath $pg -ErrorAction SilentlyContinue) {
$n = @(Get-ChildItem -LiteralPath $pg -ErrorAction SilentlyContinue).Count
W (" >> REACHABLE - $n item(s) under it. (not the NRE cause) <<")
} else {
W " >> *** UNREACHABLE *** bay cannot open the part-group UNC (share offline, not on the"
W " >> production net yet, or no credential to \\tsgwp00525\SHARED). goCMM enumerating this"
W " >> path returns null -> startup NRE even for admin. Check net connectivity + share auth. <<"
}
} else { W " (no Selected Part Group in registry)" }
} catch { W " ERROR testing part-group UNC: $($_.Exception.Message)" }
W ""
# (b) PC-DMIS COM automation - THE confirmed root cause (see debug.log). goCMM
# connects via Activator.CreateInstance on the PC-DMIS COM type. If the
# automation server is not registered, Type.GetTypeFromProgID returns null
# -> CreateInstance(null) -> "ArgumentNullException: ... Parameter name: type"
# -> ConnectToSoftware derefs null -> NRE. PC-DMIS can be RUNNING and still
# not be COM-registered. Stack: GEAE.Common.CMM.CMMInterfaces.PCDMIS.ConnectToSoftware.
$comOk = $false
foreach ($pgid in 'PCDLRN.Application','PCDLRN.Automation','Pcdlrn.Application','PCDLRN.Object') {
foreach ($cls in "HKLM:\SOFTWARE\Classes\$pgid","HKLM:\SOFTWARE\Classes\WOW6432Node\$pgid") {
if (Test-Path $cls) {
$clsid = (Get-ItemProperty -Path "$cls\CLSID" -ErrorAction SilentlyContinue).'(default)'
$comOk = $true
$srv = $null
if ($clsid) {
foreach ($cb in "HKLM:\SOFTWARE\Classes\CLSID\$clsid\LocalServer32","HKLM:\SOFTWARE\Classes\WOW6432Node\CLSID\$clsid\LocalServer32") {
if (Test-Path $cb) { $srv = (Get-ItemProperty -Path $cb -EA SilentlyContinue).'(default)' }
}
}
W (" COM ProgID registered: $pgid CLSID=$clsid server=$srv")
}
}
}
if (-not $comOk) {
W " >> *** PC-DMIS COM SERVER NOT REGISTERED *** no PCDLRN.* ProgID in HKCR. This is exactly the"
W " >> null behind 'ArgumentNullException: ... Parameter name: type' in debug.log -> goCMM startup NRE."
W " >> FIX: run the installed Pcdlrn.exe once ELEVATED (self-registers), or 'Pcdlrn.exe /regserver'."
W " >> Then confirm the registered build matches the goCMM-expected PC-DMIS version (the version gate). <<"
}
# install presence (context for the COM verdict)
$pcdlrn = $null
foreach ($d in 'C:\Program Files\Hexagon','C:\Program Files (x86)\Hexagon','C:\Program Files\WAI','C:\Program Files (x86)\WAI') {
if (Test-Path $d) {
$hit = Get-ChildItem $d -Filter 'Pcdlrn.exe' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($hit) { $pcdlrn = $hit.FullName; W (" Pcdlrn.exe: $($hit.FullName) v$((Get-Item $hit.FullName).VersionInfo.FileVersion)"); break }
}
}
if (-not $pcdlrn) { W " Pcdlrn.exe NOT found in standard dirs - PC-DMIS not installed where expected." }
W ""
W "================ UAC / registry virtualization ================"
(reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableLUA 2>&1 | Out-String) | W
(reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableVirtualization 2>&1 | Out-String) | W

View File

@@ -0,0 +1,35 @@
@echo off
REM ==========================================================================
REM Diagnose-eDNCDetection.bat - diagnose the eDNC 6.4.5 enforce loop (MSI
REM 1603 "same or newer already installed" every 5 minutes).
REM
REM Usage (run on the problem PC):
REM Diagnose-eDNCDetection.bat auto-find manifest (W: or UNC)
REM Diagnose-eDNCDetection.bat /manifest <path> explicit manifest.json path
REM
REM Read-only: no installs, no share writes.
REM ==========================================================================
setlocal EnableDelayedExpansion
REM --- self-elevate ---
net session >nul 2>&1
if %errorlevel% neq 0 (
echo Requesting administrator elevation...
powershell -NoProfile -Command "Start-Process -Verb RunAs -FilePath '%~f0' -ArgumentList '%*'"
exit /b
)
set "PS=%~dp0Diagnose-eDNCDetection.ps1"
set "ARGS="
:parse
if "%~1"=="" goto run
if /I "%~1"=="/manifest" set "ARGS=!ARGS! -ManifestPath '%~2'" & shift & shift & goto parse
shift
goto parse
:run
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS%" !ARGS!
echo.
pause
endlocal

View File

@@ -0,0 +1,232 @@
<#
Diagnose-eDNCDetection.ps1
One-shot diagnosis of the eDNC 6.4.5 enforce loop: GE-Enforce re-runs
eDNC_6-4-5.msi every 5 minutes, the MSI aborts 1603 with "The same or newer
version of this software is already installed", and InUseCheck kills DncMain
in the process. Root cause is the manifest entry's Detection* fields not
matching what is actually installed.
Runs the whole triage and prints a verdict:
1. Installed state - DncMain.exe / NTLARS.exe file versions, x86+x64
uninstall registry entries for eDNC (DisplayVersion,
ProductCode).
2. Manifest entry - the live "eDNC (bundles NTLARS)" entry from the SFLD
share manifest (uses W: if enforce has it mounted,
else direct UNC, else -ManifestPath).
3. Comparison - replays Install-FromManifest's Test-AppInstalled
logic against the entry and says WHY it misses.
4. Verdict - suggested corrected manifest JSON.
Read-only: no installs, no share writes. Run as administrator on the
problem PC (registry + share read).
Params:
-ManifestPath explicit path to the collections manifest.json (skips
W:/UNC auto-discovery)
#>
param(
[string]$ManifestPath
)
$ErrorActionPreference = 'Continue'
function Section($t){ Write-Host ''; Write-Host ("==== {0} ====" -f $t) -ForegroundColor Cyan }
function KV($k,$v){ Write-Host (" {0,-24}: {1}" -f $k, $v) }
Write-Host '########################################################'
Write-Host '# eDNC Detection Loop Diagnosis'
Write-Host ("# {0} host {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), [System.Environment]::MachineName)
Write-Host '########################################################'
# ---------------------------------------------------------------------------
# 1. Installed state
# ---------------------------------------------------------------------------
Section '1. Installed eDNC state on this PC'
$dncMain = 'C:\Program Files (x86)\Dnc\bin\DncMain.exe'
$ntlars = 'C:\Program Files (x86)\Dnc\Common\NTLARS.exe'
$dncMainVer = ''
$ntlarsVer = ''
if (Test-Path $dncMain) {
$dncMainVer = (Get-Item $dncMain).VersionInfo.FileVersion
KV 'DncMain.exe' ("present, FileVersion {0}" -f $dncMainVer)
} else {
KV 'DncMain.exe' 'MISSING'
}
if (Test-Path $ntlars) {
$ntlarsVer = (Get-Item $ntlars).VersionInfo.FileVersion
KV 'NTLARS.exe' ("present, FileVersion {0}" -f $ntlarsVer)
} else {
KV 'NTLARS.exe' 'MISSING'
}
# Uninstall entries - both registry views. Win32_Product deliberately avoided
# (it triggers MSI self-repair on enumeration).
$uninstallRoots = @(
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$edncProducts = @()
foreach ($root in $uninstallRoots) {
$edncProducts += Get-ItemProperty -Path $root -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like '*eDNC*' -or $_.DisplayName -like '*DNC*Client*' }
}
if ($edncProducts) {
foreach ($p in $edncProducts) {
Write-Host ''
KV 'DisplayName' $p.DisplayName
KV 'DisplayVersion' $p.DisplayVersion
KV 'ProductCode' $p.PSChildName
KV 'InstallDate' $p.InstallDate
KV 'RegistryView' ($(if ($p.PSPath -like '*WOW6432Node*') { 'x86 (WOW6432Node)' } else { 'x64' }))
}
} else {
Write-Host ' No eDNC uninstall entry found in either registry view.' -ForegroundColor Yellow
}
# ---------------------------------------------------------------------------
# 2. Manifest entry from the share
# ---------------------------------------------------------------------------
Section '2. Manifest entry (share)'
$candidates = @()
if ($ManifestPath) { $candidates += $ManifestPath }
$candidates += 'W:\gea-shopfloor-collections\manifest.json'
$candidates += '\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor\gea-shopfloor-collections\manifest.json'
$manifestFile = $null
foreach ($c in $candidates) {
if (Test-Path $c) { $manifestFile = $c; break }
}
$entry = $null
if ($manifestFile) {
KV 'Manifest' $manifestFile
try {
$manifest = Get-Content -LiteralPath $manifestFile -Raw | ConvertFrom-Json
$entry = $manifest.Applications | Where-Object { $_.Name -like 'eDNC*' } | Select-Object -First 1
} catch {
Write-Host (" Failed to parse manifest: {0}" -f $_) -ForegroundColor Red
}
if ($entry) {
KV 'Name' $entry.Name
KV 'Installer' $entry.Installer
KV 'Type' $entry.Type
KV 'InstallArgs' $entry.InstallArgs
KV 'DetectionMethod' $entry.DetectionMethod
KV 'DetectionPath' $entry.DetectionPath
KV 'DetectionName' $entry.DetectionName
KV 'DetectionValue' $entry.DetectionValue
} else {
Write-Host ' No eDNC* entry found in manifest Applications.' -ForegroundColor Yellow
}
} else {
Write-Host ' Manifest not reachable (no W: mount, UNC denied).' -ForegroundColor Yellow
Write-Host ' Re-run with -ManifestPath, or run while GE-Enforce has W: mounted.'
}
# ---------------------------------------------------------------------------
# 3. Replay Test-AppInstalled against the entry
# ---------------------------------------------------------------------------
Section '3. Detection replay'
$detected = $null
$missReason = ''
if ($entry) {
switch ($entry.DetectionMethod) {
'File' {
$detected = Test-Path $entry.DetectionPath
if (-not $detected) { $missReason = "DetectionPath does not exist: $($entry.DetectionPath)" }
}
'FileVersion' {
if (-not (Test-Path $entry.DetectionPath)) {
$detected = $false
$missReason = "DetectionPath does not exist: $($entry.DetectionPath)"
} elseif (-not $entry.DetectionValue) {
$detected = $false
$missReason = 'FileVersion detection with no DetectionValue always misses'
} else {
$actual = (Get-Item $entry.DetectionPath -ErrorAction SilentlyContinue).VersionInfo.FileVersion
$detected = ($actual -eq $entry.DetectionValue)
if (-not $detected) {
$missReason = "exact-match compare failed: actual '$actual' vs DetectionValue '$($entry.DetectionValue)'"
}
}
}
'Registry' {
if (-not (Test-Path $entry.DetectionPath)) {
$detected = $false
$missReason = "registry key does not exist: $($entry.DetectionPath) (eDNC uninstall key path varies across 6.x releases)"
} elseif ($entry.DetectionName) {
$v = Get-ItemProperty -Path $entry.DetectionPath -Name $entry.DetectionName -ErrorAction SilentlyContinue
if (-not $v) {
$detected = $false
$missReason = "value '$($entry.DetectionName)' missing under $($entry.DetectionPath)"
} elseif ($entry.DetectionValue) {
$actual = $v.$($entry.DetectionName)
$detected = ("$actual" -eq "$($entry.DetectionValue)")
if (-not $detected) {
$missReason = "exact-match compare failed: actual '$actual' vs DetectionValue '$($entry.DetectionValue)'"
}
} else {
$detected = $true
}
} else {
$detected = $true
}
}
default {
$missReason = "detection method '$($entry.DetectionMethod)' not replayed by this script"
}
}
if ($null -ne $detected) {
KV 'Test-AppInstalled' ($(if ($detected) { 'TRUE (would skip install)' } else { 'FALSE (re-runs MSI every cycle)' }))
if ($missReason) { KV 'Miss reason' $missReason }
}
} else {
Write-Host ' Skipped - no manifest entry available to replay.'
}
# ---------------------------------------------------------------------------
# 4. Verdict
# ---------------------------------------------------------------------------
Section '4. Verdict'
$edncInstalled = ($edncProducts.Count -gt 0) -or (Test-Path $dncMain)
if (-not $edncInstalled) {
Write-Host ' eDNC does NOT appear installed. The 1603 "same or newer" abort'
Write-Host ' then points at a stale MSI registration; check the upgrade-code'
Write-Host ' products under HKLM:\SOFTWARE\Classes\Installer\UpgradeCodes.'
} elseif ($detected -eq $true) {
Write-Host ' Detection passes now. If enforce still loops, the manifest on the'
Write-Host ' share differs from the one inspected here - confirm the path.'
} else {
Write-Host ' eDNC IS installed but the manifest entry does not detect it, so'
Write-Host ' GE-Enforce re-runs the MSI every cycle and the MSI aborts 1603'
Write-Host ' ("same or newer already installed"). Fix the manifest entry on'
Write-Host ' the share. Suggested replacement (File detection on NTLARS.exe,'
Write-Host ' same approach as the working 6.4.3 machineapps entry):'
Write-Host ''
$suggested = [ordered]@{
Name = 'eDNC (bundles NTLARS)'
Installer = 'apps\eDNC_6-4-5.msi'
Type = 'MSI'
InstallArgs = '/qn /norestart ALLUSERS=1 REBOOT=ReallySuppress SITESELECTED="West Jefferson"'
DetectionMethod = 'File'
DetectionPath = 'C:\Program Files (x86)\Dnc\Common\NTLARS.exe'
} | ConvertTo-Json
Write-Host $suggested
Write-Host ''
if ($edncProducts) {
$dv = ($edncProducts | Select-Object -First 1).DisplayVersion
Write-Host (" Alternative: keep Registry detection but set DetectionValue to the")
Write-Host (" actual installed DisplayVersion: '{0}'" -f $dv)
}
Write-Host ' Until the manifest is fixed, every enforce cycle also risks killing'
Write-Host ' a live DncMain session (InUseCheck force-kill before the doomed MSI).'
}
Write-Host ''

View File

@@ -1,21 +1,28 @@
# Report-AssetToShopDB.ps1
#
# Reports a collections bay's identity to ShopDB so the machines record stays
# current with whatever the bay actually is right now: hostname, BIOS serial,
# DNC machine number (2001, 2002, ...) and its corp/AESFMA IPv4 address.
# Reports a PC's identity to ShopDB so the machines record stays current with
# whatever the PC actually is right now: hostname, BIOS serial, pc-type, logged-
# in user, DNC machine number (2001, 2002, ... when present) and its corp/AESFMA
# IPv4 address.
#
# Deployed in common\ (runs on EVERY shopfloor pc-type: collections, cmm,
# keyence, waxtrace, genspect, heattreat, partmarker, nocollections, ...), not
# collections-only. Non-DNC bays (cmm/keyence/waxtrace) simply report identity
# with no machineNo, so no PC-to-machine relationship is built - by design.
#
# Runs every GE-Enforce cycle as a Type=PS1 manifest entry (DetectionMethod
# Always) under the SYSTEM scheduled task. Idempotent on the server side:
# ShopDB api.asp action=updateCompleteAsset upserts the machines row keyed by
# hostname, clears+reinserts the interface rows, and (re)creates the
# PC-to-machine relationship from machineNo. Safe to fire repeatedly.
# hostname (patch-style: only the fields posted here are updated, so it never
# clobbers model/VNC/WinRM), clears+reinserts the interface rows, and (re)creates
# the PC-to-machine relationship from machineNo. Safe to fire repeatedly.
#
# WHY collections-only and corp-NIC-only:
# Collections (controller-NIC) bays carry two NICs - a private controller
# NIC (e.g. 192.168.x / 10.x stray) and the routable corp/AESFMA NIC. Only
# the corp NIC belongs in ShopDB, so we filter to the WJ corp ranges and
# drop everything else. Mirrors the allowed-range gate in
# Invoke-FilteredReportIP.ps1.
# WHY corp-NIC-only:
# Some bays (collections/controller) carry two NICs - a private controller NIC
# (e.g. 192.168.x / 10.x stray) and the routable corp/AESFMA NIC. Only the corp
# NIC belongs in ShopDB, so we filter to the WJ corp ranges and drop the rest.
# Single-NIC PCs just pass their one corp IP through the same gate. Mirrors the
# allowed-range gate in Invoke-FilteredReportIP.ps1.
#
# Always exits 0 so the GE-Enforce "last run result" stays clean; failures are
# logged, never thrown.
@@ -83,15 +90,17 @@ if (-not $serialNumber) {
exit 0
}
# DNC machine number (2001, 2002, ...). optional - sent only if found.
# Resolution order mirrors the GE-Enforce lib Get-CurrentMachineNumber so the
# reporter and manifest gating agree:
# 1. eDNC registry (WOW6432Node, then native) - follows bay reassignment, which
# Set-MachineNumber rewrites here.
# 2. C:\Enrollment\machine-number.txt - the imaging-time value written once by
# startnet.cmd. Used when eDNC has not populated the registry yet (fresh
# image, or a bay where eDNC has not run), so the PC still reports its number
# and api.asp can build the relationship.
# Machine identifier - optional, sent only if found. api.asp matches it against
# the equipment machinenumber OR alias column, so one value covers every type:
# 1. eDNC registry (WOW6432Node, then native) - DNC/collections bays (2001...).
# Follows bay reassignment, which Set-MachineNumber rewrites here.
# 2. C:\Enrollment\cmm\cmmid.txt - CMM bay id (e.g. CMM3), written by
# select-cmm-bay.ps1 at imaging. Matches the equipment machinenumber.
# 3. C:\Enrollment\machine-number.txt - imaging value. For wax&trace this is the
# asset tag from select-waxtrace-asset.ps1 (matches the equipment alias);
# for DNC bays it is the digit number. 9999 is the placeholder = skip.
# keyence / genspect / part-marker have no per-bay id on the PC -> no machineNo
# is sent and the link is assigned manually in ShopDB.
$machineNo = ''
foreach ($regPath in @(
'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General',
@@ -101,43 +110,115 @@ foreach ($regPath in @(
try {
if (Test-Path $regPath) {
$v = [string](Get-ItemProperty -Path $regPath -Name MachineNo -ErrorAction Stop).MachineNo
if ($v) { $machineNo = $v.Trim() }
if ($v -and $v.Trim() -ne '9999') { $machineNo = $v.Trim() }
}
} catch {
Log "WARN could not read MachineNo from ${regPath}: $($_.Exception.Message)"
}
}
if (-not $machineNo) {
$cmmFile = 'C:\Enrollment\cmm\cmmid.txt'
if (Test-Path -LiteralPath $cmmFile) {
try {
$v = ([string](Get-Content -LiteralPath $cmmFile -First 1 -ErrorAction Stop)).Trim()
if ($v -and $v -ne '9999') { $machineNo = $v; Log "machineNo from $cmmFile (CMM bay id): $machineNo" }
} catch { Log "WARN could not read ${cmmFile}: $($_.Exception.Message)" }
}
}
if (-not $machineNo) {
$mnFile = 'C:\Enrollment\machine-number.txt'
if (Test-Path -LiteralPath $mnFile) {
try {
$v = Get-Content -LiteralPath $mnFile -First 1 -ErrorAction Stop
if ($v) { $machineNo = ([string]$v).Trim(); Log "machineNo from $mnFile (eDNC registry empty): $machineNo" }
$v = ([string](Get-Content -LiteralPath $mnFile -First 1 -ErrorAction Stop)).Trim()
if ($v -and $v -ne '9999') { $machineNo = $v; Log "machineNo from $mnFile (imaging value): $machineNo" }
} catch { Log "WARN could not read ${mnFile}: $($_.Exception.Message)" }
}
}
# OS caption for the operatingsystems lookup.
# OS for the operatingsystems lookup: caption + feature-update (e.g. 23H2, read
# from the registry since WMI does not expose it) + major build number. Yields a
# string like "Microsoft Windows 11 Enterprise 23H2 (build 22631)" so the fleet
# OS-version breakdown is visible everywhere osid is shown - no schema change,
# api.asp upserts each distinct string into operatingsystems.
$osVersion = ''
# last boot time - api.asp stores it as machines.lastboottime; uptime is derived
# server-side (DATEDIFF(NOW(), lastboottime)). MySQL datetime format.
$lastBootTime = ''
try {
$osVersion = (Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop).Caption
if ($osVersion) { $osVersion = $osVersion.Trim() }
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
$osVersion = "$($os.Caption)".Trim()
$displayVersion = ''
try {
$displayVersion = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name DisplayVersion -ErrorAction Stop).DisplayVersion
} catch {}
if ($displayVersion) { $osVersion += " $displayVersion" }
if ($os.BuildNumber) { $osVersion += " (build $($os.BuildNumber))" }
$osVersion = $osVersion.Trim()
try { $lastBootTime = $os.LastBootUpTime.ToString('yyyy-MM-dd HH:mm:ss') } catch {}
} catch {}
# gather corp NICs only. one networkInterfaces entry per allowed IPv4.
# interactive console user (DOMAIN\user). this script runs as SYSTEM so we
# cannot use $env:USERNAME; Win32_ComputerSystem.UserName is the console
# session owner and works from SYSTEM context. empty when nobody is logged on,
# in which case we omit it from the post so an unattended bay does not blank
# the last-known user on the server.
$loggedInUser = ''
try {
$loggedInUser = (Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop).UserName
# Win32 returns COMPUTERNAME\user (local account) or DOMAIN\user. Keep only
# the username part: matches the legacy bare-username convention and avoids
# the backslash, which the inline api.asp SQL does not escape so MySQL eats
# it (FB90238\ShopFloor was being stored as FB90238ShopFloor).
if ($loggedInUser) { $loggedInUser = ($loggedInUser -split '\\')[-1].Trim() }
} catch {
Log "WARN could not read logged-in user: $($_.Exception.Message)"
}
# imaging pc-type (gea-shopfloor-*), read from the enrollment file written at
# image time. api.asp maps both the gea-shopfloor-* values and the legacy
# display strings to the right pctypeid. Sent only when present; when absent the
# server's patch-style update leaves the existing pctype untouched (so a bare
# report never re-types a PC). This is what lets the reporter run fleet-wide
# from common\ instead of collections-only.
$pcType = ''
$ptFile = 'C:\Enrollment\pc-type.txt'
if (Test-Path -LiteralPath $ptFile) {
try {
$pcType = (Get-Content -LiteralPath $ptFile -First 1 -ErrorAction Stop).Trim()
} catch { Log "WARN could not read ${ptFile}: $($_.Exception.Message)" }
}
# PC make/model from WMI (e.g. "Dell Inc." / "OptiPlex 7090"). api.asp resolves
# or creates the vendor + model rows and links modelnumberid. Sent only when
# present so a WMI read failure does not blank the model on the row.
$manufacturer = ''
$model = ''
try {
$cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
$manufacturer = "$($cs.Manufacturer)".Trim()
$model = "$($cs.Model)".Trim()
} catch {
Log "WARN could not read make/model: $($_.Exception.Message)"
}
# gather IPv4 NICs - BOTH the corp/AESFMA NIC and the controller/machine LAN NIC,
# each with its MAC. Physical adapters only (drop Hyper-V/VPN/WSL/virtual plus
# link-local 169.254 and loopback). Each NIC is tagged IsMachineNetwork: true for
# the controller LAN (any IP outside the corp ranges), false for the corp NIC.
$interfaces = @()
try {
$ipObjs = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop |
Where-Object { $_.IPAddress -notmatch '^169\.254' -and $_.IPAddress -ne '127.0.0.1' }
foreach ($ipo in $ipObjs) {
if (-not (Test-InAllowedRange $ipo.IPAddress)) { continue }
$adapter = $null
try { $adapter = Get-NetAdapter -InterfaceIndex $ipo.InterfaceIndex -ErrorAction Stop } catch {}
# physical + connected only; skip virtual adapters (Hyper-V/VPN/WSL/etc)
if (-not $adapter) { continue }
if (-not $adapter.HardwareInterface) { continue }
if ($adapter.Status -ne 'Up') { continue }
$mac = ''
$mac = $adapter.MacAddress
$gw = ''
try {
$adapter = Get-NetAdapter -InterfaceIndex $ipo.InterfaceIndex -ErrorAction Stop
$mac = $adapter.MacAddress
} catch {}
try {
$gw = (Get-NetRoute -InterfaceIndex $ipo.InterfaceIndex -DestinationPrefix '0.0.0.0/0' -ErrorAction Stop |
Select-Object -First 1).NextHop
@@ -149,13 +230,15 @@ try {
[Array]::Reverse($maskBytes)
$subnetMask = ($maskBytes | ForEach-Object { $_ }) -join '.'
$isCorp = Test-InAllowedRange $ipo.IPAddress
$interfaces += [pscustomobject]@{
IPAddress = $ipo.IPAddress
MACAddress = $mac
SubnetMask = $subnetMask
DefaultGateway = $gw
InterfaceName = $ipo.InterfaceAlias
IsMachineNetwork = $false # corp NIC, not the controller LAN
IsMachineNetwork = (-not $isCorp) # controller/machine LAN = true; corp = false
}
}
} catch {
@@ -163,7 +246,7 @@ try {
}
if ($interfaces.Count -eq 0) {
Log 'WARN no corp-range IPv4 found; posting identity without interfaces.'
Log 'WARN no physical IPv4 NIC found; posting identity without interfaces.'
}
$networkInterfacesJson = if ($interfaces.Count -gt 0) { $interfaces | ConvertTo-Json -Compress -Depth 4 } else { '' }
@@ -174,14 +257,18 @@ $body = @{
action = 'updateCompleteAsset'
hostname = $hostname
serialNumber = $serialNumber
pcType = 'Shopfloor'
osVersion = $osVersion
networkInterfaces = $networkInterfacesJson
}
if ($machineNo) { $body['machineNo'] = $machineNo }
if ($machineNo) { $body['machineNo'] = $machineNo }
if ($loggedInUser) { $body['loggedInUser'] = $loggedInUser }
if ($pcType) { $body['pcType'] = $pcType }
if ($manufacturer) { $body['manufacturer'] = $manufacturer }
if ($model) { $body['model'] = $model }
if ($lastBootTime) { $body['lastBootTime'] = $lastBootTime }
Log ("POST {0} host={1} serial={2} machineNo={3} ips={4}" -f `
$ApiUrl, $hostname, $serialNumber, $machineNo, (($interfaces | ForEach-Object { $_.IPAddress }) -join ','))
Log ("POST {0} host={1} serial={2} pcType={3} make={4} model={5} os={6} boot={7} machineNo={8} user={9} ips={10}" -f `
$ApiUrl, $hostname, $serialNumber, $pcType, $manufacturer, $model, $osVersion, $lastBootTime, $machineNo, $loggedInUser, (($interfaces | ForEach-Object { $_.IPAddress }) -join ','))
try {
$resp = Invoke-RestMethod -Uri $ApiUrl -Method Post -Body $body -TimeoutSec $TimeoutSec -ErrorAction Stop

View File

@@ -190,6 +190,7 @@
{ "name": "Defect_Tracker", "lnkPath": "%PUBLIC%\\Desktop\\Shopfloor Tools\\Defect_Tracker.lnk" },
{ "name": "PC-DMIS 2016", "lnkPath": "%PUBLIC%\\Desktop\\Shopfloor Tools\\PC-DMIS 2016.lnk" },
{ "name": "PC-DMIS 2019 R2", "lnkPath": "%PUBLIC%\\Desktop\\Shopfloor Tools\\PC-DMIS 2019 R2.lnk" },
{ "name": "PC-DMIS 2026.1", "lnkPath": "%PUBLIC%\\Desktop\\Shopfloor Tools\\PC-DMIS 2026.1.lnk" },
{ "name": "CLM Admin", "lnkPath": "%PUBLIC%\\Desktop\\Shopfloor Tools\\CLM Admin.lnk" },
{ "name": "goCMM", "lnkPath": "%PUBLIC%\\Desktop\\Shopfloor Tools\\goCMM.lnk" }
],
@@ -198,6 +199,7 @@
{ "name": "Defect_Tracker", "kind": "existing", "sourceName": "Defect_Tracker.lnk" },
{ "name": "PC-DMIS 2016", "kind": "exe", "exePath": "C:\\Program Files\\Hexagon\\PC-DMIS 2016.0 64-bit\\PCDLRN.exe" },
{ "name": "PC-DMIS 2019 R2", "kind": "exe", "exePath": "C:\\Program Files\\Hexagon\\PC-DMIS 2019 R2 64-bit\\PCDLRN.exe" },
{ "name": "PC-DMIS 2026.1", "kind": "exe", "exePath": "C:\\Program Files\\Hexagon\\PC-DMIS 2026.1 64-bit\\PCDLRN.exe" },
{ "name": "CLM Admin", "kind": "exe", "exePath": "C:\\Program Files\\Hexagon\\CLM Admin 1.8 64-bit\\clmadmin.exe" },
{ "name": "goCMM", "kind": "exe", "exePath": "C:\\Program Files (x86)\\General Electric\\goCMM\\goCMM.exe" }
],

View File

@@ -38,14 +38,10 @@ echo.
echo 1. GEA Standard
echo 2. GEA Engineer
echo 3. GEA Shopfloor (sub-menu)
echo 4. GE Standard
echo 5. GE Engineer
echo 6. GE Shopfloor Lockdown
echo 7. GE Shopfloor MCE
echo.
echo ========================================
echo.
set /p choice=Enter your choice (1-7):
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-*
@@ -270,10 +266,6 @@ echo. > X:\Boot.tag
if "%choice%"=="1" goto gea-standard
if "%choice%"=="2" goto gea-engineer
if "%choice%"=="3" goto gea-shopfloor
if "%choice%"=="4" goto ge-standard
if "%choice%"=="5" goto ge-engineer
if "%choice%"=="6" goto ge-shopfloor-lockdown
if "%choice%"=="7" goto ge-shopfloor-mce
echo Invalid choice. Please try again.
pause
goto menu
@@ -302,38 +294,6 @@ for /l %%i in (1,1,2000000) do rem
net use Z: \\172.16.9.1\winpeapps\gea-shopfloor /user:pxe-upload pxe /persistent:no
goto end
:ge-standard
echo.
echo Starting GE 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\ge-standard /user:pxe-upload pxe /persistent:no
goto end
:ge-engineer
echo.
echo Starting GE 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\ge-engineer /user:pxe-upload pxe /persistent:no
goto end
:ge-shopfloor-lockdown
echo.
echo Starting GE Shopfloor Lockdown setup...
start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe
for /l %%i in (1,1,2000000) do rem
net use Z: \\172.16.9.1\winpeapps\ge-shopfloor-lockdown /user:pxe-upload pxe /persistent:no
goto end
:ge-shopfloor-mce
echo.
echo Starting GE Shopfloor MCE setup...
start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe
for /l %%i in (1,1,2000000) do rem
net use Z: \\172.16.9.1\winpeapps\ge-shopfloor-mce /user:pxe-upload pxe /persistent:no
goto end
:end
echo.

View File

@@ -13,8 +13,8 @@
# Pushes the NEWEST backup set per cmm_id to:
# /srv/samba/enrollment/installers-post/cmm/backups/<cmm_id>/
#
# DODA bays: do NOT back them up (don't put them under cmm-bk/). 09-Setup-CMM
# also gates restore on cmm-bay-config doda=no as a second guard.
# DODA bays ARE backed up + restored like any other now (the old skip-on-doda
# policy in Restore-CMM was dropped 2026-06-17). Stage them here the same way.
#
# Usage: ./playbook/sync-cmm-backups.sh (all cmm_id folders)
# CMM_ID=CMM3 ./playbook/sync-cmm-backups.sh (just one)

View File

@@ -38,14 +38,10 @@ echo.
echo 1. GEA Standard
echo 2. GEA Engineer
echo 3. GEA Shopfloor
echo 4. GE Standard
echo 5. GE Engineer
echo 6. GE Shopfloor Lockdown
echo 7. GE Shopfloor MCE
echo.
echo ========================================
echo.
set /p choice=Enter your choice (1-7):
set /p choice=Enter your choice (1-3):
REM --- Only shopfloor images (3,6,7) need GCCH enrollment ---
set PPKG=
@@ -186,10 +182,6 @@ echo. > X:\Boot.tag
if "%choice%"=="1" goto gea-standard
if "%choice%"=="2" goto gea-engineer
if "%choice%"=="3" goto gea-shopfloor
if "%choice%"=="4" goto ge-standard
if "%choice%"=="5" goto ge-engineer
if "%choice%"=="6" goto ge-shopfloor-lockdown
if "%choice%"=="7" goto ge-shopfloor-mce
echo Invalid choice. Please try again.
pause
goto menu
@@ -218,38 +210,6 @@ for /l %%i in (1,1,2000000) do rem
net use Z: \\172.16.9.1\winpeapps\gea-shopfloor /user:pxe-upload pxe /persistent:no
goto end
:ge-standard
echo.
echo Starting GE 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\ge-standard /user:pxe-upload pxe /persistent:no
goto end
:ge-engineer
echo.
echo Starting GE 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\ge-engineer /user:pxe-upload pxe /persistent:no
goto end
:ge-shopfloor-lockdown
echo.
echo Starting GE Shopfloor Lockdown setup...
start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe
for /l %%i in (1,1,2000000) do rem
net use Z: \\172.16.9.1\winpeapps\ge-shopfloor-lockdown /user:pxe-upload pxe /persistent:no
goto end
:ge-shopfloor-mce
echo.
echo Starting GE Shopfloor MCE setup...
start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe
for /l %%i in (1,1,2000000) do rem
net use Z: \\172.16.9.1\winpeapps\ge-shopfloor-mce /user:pxe-upload pxe /persistent:no
goto end
:end
echo.
echo Waiting for PESetup.exe to start...

View File

@@ -56,20 +56,12 @@ IMAGE_TYPES = [
"gea-standard",
"gea-engineer",
"gea-shopfloor",
"ge-standard",
"ge-engineer",
"ge-shopfloor-lockdown",
"ge-shopfloor-mce",
]
FRIENDLY_NAMES = {
"gea-standard": "GE Aerospace Standard",
"gea-engineer": "GE Aerospace Engineer",
"gea-shopfloor": "GE Aerospace Shop Floor",
"ge-standard": "GE Legacy Standard",
"ge-engineer": "GE Legacy Engineer",
"ge-shopfloor-lockdown": "GE Legacy Shop Floor Lockdown",
"ge-shopfloor-mce": "GE Legacy Shop Floor MCE",
}
# --- Unattend XML namespaces -------------------------------------------------