Shopfloor: Configure-PC tool, machine-number logon prompt, execution order fixes

New tools:

Configure-PC.bat/.ps1 - Interactive desktop tool for SupportUser to
configure a shopfloor PC after imaging. Two sections:
  1. Machine number: if UDC/eDNC are still at placeholder 9999, prompt
     to set the real number right now (updates UDC JSON + eDNC registry,
     restarts UDC.exe with new args).
  2. Auto-startup toggle: pick which apps start at user logon from a
     numbered list (UDC, eDNC, Defect Tracker, WJ Shopfloor, Plant Apps).
     Creates/removes .lnk files in AllUsers Startup folder. Toggle UI
     shows [ON]/[  ] state, safe to re-run anytime. Plant Apps URL
     resolved from .url file at runtime with hardcoded fallback to
     https://mes-wjefferson.apps.lr.geaerospace.net/run/...
  3. Item 6 in the toggle list: register/unregister a "Check Machine
     Number" logon task for standard (non-admin) users. When enabled,
     the task fires at every logon, checks for 9999, pops an InputBox
     if found, updates both apps, then unregisters itself on success.

Check-MachineNumber.ps1 - The logon task script. Runs as the logged-in
user (needs GUI for InputBox), not SYSTEM. Writing to ProgramData + HKLM
is possible because 02-MachineNumberACLs.ps1 pre-grants BUILTIN\Users
write access on the two specific targets during imaging.

02-MachineNumberACLs.ps1 - Standard type-specific script (runs after
01-eDNC.ps1). Opens C:\ProgramData\UDC\udc_settings.json for Users:Modify
and HKLM:\...\GE Aircraft Engines\DNC\General for Users:SetValue. Narrow
scope, not blanket admin.

Execution order fixes in Run-ShopfloorSetup.ps1:

The dispatcher now has two lists: $skipInBaseline (scripts NOT run in the
alphabetical baseline loop) and $runAfterTypeSpecific (scripts run
explicitly after type-specific scripts complete). This fixes the bug where
06/07 ran before 01-eDNC.ps1 installed DnC, so eDNC/NTLARS shortcuts were
silently skipped.

New execution order:
  Baseline: 00-PreInstall, 04-NetworkAndWinRM (skipping 05-08 + tools)
  Type-specific: 01-eDNC, 02-MachineNumberACLs
  Finalization: 06-OrganizeDesktop, 07-TaskbarLayout

06 internally calls 05 (Office shortcuts, Phase 0) and 08 (Edge config,
Phase 4) as sub-phases, so they also benefit from running late. Office
isn't installed until after the first reboot (ppkg streams C2R), so 05
no-ops at imaging time but succeeds when 06's SYSTEM logon task re-runs
it on the second boot. 08 resolves startup-tab URLs from .url files
delivered by DSC (even later); same self-heal via the logon task.

Other fixes in this commit:

- OpenText Setup-OpenText.ps1 Step 4: exclude WJ_Office.lnk, IBM_qks.lnk,
  mmcs.lnk desktop shortcuts (matching the Step 3 .hep profile exclusion
  from the previous commit). Removes stale copies from prior installs.
- 05-OfficeShortcuts.ps1: widened Office detection to 6 path variants
  covering C2R + MSI + Office15/16, with diagnostic output on miss.
- 06-OrganizeDesktop.ps1: removed Phase 3 (desktop-root pin copies for
  eDNC/NTLARS) so shortcuts live in Shopfloor Tools only, not duplicated
  at root. Emptied $keepAtRoot. Added Phase 0 (call 05) and Phase 4
  (call 08). Lazy folder creation + empty-folder cleanup. Scheduled task
  now runs as SYSTEM (was BUILTIN\Users with Limited which failed the
  admin check). Added NTLARS to 07's taskbar pin list.
- 08-EdgeDefaultBrowser.ps1: Plant Apps URL fallback hardcoded from
  device-config.yaml.
- All new scripts have Start-Transcript logging to C:\Logs\SFLD\ with
  timestamps and running-as identity.
- Run-ShopfloorSetup.ps1: Start-Transcript + Stop-Transcript wrapping
  entire dispatcher run, writes to C:\Logs\SFLD\shopfloor-setup.log.
  Configure-PC.bat added to SupportUser desktop copy list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-04-10 08:44:28 -04:00
parent 900180cd12
commit cb2a9d48a1
10 changed files with 899 additions and 66 deletions

View File

