diff --git a/playbook/shopfloor-setup/Shopfloor/Configure-PC.ps1 b/playbook/shopfloor-setup/Shopfloor/Configure-PC.ps1 index 5948f2e..3091ff1 100644 --- a/playbook/shopfloor-setup/Shopfloor/Configure-PC.ps1 +++ b/playbook/shopfloor-setup/Shopfloor/Configure-PC.ps1 @@ -285,8 +285,13 @@ if ($null -ne $cfgItems -and $cfgItems.Count -gt 0) { ) } -# Machine-number logon task is item 6 -$machineNumTaskName = 'Check Machine Number' +# Machine-number logon tasks (item 6 toggle controls both) +# 2026-05-24: split into user-context Prompt + SYSTEM-context Apply. +# 'Check Machine Number' is the legacy single-task name kept for +# backward-detection on bays imaged before the split. +$machineNumPromptTask = 'Prompt Machine Number' +$machineNumApplyTask = 'Apply Machine Number' +$machineNumLegacyTask = 'Check Machine Number' # ============================================================================ # Interactive UI @@ -354,8 +359,12 @@ foreach ($item in $items) { Write-Host " $($item.Num). $on $($item.Label) - $($item.Detail)$avail" } -# Item 6: machine number logon prompt -$machineNumTaskExists = [bool](Get-ScheduledTask -TaskName $machineNumTaskName -ErrorAction SilentlyContinue) +# Item 6: machine number logon prompt. "ON" if EITHER the new Prompt task OR +# the legacy Check Machine Number task is registered. +$machineNumTaskExists = [bool]( + (Get-ScheduledTask -TaskName $machineNumPromptTask -ErrorAction SilentlyContinue) -or + (Get-ScheduledTask -TaskName $machineNumLegacyTask -ErrorAction SilentlyContinue) +) $mnOn = if ($machineNumTaskExists) { '[ON]' } else { '[ ]' } Write-Host " 6. $mnOn Prompt standard user for machine number if 9999" @@ -419,62 +428,47 @@ if ($selection) { # 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: $_" } + # Toggle OFF - remove Prompt + Apply (new design) AND the legacy + # Check Machine Number task name (in case this bay was imaged + # before the split and never re-imaged). + $removed = @() + foreach ($t in @($machineNumPromptTask, $machineNumApplyTask, $machineNumLegacyTask)) { + try { + if (Get-ScheduledTask -TaskName $t -ErrorAction SilentlyContinue) { + Unregister-ScheduledTask -TaskName $t -Confirm:$false -ErrorAction Stop + $removed += $t + } + } catch { Write-Warning " Failed to remove '$t': $_" } + } + if ($removed) { + Write-Host " Machine number logon prompt: REMOVED ($($removed -join ', '))" -ForegroundColor Yellow + } + $machineNumTaskExists = $false } 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). + # Defer task registration to the shared registrar so this code + # path always matches the imaging-time path. Registrar installs + # BOTH the user-context "Prompt Machine Number" task and the + # SYSTEM-context "Apply Machine Number" task, sets the SDDL on + # Apply so Limited users can schtasks /run it, and cleans up + # any legacy "Check Machine Number" task name. $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' + $registrar = Join-Path $scriptDir 'Register-CheckMachineNumberTask.ps1' + if (-not (Test-Path -LiteralPath $registrar)) { + $registrar = 'C:\Enrollment\shopfloor-setup\Shopfloor\Register-CheckMachineNumberTask.ps1' } - - if (Test-Path -LiteralPath $checkScript) { + if (Test-Path -LiteralPath $registrar) { 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 - + & $registrar Write-Host " Machine number logon prompt: ENABLED" -ForegroundColor Green - Write-Host " (will auto-disable after machine number is set)" -ForegroundColor DarkGray + Write-Host " (Prompt user-task + Apply SYSTEM-task registered;" -ForegroundColor DarkGray + Write-Host " will auto-disable after machine number is set)" -ForegroundColor DarkGray $machineNumTaskExists = $true } catch { - Write-Warning " Failed to register task: $_" + Write-Warning " Register-CheckMachineNumberTask failed: $_" } } else { - Write-Warning " Check-MachineNumber.ps1 not found at $checkScript" + Write-Warning " Register-CheckMachineNumberTask.ps1 not found at $registrar" } } } diff --git a/playbook/shopfloor-setup/gea-shopfloor-nocollections/02-MachineNumberACLs.ps1 b/playbook/shopfloor-setup/gea-shopfloor-nocollections/02-MachineNumberACLs.ps1 index 96f1a5d..ce841f8 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-nocollections/02-MachineNumberACLs.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-nocollections/02-MachineNumberACLs.ps1 @@ -1,81 +1,29 @@ -# 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. +# 02-MachineNumberACLs.ps1 - NO-OP (deprecated 2026-05-24). # -# Runs during imaging as admin (type-specific Standard phase, after -# 01-eDNC.ps1 has installed DnC). Only touches Standard PCs. +# This script used to grant BUILTIN\Users SetValue on the eDNC reg key +# and Modify on the UDC ProgramData dir so the logged-in user could +# update machine number from the Check-MachineNumber logon dialog without +# elevation. # -# 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 +# That design had two flaws: +# 1. Security hole - any logged-in user could overwrite the machine- +# identity reg key. +# 2. Fragile - ACL grants raced with eDNC install timing on some bays; +# the OpenSubKey call returned null + the grant was silently skipped, +# leaving Check-MachineNumber unable to update the bay (yet the old +# Update-MachineNumber.ps1 reported success anyway because +# Set-ItemProperty's PermissionDenied is non-terminating). +# +# Replaced by the two-task design in Register-CheckMachineNumberTask.ps1: +# - "Prompt Machine Number" : user-context GUI, no privileges +# - "Apply Machine Number" : SYSTEM-context worker, full HKLM access +# +# Left as a no-op so Stage-Dispatcher / Run-ShopfloorSetup discovery +# patterns don't have to be updated. Existing bays' ACL grants are still +# present and harmless (the SYSTEM Apply task ignores them). -# --- Transcript --- $logDir = 'C:\Logs\SFLD' if (-not (Test-Path $logDir)) { try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch {} } try { Start-Transcript -Path (Join-Path $logDir '02-MachineNumberACLs.log') -Append -Force | Out-Null } catch {} - -# --- Skip on Timeclock sub-type (no UDC/eDNC to grant ACLs for) --- -$subtypeFile = 'C:\Enrollment\pc-subtype.txt' -if (Test-Path $subtypeFile) { - $subtype = (Get-Content $subtypeFile -First 1 -ErrorAction SilentlyContinue).Trim() - if ($subtype -eq 'Timeclock') { - Write-Host "02-MachineNumberACLs: skipped (Standard-Timeclock)" - try { Stop-Transcript | Out-Null } catch {} - return - } -} - -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 directory --- -# Set ACL on the DIRECTORY (not the file) with inheritance so that -# udc_settings.json inherits the permission whenever UDC.exe creates it. -# UDC_Setup.exe is killed by KillAfterDetection before UDC.exe writes the -# JSON, so the file doesn't exist at this point. Directory-level ACL with -# ContainerInherit + ObjectInherit covers any file created inside later. -$udcDir = 'C:\ProgramData\UDC' -if (Test-Path -LiteralPath $udcDir) { - try { - $acl = Get-Acl -LiteralPath $udcDir - $rule = New-Object System.Security.AccessControl.FileSystemAccessRule( - 'BUILTIN\Users', 'Modify', - ([System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor - [System.Security.AccessControl.InheritanceFlags]::ObjectInherit), - [System.Security.AccessControl.PropagationFlags]::None, - 'Allow') - $acl.AddAccessRule($rule) - Set-Acl -LiteralPath $udcDir -AclObject $acl -ErrorAction Stop - Write-Host " UDC dir: BUILTIN\Users granted Modify (inherited) on $udcDir" - } catch { - Write-Warning " Failed to set ACL on $udcDir : $_" - } -} else { - Write-Host " UDC dir not found at $udcDir - 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." +Write-Host "02-MachineNumberACLs.ps1: no-op (replaced by SYSTEM Apply task - see Register-CheckMachineNumberTask.ps1)" try { Stop-Transcript | Out-Null } catch {}