Files
pxe-server/playbook/shopfloor-setup/Shopfloor/Configure-PC.ps1
cproudlock 97b9e58d23 Configure-PC + nocollections ACL: align with two-task machine number design
- gea-shopfloor-nocollections/02-MachineNumberACLs.ps1: gut to no-op
  matching the collections variant. SYSTEM Apply task no longer needs
  per-user ACLs on the eDNC reg key or UDC ProgramData dir.

- Configure-PC.ps1 item 6 (Machine number logon prompt toggle):
  Stop duplicating Register-ScheduledTask logic inline. Call the
  shared Register-CheckMachineNumberTask.ps1 registrar so both the
  Prompt user-task and Apply SYSTEM-task are installed with matching
  SDDL config. Existence check now treats EITHER the new "Prompt
  Machine Number" task OR the legacy "Check Machine Number" task as
  "ON" so old bays still register correctly. Toggle OFF unregisters
  all three names (Prompt + Apply + legacy) for clean removal.
2026-05-24 18:36:35 -04:00

505 lines
21 KiB
PowerShell

# 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.
param(
# When set, only show the machine number section (skip startup items).
# Used by sync_intune -AsTask after completion -- startup items are
# already auto-applied by 06-OrganizeDesktop from the PC profile.
[switch]$MachineNumberOnly
)
$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)"
# Load site config + PC profile (resolves pc-type.txt + pc-subtype.txt
# into a profile from site-config.json's pcProfiles section)
. "$PSScriptRoot\lib\Get-PCProfile.ps1"
. "$PSScriptRoot\lib\Update-MachineNumber.ps1"
$startupDir = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup'
$publicDesktop = 'C:\Users\Public\Desktop'
$overridesPath = 'C:\ProgramData\GE\Shopfloor\startup-overrides.json'
# Persist user-toggled startup overrides so the logon-triggered sweep
# (06-OrganizeDesktop.ps1 Phase 3) doesn't re-create .lnks the tech
# explicitly removed. Labels in $suppressed are the ones currently OFF.
function Get-SuppressedStartup {
if (-not (Test-Path -LiteralPath $overridesPath)) { return @() }
try {
$data = Get-Content -LiteralPath $overridesPath -Raw | ConvertFrom-Json
if ($data.suppressed) { return @($data.suppressed) }
} catch {
Write-Warning " Failed to parse $overridesPath : $_"
}
return @()
}
function Set-SuppressedStartup {
param([string[]]$Labels)
$dir = Split-Path -Parent $overridesPath
if (-not (Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
}
$payload = @{ suppressed = @($Labels | Sort-Object -Unique) }
$payload | ConvertTo-Json | Set-Content -LiteralPath $overridesPath -Encoding UTF8
}
# ============================================================================
# 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
# ============================================================================
$currentMN = Get-CurrentMachineNumber
$currentUdc = $currentMN.Udc
$currentEdnc = $currentMN.Ednc
$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
# Resolved from: pcProfile.startupItems > siteConfig.startupItems > hardcoded
# ============================================================================
$cfgItems = Get-ProfileValue 'startupItems'
if ($null -ne $cfgItems -and $cfgItems.Count -gt 0) {
$items = @()
$num = 0
foreach ($si in $cfgItems) {
$num++
switch ($si.type) {
'exe' {
$target = $si.target
$items += @{
Num = $num
Label = $si.label
Detail = (Split-Path -Leaf $target)
Available = (Test-Path $target)
CreateLnk = [scriptblock]::Create("return (New-StartupLnk -Name '$($si.label)' -Target '$target')")
}
}
'existing' {
$srcLnk = $si.sourceLnk
$label = $si.label
$items += @{
Num = $num
Label = $label
Detail = 'shortcut'
Available = $true
CreateLnk = [scriptblock]::Create(@"
`$src = @(
(Join-Path `$publicDesktop '$srcLnk'),
(Join-Path (Join-Path `$publicDesktop 'Shopfloor Tools') '$srcLnk')
) | Where-Object { Test-Path -LiteralPath `$_ } | Select-Object -First 1
if (`$src) {
`$dst = Join-Path `$startupDir '$label.lnk'
try { Copy-Item -LiteralPath `$src -Destination `$dst -Force; return `$true }
catch { Write-Warning "Failed to copy $label`: `$_"; return `$false }
} else {
Write-Warning "$srcLnk not found on desktop"
return `$false
}
"@)
}
}
'url' {
$urlKey = $si.urlKey
$label = $si.label
$fallback = if ($urlKey -and $siteConfig.urls) { $siteConfig.urls.$urlKey } else { '' }
$items += @{
Num = $num
Label = $label
Detail = 'opens in Edge'
Available = [bool]$edgePath
CreateLnk = [scriptblock]::Create(@"
`$fallback = '$fallback'
`$url = Get-UrlFromFile '$label'
if (-not `$url) {
Write-Host " $label .url not found on desktop, using known URL."
`$url = `$fallback
}
Write-Host " URL: `$url"
return (New-StartupLnk -Name '$label' -Target `$edgePath -Arguments "--new-window ```"`$url```"")
"@)
}
}
}
}
} else {
$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 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
# ============================================================================
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+$') {
$site = if ($siteConfig) { $siteConfig.siteName } else { 'West Jefferson' }
$mnResult = Update-MachineNumber -NewNumber $newNum -Site $site
if ($mnResult.UdcUpdated) {
Write-Host " UDC : $currentUdc -> $newNum" -ForegroundColor Green
$currentUdc = $newNum
}
if ($mnResult.EdncUpdated) {
Write-Host " eDNC : $currentEdnc -> $newNum" -ForegroundColor Green
$currentEdnc = $newNum
}
foreach ($err in $mnResult.Errors) { Write-Warning " $err" }
if ($mnResult.UdcUpdated) { Write-Host " UDC.exe relaunched." }
$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 ---
# Skipped when -MachineNumberOnly (called from sync_intune -AsTask after
# completion -- startup items already auto-applied by 06-OrganizeDesktop).
if (-not $MachineNumberOnly) {
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. "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"
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
$suppressed = @(Get-SuppressedStartup)
$suppressedChanged = $false
foreach ($item in $items) {
if ($selected -notcontains $item.Num) { continue }
$existingLnk = Join-Path $startupDir "$($item.Label).lnk"
if (Test-Path -LiteralPath $existingLnk) {
# Toggling OFF: remove the .lnk AND record the suppression so
# the logon sweep in 06-OrganizeDesktop doesn't re-create it.
try {
Remove-Item -LiteralPath $existingLnk -Force
Write-Host " $($item.Label): REMOVED from startup" -ForegroundColor Yellow
} catch { Write-Warning " Failed to remove $($item.Label): $_" }
if ($suppressed -notcontains $item.Label) {
$suppressed += $item.Label
$suppressedChanged = $true
}
} else {
# Toggling ON: drop any suppression first so the sweep won't
# fight us next logon, then create the .lnk.
if ($suppressed -contains $item.Label) {
$suppressed = @($suppressed | Where-Object { $_ -ne $item.Label })
$suppressedChanged = $true
}
if (-not $item.Available) {
Write-Host " $($item.Label): not installed, cannot add to startup" -ForegroundColor DarkGray
continue
}
$result = & $item.CreateLnk
if ($result) {
Write-Host " $($item.Label): ADDED to startup" -ForegroundColor Green
}
}
}
if ($suppressedChanged) {
try {
Set-SuppressedStartup -Labels $suppressed
Write-Host " (saved override state to $overridesPath)" -ForegroundColor DarkGray
} catch {
Write-Warning " Failed to write override state: $_"
}
}
# Process item 6: machine number logon task
if ($selected -contains 6) {
if ($machineNumTaskExists) {
# 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
# 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
$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 $registrar) {
try {
& $registrar
Write-Host " Machine number logon prompt: ENABLED" -ForegroundColor Green
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 " Register-CheckMachineNumberTask failed: $_"
}
} else {
Write-Warning " Register-CheckMachineNumberTask.ps1 not found at $registrar"
}
}
}
} else {
Write-Host " No changes." -ForegroundColor DarkGray
}
} # end if (-not $MachineNumberOnly)
# --- 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) }