@@ -280,13 +280,40 @@ foreach ($u in $userDirs) {
}
# --- Step 4: Public Desktop shortcuts ---
# Same exclusion list as the Profile step: these three sessions aren't
# used on shopfloor PCs, so we skip deploying their .lnk files AND
# remove any that a prior install left behind.
Write-SetupLog ""
Write-SetupLog "Step 4: Deploying public desktop shortcuts..."
$shortcutSrc = Join-Path $SourceDir 'W10shortcuts'
$publicDesktop = 'C:\Users\Public\Desktop'
$excludeShortcuts = @(
'WJ_Office.lnk',
'IBM_qks.lnk',
'mmcs.lnk'
)
# Clean up stale copies from prior installs first
foreach ($name in $excludeShortcuts) {
$stale = Join-Path $publicDesktop $name
if (Test-Path -LiteralPath $stale) {
try {
Remove-Item -LiteralPath $stale -Force -ErrorAction Stop
Write-SetupLog " removed stale desktop shortcut: $name"
} catch {
Write-SetupLog " failed to remove stale $stale : $_"
}
}
}
if (Test-Path $shortcutSrc) {
$lnkFiles = Get-ChildItem -Path $shortcutSrc -Filter '*.lnk' -File -ErrorAction SilentlyContinue
foreach ($l in $lnkFiles) {
if ($excludeShortcuts -contains $l.Name) {
Write-SetupLog " skip (excluded): $($l.Name)"
continue
}
Copy-Item -Path $l.FullName -Destination $publicDesktop -Force
Write-SetupLog " $($l.Name) -> $publicDesktop"
}

View File

@@ -1,6 +1,24 @@
# Run-ShopfloorSetup.ps1 - Dispatcher for shopfloor PC type setup
# Runs Shopfloor baseline scripts first, then type-specific scripts on top.
# --- Transcript logging ---
# Captures everything the dispatcher and all child scripts write to host so
# we can diagnose setup failures after the fact. -Append + -Force so repeat
# invocations (e.g. after a reboot mid-setup) accumulate instead of clobbering.
$logDir = 'C:\Logs\SFLD'
if (-not (Test-Path $logDir)) {
try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch { $logDir = $env:TEMP }
}
$transcriptPath = Join-Path $logDir 'shopfloor-setup.log'
try { Start-Transcript -Path $transcriptPath -Append -Force | Out-Null } catch {}
Write-Host ""
Write-Host "================================================================"
Write-Host "=== Run-ShopfloorSetup.ps1 starting $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ==="
Write-Host " Transcript: $transcriptPath"
Write-Host "================================================================"
Write-Host ""
# Bump AutoLogonCount HIGH at the start so reboots during setup (e.g. VC++ 2008
# triggering an immediate ExitWindowsEx) don't exhaust autologin attempts before
# the dispatcher can complete. The end-of-script reset puts it back to 2 once
@@ -40,11 +58,44 @@ if (-not $pcType) {
Write-Host "Shopfloor PC Type: $pcType"
# --- Run Shopfloor baseline scripts first ---
# Scripts to skip in the alphabetical baseline loop. Each is either run
# explicitly in the finalization phase below, or invoked internally by
# another script:
#
# 05 - Office shortcuts. Invoked by 06 as Phase 0. Office isn't installed
# until after the first reboot, so 05 no-ops on the imaging run.
# 06's SYSTEM logon task re-runs it on the second boot.
# 06 - Desktop org. Phase 2 needs eDNC/NTLARS on disk (installed by
# type-specific 01-eDNC.ps1). Run in finalization phase.
# 07 - Taskbar pin layout. Reads 06's output. Run in finalization phase.
# 08 - Edge default browser + startup tabs. Invoked by 06 as Phase 4.
# Reads .url files delivered by DSC (after setup reboots). 06's
# SYSTEM logon task re-runs it to pick them up.
$skipInBaseline = @(
'05-OfficeShortcuts.ps1',
'06-OrganizeDesktop.ps1',
'07-TaskbarLayout.ps1',
'08-EdgeDefaultBrowser.ps1',
'Check-MachineNumber.ps1',
'Configure-PC.ps1'
)
# Scripts run AFTER type-specific scripts complete. 05 and 08 are NOT
# here because 06 calls them internally as sub-phases.
$runAfterTypeSpecific = @(
'06-OrganizeDesktop.ps1',
'07-TaskbarLayout.ps1'
)
# --- Run Shopfloor baseline scripts first (skipping deferred ones) ---
$baselineDir = Join-Path $setupDir "Shopfloor"
if (Test-Path $baselineDir) {
$scripts = Get-ChildItem -Path $baselineDir -Filter "*.ps1" -File | Sort-Object Name
foreach ($script in $scripts) {
if ($skipInBaseline -contains $script.Name) {
Write-Host "Skipping baseline: $($script.Name) (runs in finalization phase)"
continue
}
shutdown /a 2>$null
Write-Host "Running baseline: $($script.Name)"
try {
@@ -74,13 +125,36 @@ if ($pcType -ne "Shopfloor") {
}
}
# --- Finalization: run deferred baseline scripts (desktop org, taskbar pins)
# ---
# These needed to wait until all apps (eDNC, NTLARS, UDC, OpenText) were
# installed by the baseline + type-specific phases above. 06 internally
# calls 05 (Office shortcuts) and 08 (Edge config) as sub-phases, so we
# only need to invoke 06 and 07 explicitly here.
foreach ($name in $runAfterTypeSpecific) {
$script = Join-Path $baselineDir $name
if (-not (Test-Path $script)) {
Write-Warning "Deferred script not found: $script"
continue
}
shutdown /a 2>$null
Write-Host "Running deferred baseline: $name"
try {
& $script
} catch {
Write-Warning "Deferred script $name failed: $_"
}
}
Write-Host "Shopfloor setup complete for $pcType."
# Copy utility scripts to SupportUser desktop
$syncScript = Join-Path $setupDir "Shopfloor\sync_intune.bat"
if (Test-Path $syncScript) {
Copy-Item -Path $syncScript -Destination "C:\Users\SupportUser\Desktop\sync_intune.bat" -Force
Write-Host "sync_intune.bat copied to desktop."
foreach ($tool in @('sync_intune.bat', 'Configure-PC.bat')) {
$src = Join-Path $setupDir "Shopfloor\$tool"
if (Test-Path $src) {
Copy-Item -Path $src -Destination "C:\Users\SupportUser\Desktop\$tool" -Force
Write-Host "$tool copied to desktop."
}
}
# Standard PCs get the UDC/eDNC machine number helper
@@ -98,5 +172,13 @@ if ($pcType -eq "Standard") {
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoLogonCount /t REG_DWORD /d 2 /f | Out-Null
Write-Host "Auto-logon set to 2 remaining logins."
Write-Host ""
Write-Host "================================================================"
Write-Host "=== Run-ShopfloorSetup.ps1 complete $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ==="
Write-Host "================================================================"
# Flush transcript before shutdown so the log file is complete on next boot
try { Stop-Transcript | Out-Null } catch {}
Write-Host "Rebooting in 10 seconds..."
shutdown /r /t 10

View File

@@ -21,14 +21,25 @@ $officeApps = @(
@{ Exe = 'POWERPNT.EXE'; Name = 'PowerPoint' }
)
# Office binary location depends on x86 vs x64 install. The standard ShopFloor
# Office ppkg is x86 (GCCH_Prod_SFLD_StdOffice-x86_*) so it lands under
# Program Files (x86), but we check both so the script works either way.
$officeRoot = $null
foreach ($base in @(
# Office binary location varies by install type and version:
# - Office 2019+ / M365 / LTSC Click-to-Run: <PF>\Microsoft Office\root\Office16\
# - Office 2016 MSI (legacy): <PF>\Microsoft Office\Office16\ (no root\)
# - Office 2013 MSI: <PF>\Microsoft Office\Office15\
# - x86 Office on 64-bit Windows: Program Files (x86)\...
# The standard ShopFloor Office ppkg is x86 Click-to-Run so normal case
# is "Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE", but
# we check all variants so the script works across SKUs.
$officeSearchRoots = @(
'C:\Program Files\Microsoft Office\root\Office16',
'C:\Program Files (x86)\Microsoft Office\root\Office16'
)) {
'C:\Program Files (x86)\Microsoft Office\root\Office16',
'C:\Program Files\Microsoft Office\Office16',
'C:\Program Files (x86)\Microsoft Office\Office16',
'C:\Program Files\Microsoft Office\Office15',
'C:\Program Files (x86)\Microsoft Office\Office15'
)
$officeRoot = $null
foreach ($base in $officeSearchRoots) {
if (Test-Path (Join-Path $base 'EXCEL.EXE')) {
$officeRoot = $base
break
@@ -37,6 +48,11 @@ foreach ($base in @(
if (-not $officeRoot) {
Write-Host "No Office install detected - skipping shortcut creation."
Write-Host "Searched:"
foreach ($p in $officeSearchRoots) {
$hit = if (Test-Path $p) { "(dir exists)" } else { "(missing)" }
Write-Host " $p $hit"
}
exit 0
}

View File

@@ -49,14 +49,13 @@ if (-not $isAdmin) {
$publicDesktop = 'C:\Users\Public\Desktop'
$shopfloorToolsDir = Join-Path $publicDesktop 'Shopfloor Tools'
$scriptPath = $MyInvocation.MyCommand.Path
$scriptDir = Split-Path -Parent $scriptPath
# Filenames that always stay at the desktop root regardless of classification.
# End users click these many times a day and an extra folder click is real
# friction. Phase 2 also drops these here as a post-sweep safety net.
$keepAtRoot = @(
'eDNC.lnk',
'NTLARS.lnk'
)
# Empty right now - every shortcut lives inside a category folder for a
# clean end-user desktop. Add entries here if a future shortcut needs to
# stay pinned at root.
$keepAtRoot = @()
# ============================================================================
# Phase 1: Sweep loose shortcuts at desktop root into category folders
@@ -109,17 +108,11 @@ function Invoke-DesktopSweep {
}
}
# Ensure category folders exist
foreach ($cat in $categories.Keys) {
$dir = Join-Path $DesktopPath $cat
if (-not (Test-Path -LiteralPath $dir)) {
try {
New-Item -ItemType Directory -Path $dir -Force -ErrorAction Stop | Out-Null
} catch {
Write-Warning "Failed to create category folder '$cat' : $_"
}
}
}
# Category folders are created LAZILY - only when we're about to move
# something into them - so a Display / Wax-Trace PC without Office
# doesn't get an empty Office\ folder littering the desktop. See
# Remove-EmptyCategoryFolders at the end of the script for the
# post-sweep cleanup pass that finishes the job.
# WScript.Shell for resolving .lnk targets
$shell = $null
@@ -187,7 +180,15 @@ function Invoke-DesktopSweep {
}
if ($category) {
$destDir = Join-Path $DesktopPath $category
$destDir = Join-Path $DesktopPath $category
if (-not (Test-Path -LiteralPath $destDir)) {
try {
New-Item -ItemType Directory -Path $destDir -Force -ErrorAction Stop | Out-Null
} catch {
Write-Warning "Failed to create category folder '$category' : $_"
continue
}
}
$destPath = Join-Path $destDir $item.Name
try {
Move-Item -LiteralPath $item.FullName -Destination $destPath -Force -ErrorAction Stop
@@ -269,17 +270,10 @@ function Find-ExistingLnk {
}
function Add-ShopfloorToolsApps {
if (-not (Test-Path -LiteralPath $shopfloorToolsDir)) {
try {
New-Item -ItemType Directory -Path $shopfloorToolsDir -Force -ErrorAction Stop | Out-Null
} catch {
Write-Warning "Failed to create $shopfloorToolsDir : $_"
return
}
}
# App registry - each entry describes how to materialize one shortcut
# into Shopfloor Tools\.
# into Shopfloor Tools\. Folder creation is deferred until we know we
# have at least one app to put in it (lazy creation, so PC types with
# nothing installed don't get an empty Shopfloor Tools folder).
#
# Kind = 'exe' -> build a fresh .lnk from ExePath
# Kind = 'existing' -> copy an existing .lnk via Find-ExistingLnk
@@ -291,12 +285,29 @@ function Add-ShopfloorToolsApps {
@{ Name = 'Defect_Tracker'; Kind = 'existing'; SourceName = 'Defect_Tracker.lnk' }
)
# Lazy folder creation - only create Shopfloor Tools\ the first time
# we have an app that's actually going to land in it. PC types with
# nothing installed get no empty folder.
$ensureToolsDir = {
if (-not (Test-Path -LiteralPath $shopfloorToolsDir)) {
try {
New-Item -ItemType Directory -Path $shopfloorToolsDir -Force -ErrorAction Stop | Out-Null
return $true
} catch {
Write-Warning "Failed to create $shopfloorToolsDir : $_"
return $false
}
}
return $true
}
foreach ($app in $apps) {
$dest = Join-Path $shopfloorToolsDir "$($app.Name).lnk"
switch ($app.Kind) {
'exe' {
if (Test-Path -LiteralPath $app.ExePath) {
if (-not (& $ensureToolsDir)) { break }
if (New-ShopfloorLnk -Path $dest -Target $app.ExePath) {
Write-Host " created: $($app.Name) -> $dest"
}
@@ -308,6 +319,7 @@ function Add-ShopfloorToolsApps {
'existing' {
$src = Find-ExistingLnk $app.SourceName
if ($src) {
if (-not (& $ensureToolsDir)) { break }
try {
Copy-Item -LiteralPath $src -Destination $dest -Force -ErrorAction Stop
Write-Host " copied: $($app.Name) from $src"
@@ -323,30 +335,56 @@ function Add-ShopfloorToolsApps {
}
# ============================================================================
# Phase 3: Pin eDNC and NTLARS shortcuts at the desktop root
# Phase 3: Remove empty category folders
#
# These are the two shortcuts end users touch most frequently. Allowlisted
# in the sweep, and also force-dropped here (copied from the Shopfloor
# Tools\ versions we just created) so a sweep-and-forget user still ends
# up with them loose at the root.
# If the sweep classified nothing into a given category (e.g. no Office
# on a Display PC, no Shopfloor Tools apps on a kiosk), we don't want an
# empty folder cluttering the desktop. The scheduled-task sweep also runs
# this so categories that go empty between logons self-heal.
# ============================================================================
function Update-DesktopRootPins {
foreach ($name in @('eDNC.lnk', 'NTLARS.lnk')) {
$src = Join-Path $shopfloorToolsDir $name
$dst = Join-Path $publicDesktop $name
if (Test-Path -LiteralPath $src) {
function Remove-EmptyCategoryFolders {
foreach ($cat in @('Office', 'Shopfloor Tools', 'Web Links')) {
$dir = Join-Path $publicDesktop $cat
if (-not (Test-Path -LiteralPath $dir)) { continue }
$contents = @(Get-ChildItem -LiteralPath $dir -Force -ErrorAction SilentlyContinue)
if ($contents.Count -eq 0) {
try {
Copy-Item -LiteralPath $src -Destination $dst -Force -ErrorAction Stop
Write-Host " root: $name"
Remove-Item -LiteralPath $dir -Force -ErrorAction Stop
Write-Host " removed empty: $cat\"
} catch {
Write-Warning "Failed to drop $dst : $_"
Write-Warning "Failed to remove empty $dir : $_"
}
}
}
}
# ============================================================================
# Scheduled task registration (phase 1 re-run at every logon)
# Phase 4: Edge default browser + startup tabs (delegated to 08)
#
# 08-EdgeDefaultBrowser.ps1 resolves startup-tab URLs from .url files on
# the Public Desktop (or in Web Links\ after the sweep). The .url files
# are delivered by DSC AFTER this initial shopfloor setup, so the first
# run at imaging time won't find them (falls back to hardcoded URLs,
# Plant Apps gets skipped). By calling 08 from inside 06, every SYSTEM
# scheduled-task logon re-run of 06 also re-runs 08 — so after DSC drops
# the .url files and the next sweep files them into Web Links\, 08
# picks them up and updates the Edge policy. Self-healing.
# ============================================================================
function Invoke-EdgeDefaultBrowser {
$edgeScript = Join-Path $scriptDir '08-EdgeDefaultBrowser.ps1'
if (-not (Test-Path -LiteralPath $edgeScript)) {
Write-Host " 08-EdgeDefaultBrowser.ps1 not found - skipping"
return
}
try {
& $edgeScript
} catch {
Write-Warning "08-EdgeDefaultBrowser.ps1 failed: $_"
}
}
# ============================================================================
# Scheduled task registration (re-runs 06 at every logon as SYSTEM)
# ============================================================================
function Register-SweepScheduledTask {
param([string]$ScriptPath)
@@ -365,12 +403,16 @@ function Register-SweepScheduledTask {
$trigger = New-ScheduledTaskTrigger -AtLogOn
# Run as whichever user logs in, at Limited (standard) rights. Public
# desktop is writable by BUILTIN\Users so no elevation needed. Using
# the well-known Users group SID so this works on non-English Windows.
# Run as SYSTEM so the script can (a) pass its admin check at the
# top, (b) write to Public Desktop / ProgramData Start Menu / the
# Default User profile without Access Denied, and (c) register the
# next iteration of itself idempotently. Earlier versions used
# -GroupId 'BUILTIN\Users' -RunLevel Limited which fired at logon
# but the script exited immediately on the non-admin check.
$principal = New-ScheduledTaskPrincipal `
-GroupId 'S-1-5-32-545' `
-RunLevel Limited
-UserId 'NT AUTHORITY\SYSTEM' `
-LogonType ServiceAccount `
-RunLevel Highest
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
@@ -399,6 +441,20 @@ function Register-SweepScheduledTask {
# ============================================================================
# Main
# ============================================================================
Write-Host ""
Write-Host "=== Phase 0: create Office shortcuts (if Office is installed) ==="
# Delegated to 05-OfficeShortcuts.ps1. Office is installed via ppkg but
# doesn't finish streaming until AFTER the first reboot, so the first
# imaging-time run of 06 finds no Office and this no-ops. On the second
# logon (after reboot), Office is installed and 05 creates the shortcuts
# so Phase 1 below can sweep them into the Office\ folder.
$officeScript = Join-Path $scriptDir '05-OfficeShortcuts.ps1'
if (Test-Path -LiteralPath $officeScript) {
try { & $officeScript } catch { Write-Warning "05-OfficeShortcuts.ps1 failed: $_" }
} else {
Write-Host " 05-OfficeShortcuts.ps1 not found - skipping"
}
Write-Host ""
Write-Host "=== Phase 1: sweep loose shortcuts into category folders ==="
Invoke-DesktopSweep -DesktopPath $publicDesktop
@@ -408,11 +464,15 @@ Write-Host "=== Phase 2: populate Shopfloor Tools with app shortcuts ==="
Add-ShopfloorToolsApps
Write-Host ""
Write-Host "=== Phase 3: drop eDNC / NTLARS at desktop root ==="
Update-DesktopRootPins
Write-Host "=== Phase 3: remove empty category folders ==="
Remove-EmptyCategoryFolders
Write-Host ""
Write-Host "=== Phase 4: register logon sweeper scheduled task ==="
Write-Host "=== Phase 4: Edge default browser + startup tabs ==="
Invoke-EdgeDefaultBrowser
Write-Host ""
Write-Host "=== Phase 5: register logon sweeper scheduled task ==="
Register-SweepScheduledTask -ScriptPath $scriptPath
exit 0

View File

@@ -7,9 +7,7 @@
# This script does NOT create, move, or copy any shortcuts - all shortcut
# management lives in 06. 07 is the last-mile taskbar config only.
#
# Pin order (left to right): Edge, WJ Shopfloor, UDC, eDNC, Defect_Tracker.
# NTLARS is intentionally not pinned - per the original spec it lives on
# the desktop root only.
# Pin order (left to right): Edge, WJ Shopfloor, UDC, eDNC, NTLARS, Defect_Tracker.
#
# LayoutModification.xml is written to the Default User profile shell
# directory, which means the pins apply on FIRST LOGON of any new user

View File

@@ -196,7 +196,7 @@ function Resolve-StartupUrl {
# Tab order as requested: Plant Apps, Shop Floor Homepage, Shopfloor Dashboard
$startupTabs = @()
$plantApps = Resolve-StartupUrl -BaseName 'Plant Apps'
$plantApps = Resolve-StartupUrl -BaseName 'Plant Apps' -Fallback 'https://mes-wjefferson.apps.lr.geaerospace.net/run/?app_name=Plant%20Applications'
if ($plantApps) { $startupTabs += $plantApps }
$shopFloorHome = Resolve-StartupUrl -BaseName 'WJ Shop Floor Homepage' -Fallback 'http://tsgwp00524.logon.ds.ge.com/'

View File

@@ -0,0 +1,158 @@
# Check-MachineNumber.ps1 - Logon-triggered check for placeholder machine
# number. If UDC or eDNC are still at 9999, pops an InputBox for the user
# to enter the real number. On success, unregisters the scheduled task so
# the prompt never appears again.
#
# Runs as the LOGGED-IN USER (not SYSTEM) because it needs to show GUI.
# Writing to ProgramData + HKLM is possible because 02-MachineNumberACLs.ps1
# pre-granted BUILTIN\Users write access on those specific targets during
# imaging.
#
# Registered/unregistered by Configure-PC.ps1 (item 6 in the toggle list).
# --- Transcript logging ---
$logDir = 'C:\Logs\SFLD'
if (-not (Test-Path $logDir)) {
try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch { $logDir = $env:TEMP }
}
$transcriptPath = Join-Path $logDir 'Check-MachineNumber.log'
try { Start-Transcript -Path $transcriptPath -Append -Force | Out-Null } catch {}
Write-Host "Check-MachineNumber.ps1 starting $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host "Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
Add-Type -AssemblyName Microsoft.VisualBasic
Add-Type -AssemblyName System.Windows.Forms
$taskName = 'Check Machine Number'
$udcSettingsPath = 'C:\ProgramData\UDC\udc_settings.json'
$udcExePath = 'C:\Program Files\UDC\UDC.exe'
$ednRegPath = 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General'
$site = 'West Jefferson'
# --- Read current values ---
$currentUdc = $null
$currentEdnc = $null
if (Test-Path $udcSettingsPath) {
try {
$json = Get-Content $udcSettingsPath -Raw | ConvertFrom-Json
$currentUdc = $json.GeneralSettings.MachineNumber
} catch {}
}
if (Test-Path $ednRegPath) {
try {
$currentEdnc = (Get-ItemProperty -Path $ednRegPath -Name MachineNo -ErrorAction Stop).MachineNo
} catch {}
}
# --- Check if placeholder ---
Write-Host "UDC machine number: $(if ($currentUdc) { $currentUdc } else { '(not found)' })"
Write-Host "eDNC machine number: $(if ($currentEdnc) { $currentEdnc } else { '(not found)' })"
if ($currentUdc -ne '9999' -and $currentEdnc -ne '9999') {
Write-Host "Machine number is set (not 9999). Unregistering task and exiting."
try {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
} catch {}
try { Stop-Transcript | Out-Null } catch {}
exit 0
}
Write-Host "Placeholder 9999 detected - showing prompt."
# --- Show prompt ---
$promptLines = @()
$promptLines += "The machine number on this PC is still set to the"
$promptLines += "placeholder value (9999). Please enter the correct"
$promptLines += "machine number for this workstation."
$promptLines += ""
if ($currentUdc) { $promptLines += "Current UDC: $currentUdc" }
if ($currentEdnc) { $promptLines += "Current eDNC: $currentEdnc" }
$promptLines += ""
$promptLines += "Enter the new Machine Number:"
$prompt = $promptLines -join "`n"
$new = [Microsoft.VisualBasic.Interaction]::InputBox($prompt, "Set Machine Number", "")
if ([string]::IsNullOrWhiteSpace($new)) {
Write-Host "User cancelled. Will prompt again next logon."
try { Stop-Transcript | Out-Null } catch {}
exit 0
}
$new = $new.Trim()
# --- Validate ---
if ($new -notmatch '^\d+$') {
Write-Host "Invalid input: '$new' (not digits only). Will prompt again next logon."
[System.Windows.Forms.MessageBox]::Show(
"Machine number must be digits only.`n`nYou entered: '$new'`n`nThe prompt will appear again at next logon.",
"Invalid Machine Number",
[System.Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]::Error
) | Out-Null
try { Stop-Transcript | Out-Null } catch {}
exit 0
}
# --- Update UDC ---
$results = @()
if (Test-Path $udcSettingsPath) {
# Stop UDC first
Get-Process UDC -ErrorAction SilentlyContinue | ForEach-Object {
try { $_.Kill(); $_.WaitForExit(5000) | Out-Null } catch {}
}
Start-Sleep -Seconds 1
try {
$json = Get-Content $udcSettingsPath -Raw | ConvertFrom-Json
$json.GeneralSettings.MachineNumber = $new
$json | ConvertTo-Json -Depth 99 | Set-Content -Path $udcSettingsPath -Encoding UTF8
$results += "UDC updated to $new"
} catch {
$results += "UDC FAILED: $_"
}
}
# --- Update eDNC ---
if (Test-Path $ednRegPath) {
try {
Set-ItemProperty -Path $ednRegPath -Name MachineNo -Value $new -Type String -Force
$results += "eDNC updated to $new"
} catch {
$results += "eDNC FAILED: $_"
}
}
# --- Relaunch UDC ---
if (Test-Path $udcExePath) {
try {
Start-Process -FilePath $udcExePath -ArgumentList @('-site', "`"$site`"", '-machine', $new)
} catch {}
}
# --- Show result ---
$summary = ($results -join "`n") + "`n`nTo apply eDNC changes, restart any running DncMain.exe."
[System.Windows.Forms.MessageBox]::Show(
$summary,
"Machine Number Updated",
[System.Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]::Information
) | Out-Null
# --- Unregister task on success ---
Write-Host "Results: $($results -join '; ')"
$anyFail = $results | Where-Object { $_ -match 'FAILED' }
if (-not $anyFail) {
Write-Host "All updates succeeded. Unregistering logon task."
try {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
} catch {}
} else {
Write-Host "Some updates failed. Task stays registered - will prompt again next logon."
}
Write-Host "Check-MachineNumber.ps1 finished $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
try { Stop-Transcript | Out-Null } catch {}
exit 0

View File

@@ -0,0 +1,23 @@
@echo off
REM Configure-PC.bat - Launches Configure-PC.ps1 in an elevated PowerShell.
REM SupportUser desktop tool for setting machine number + auto-startup items.
title Configure PC
set "SCRIPT=%~dp0Configure-PC.ps1"
if exist "%SCRIPT%" goto :found
set "SCRIPT=C:\Users\SupportUser\Desktop\Configure-PC.ps1"
if exist "%SCRIPT%" goto :found
set "SCRIPT=C:\Enrollment\shopfloor-setup\Shopfloor\Configure-PC.ps1"
if exist "%SCRIPT%" goto :found
echo ERROR: Configure-PC.ps1 not found
pause
exit /b 1
:found
echo Launching: %SCRIPT%
powershell -NoProfile -Command "Start-Process powershell.exe -Verb RunAs -ArgumentList '-NoProfile','-NoExit','-ExecutionPolicy','Bypass','-File','%SCRIPT%'"
exit /b

View File

@@ -0,0 +1,414 @@
# Configure-PC.ps1 - Interactive tool for SupportUser to configure a
# shopfloor PC after imaging. Two sections:
#
# 1. Machine number - if UDC/eDNC are still at placeholder 9999, prompt
# to set the real number right now.
#
# 2. Auto-startup items + machine-number logon prompt - toggle which apps
# start automatically for all users, and optionally register a logon
# task that prompts STANDARD users for the machine number if it's
# still 9999 when they log in (for the case where SupportUser skips
# it here and the end user needs to set it themselves).
#
# Startup .lnk files go in the AllUsers Startup folder:
# C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\
#
# Run via Configure-PC.bat on the SupportUser desktop.
$ErrorActionPreference = 'Continue'
# --- Transcript logging ---
$logDir = 'C:\Logs\SFLD'
if (-not (Test-Path $logDir)) {
try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch { $logDir = $env:TEMP }
}
$transcriptPath = Join-Path $logDir 'Configure-PC.log'
try { Start-Transcript -Path $transcriptPath -Append -Force | Out-Null } catch {}
Write-Host "Transcript: $transcriptPath"
Write-Host "Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host "Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
$startupDir = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup'
$publicDesktop = 'C:\Users\Public\Desktop'
# ============================================================================
# Helpers
# ============================================================================
function Get-UrlFromFile {
param([string]$BaseName)
$candidates = @(
(Join-Path $publicDesktop "$BaseName.url"),
(Join-Path (Join-Path $publicDesktop 'Web Links') "$BaseName.url")
)
foreach ($c in $candidates) {
if (-not (Test-Path -LiteralPath $c)) { continue }
try {
$content = Get-Content -LiteralPath $c -ErrorAction Stop
$urlLine = $content | Where-Object { $_ -match '^URL=' } | Select-Object -First 1
if ($urlLine) { return ($urlLine -replace '^URL=', '').Trim() }
} catch {}
}
return $null
}
function New-StartupLnk {
param(
[string]$Name,
[string]$Target,
[string]$Arguments = '',
[string]$WorkingDirectory = ''
)
$path = Join-Path $startupDir "$Name.lnk"
$wsh = New-Object -ComObject WScript.Shell
try {
$sc = $wsh.CreateShortcut($path)
$sc.TargetPath = $Target
if ($Arguments) { $sc.Arguments = $Arguments }
if ($WorkingDirectory) { $sc.WorkingDirectory = $WorkingDirectory }
elseif ($Target) {
$parent = Split-Path -Parent $Target
if ($parent) { $sc.WorkingDirectory = $parent }
}
$sc.Save()
return $true
} catch {
Write-Warning "Failed to create startup shortcut '$Name': $_"
return $false
} finally {
try { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($wsh) | Out-Null } catch {}
}
}
# ============================================================================
# Machine number - read current state
# ============================================================================
$udcSettingsPath = 'C:\ProgramData\UDC\udc_settings.json'
$udcExePath = 'C:\Program Files\UDC\UDC.exe'
$ednRegPath = 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General'
$currentUdc = $null
$currentEdnc = $null
if (Test-Path $udcSettingsPath) {
try {
$json = Get-Content $udcSettingsPath -Raw | ConvertFrom-Json
$currentUdc = $json.GeneralSettings.MachineNumber
} catch {}
}
if (Test-Path $ednRegPath) {
try {
$currentEdnc = (Get-ItemProperty -Path $ednRegPath -Name MachineNo -ErrorAction Stop).MachineNo
} catch {}
}
$needsMachineNumber = ($currentUdc -eq '9999' -or $currentEdnc -eq '9999')
# ============================================================================
# Edge path (for URL-based startup items)
# ============================================================================
$edgePath = @(
'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe',
'C:\Program Files\Microsoft\Edge\Application\msedge.exe'
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
# ============================================================================
# Startup item definitions
# ============================================================================
$items = @(
@{
Num = 1
Label = 'UDC'
Detail = 'UDC.exe'
Available = (Test-Path 'C:\Program Files\UDC\UDC.exe')
CreateLnk = {
return (New-StartupLnk -Name 'UDC' -Target 'C:\Program Files\UDC\UDC.exe')
}
}
@{
Num = 2
Label = 'eDNC'
Detail = 'DncMain.exe'
Available = (Test-Path 'C:\Program Files (x86)\Dnc\bin\DncMain.exe')
CreateLnk = {
return (New-StartupLnk -Name 'eDNC' -Target 'C:\Program Files (x86)\Dnc\bin\DncMain.exe')
}
}
@{
Num = 3
Label = 'Defect Tracker'
Detail = 'ClickOnce app'
Available = $true
CreateLnk = {
$src = @(
(Join-Path $publicDesktop 'Defect_Tracker.lnk'),
(Join-Path (Join-Path $publicDesktop 'Shopfloor Tools') 'Defect_Tracker.lnk')
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
if ($src) {
$dst = Join-Path $startupDir 'Defect Tracker.lnk'
try { Copy-Item -LiteralPath $src -Destination $dst -Force; return $true }
catch { Write-Warning "Failed to copy Defect Tracker: $_"; return $false }
} else {
Write-Warning "Defect_Tracker.lnk not found on desktop"
return $false
}
}
}
@{
Num = 4
Label = 'WJ Shopfloor'
Detail = 'HostExplorer session'
Available = $true
CreateLnk = {
$src = @(
(Join-Path $publicDesktop 'WJ Shopfloor.lnk'),
(Join-Path (Join-Path $publicDesktop 'Shopfloor Tools') 'WJ Shopfloor.lnk')
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
if ($src) {
$dst = Join-Path $startupDir 'WJ Shopfloor.lnk'
try { Copy-Item -LiteralPath $src -Destination $dst -Force; return $true }
catch { Write-Warning "Failed to copy WJ Shopfloor: $_"; return $false }
} else {
Write-Warning "WJ Shopfloor.lnk not found on desktop"
return $false
}
}
}
@{
Num = 5
Label = 'Plant Apps'
Detail = 'opens in Edge'
Available = [bool]$edgePath
CreateLnk = {
$fallback = 'https://mes-wjefferson.apps.lr.geaerospace.net/run/?app_name=Plant%20Applications'
$url = Get-UrlFromFile 'Plant Apps'
if (-not $url) {
Write-Host " Plant Apps .url not found on desktop, using known URL."
$url = $fallback
}
Write-Host " URL: $url"
return (New-StartupLnk -Name 'Plant Apps' -Target $edgePath -Arguments "--new-window `"$url`"")
}
}
)
# Machine-number logon task is item 6
$machineNumTaskName = 'Check Machine Number'
# ============================================================================
# Interactive UI
# ============================================================================
Clear-Host
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Configure PC" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
# --- Section 1: Machine number ---
Write-Host ""
Write-Host " MACHINE NUMBER" -ForegroundColor Yellow
Write-Host " ----------------------------------------"
if ($needsMachineNumber) {
if ($currentUdc) { Write-Host " UDC : $currentUdc" -ForegroundColor Red }
if ($currentEdnc) { Write-Host " eDNC : $currentEdnc" -ForegroundColor Red }
Write-Host ""
$newNum = Read-Host " Enter new machine number (digits only, Enter to skip)"
$newNum = $newNum.Trim()
if ($newNum -and $newNum -match '^\d+$') {
$updated = @()
# Stop UDC before editing its JSON
Get-Process UDC -ErrorAction SilentlyContinue | ForEach-Object {
try { $_.Kill(); $_.WaitForExit(5000) | Out-Null } catch {}
}
Start-Sleep -Seconds 1
# UDC JSON
if (Test-Path $udcSettingsPath) {
try {
$json = Get-Content $udcSettingsPath -Raw | ConvertFrom-Json
$json.GeneralSettings.MachineNumber = $newNum
$json | ConvertTo-Json -Depth 99 | Set-Content -Path $udcSettingsPath -Encoding UTF8
Write-Host " UDC : $currentUdc -> $newNum" -ForegroundColor Green
$updated += 'UDC'
$currentUdc = $newNum
} catch { Write-Warning " UDC update failed: $_" }
}
# eDNC registry
if (Test-Path $ednRegPath) {
try {
Set-ItemProperty -Path $ednRegPath -Name MachineNo -Value $newNum -Type String -Force
Write-Host " eDNC : $currentEdnc -> $newNum" -ForegroundColor Green
$updated += 'eDNC'
$currentEdnc = $newNum
} catch { Write-Warning " eDNC update failed: $_" }
}
# Relaunch UDC
if ((Test-Path $udcExePath) -and $updated -contains 'UDC') {
try {
Start-Process -FilePath $udcExePath -ArgumentList @('-site', '"West Jefferson"', '-machine', $newNum)
Write-Host " UDC.exe relaunched."
} catch {}
}
$needsMachineNumber = ($currentUdc -eq '9999' -or $currentEdnc -eq '9999')
} elseif ($newNum) {
Write-Host " Invalid (digits only). Skipped." -ForegroundColor Yellow
} else {
Write-Host " Skipped (still 9999)." -ForegroundColor DarkGray
}
} else {
if ($currentUdc) { Write-Host " UDC : $currentUdc" -ForegroundColor Green }
if ($currentEdnc) { Write-Host " eDNC : $currentEdnc" -ForegroundColor Green }
}
# --- Section 2: Startup items + machine-number logon task ---
Write-Host ""
Write-Host " AUTO-STARTUP ITEMS" -ForegroundColor Yellow
Write-Host " ----------------------------------------"
Write-Host " Toggle which items start at user logon:"
Write-Host ""
$existingStartup = @(Get-ChildItem -LiteralPath $startupDir -Filter '*.lnk' -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty BaseName)
foreach ($item in $items) {
$on = if ($existingStartup -contains $item.Label) { '[ON]' } else { '[ ]' }
$avail = if ($item.Available) { '' } else { ' (not installed)' }
Write-Host " $($item.Num). $on $($item.Label) - $($item.Detail)$avail"
}
# Item 6: machine number logon prompt
$machineNumTaskExists = [bool](Get-ScheduledTask -TaskName $machineNumTaskName -ErrorAction SilentlyContinue)
$mnOn = if ($machineNumTaskExists) { '[ON]' } else { '[ ]' }
Write-Host " 6. $mnOn Prompt standard user for machine number if 9999"
Write-Host ""
Write-Host " Enter numbers to toggle (e.g. 1,2,6), Enter to keep current:"
$selection = Read-Host " Selection"
$selection = $selection.Trim()
if ($selection) {
$selected = $selection -split '[,\s]+' | Where-Object { $_ -match '^\d+$' } | ForEach-Object { [int]$_ }
# Process startup items 1-5
foreach ($item in $items) {
if ($selected -notcontains $item.Num) { continue }
if (-not $item.Available) {
Write-Host " $($item.Label): not installed, skipping" -ForegroundColor DarkGray
continue
}
$existingLnk = Join-Path $startupDir "$($item.Label).lnk"
if (Test-Path -LiteralPath $existingLnk) {
try {
Remove-Item -LiteralPath $existingLnk -Force
Write-Host " $($item.Label): REMOVED from startup" -ForegroundColor Yellow
} catch { Write-Warning " Failed to remove $($item.Label): $_" }
} else {
$result = & $item.CreateLnk
if ($result) {
Write-Host " $($item.Label): ADDED to startup" -ForegroundColor Green
}
}
}
# Process item 6: machine number logon task
if ($selected -contains 6) {
if ($machineNumTaskExists) {
# Toggle OFF
try {
Unregister-ScheduledTask -TaskName $machineNumTaskName -Confirm:$false -ErrorAction Stop
Write-Host " Machine number logon prompt: REMOVED" -ForegroundColor Yellow
$machineNumTaskExists = $false
} catch { Write-Warning " Failed to remove task: $_" }
} else {
# Toggle ON - register logon task
# The task needs to run as the logged-in user (for GUI), but
# writing to HKLM + ProgramData requires the ACLs we pre-grant
# during imaging (see task 7 / ACL pre-grant script).
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$checkScript = Join-Path $scriptDir 'Check-MachineNumber.ps1'
if (-not (Test-Path -LiteralPath $checkScript)) {
# Fallback: check enrollment staging dir
$checkScript = 'C:\Enrollment\shopfloor-setup\Shopfloor\Check-MachineNumber.ps1'
}
if (Test-Path -LiteralPath $checkScript) {
try {
$action = New-ScheduledTaskAction `
-Execute 'powershell.exe' `
-Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Normal -File `"$checkScript`""
$trigger = New-ScheduledTaskTrigger -AtLogOn
# Run as the logged-in user (needs GUI for InputBox), NOT
# SYSTEM (SYSTEM can't show UI to the user's desktop).
$principal = New-ScheduledTaskPrincipal `
-GroupId 'S-1-5-32-545' `
-RunLevel Limited
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 5)
Register-ScheduledTask `
-TaskName $machineNumTaskName `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Force `
-ErrorAction Stop | Out-Null
Write-Host " Machine number logon prompt: ENABLED" -ForegroundColor Green
Write-Host " (will auto-disable after machine number is set)" -ForegroundColor DarkGray
$machineNumTaskExists = $true
} catch {
Write-Warning " Failed to register task: $_"
}
} else {
Write-Warning " Check-MachineNumber.ps1 not found at $checkScript"
}
}
}
} else {
Write-Host " No changes." -ForegroundColor DarkGray
}
# --- Summary ---
Write-Host ""
Write-Host " CURRENT STATE" -ForegroundColor Cyan
Write-Host " ----------------------------------------"
$final = @(Get-ChildItem -LiteralPath $startupDir -Filter '*.lnk' -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty BaseName)
Write-Host " Startup items:"
if ($final.Count -eq 0) {
Write-Host " (none)"
} else {
foreach ($f in $final) { Write-Host " - $f" }
}
$mnState = if ($machineNumTaskExists) { 'ON (prompts at logon if 9999)' } else { 'OFF' }
Write-Host " Machine number logon prompt: $mnState"
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Done" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Completed: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
try { Stop-Transcript | Out-Null } catch {}
Write-Host "Press any key to close..."
try { [void][Console]::ReadKey($true) } catch { [void](Read-Host) }

View File

@@ -0,0 +1,55 @@
# 02-MachineNumberACLs.ps1 - Pre-grant write access on the UDC settings
# file and eDNC registry key so that STANDARD (non-admin) users can update
# the machine number via the Check-MachineNumber logon task without
# elevation or a UAC prompt.
#
# Runs during imaging as admin (type-specific Standard phase, after
# 01-eDNC.ps1 has installed DnC). Only touches Standard PCs.
#
# What gets opened up (narrow scope, not blanket admin):
# - C:\ProgramData\UDC\udc_settings.json -> BUILTIN\Users : Modify
# - HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General
# -> BUILTIN\Users : SetValue
Write-Host "02-MachineNumberACLs.ps1 starting $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host "Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
Write-Host ""
Write-Host "Setting ACLs for standard-user machine number access..."
# --- UDC settings JSON ---
$udcJson = 'C:\ProgramData\UDC\udc_settings.json'
if (Test-Path -LiteralPath $udcJson) {
try {
$acl = Get-Acl -LiteralPath $udcJson
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
'BUILTIN\Users', 'Modify', 'Allow')
$acl.AddAccessRule($rule)
Set-Acl -LiteralPath $udcJson -AclObject $acl -ErrorAction Stop
Write-Host " UDC JSON: BUILTIN\Users granted Modify on $udcJson"
} catch {
Write-Warning " Failed to set ACL on $udcJson : $_"
}
} else {
Write-Host " UDC JSON not found at $udcJson - skipping (UDC not installed?)" -ForegroundColor DarkGray
}
# --- eDNC registry key ---
$ednRegPathWin = 'SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General'
try {
$regKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($ednRegPathWin, $true)
if ($regKey) {
$regSec = $regKey.GetAccessControl()
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(
'BUILTIN\Users', 'SetValue', 'Allow')
$regSec.AddAccessRule($rule)
$regKey.SetAccessControl($regSec)
$regKey.Close()
Write-Host " eDNC reg: BUILTIN\Users granted SetValue on HKLM:\$ednRegPathWin"
} else {
Write-Host " eDNC registry key not found - skipping (eDNC not installed?)" -ForegroundColor DarkGray
}
} catch {
Write-Warning " Failed to set ACL on HKLM:\$ednRegPathWin : $_"
}
Write-Host "ACL setup complete."