# 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)" # 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' # ============================================================================ # 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 = if ($pcProfile -and $pcProfile.startupItems) { $pcProfile.startupItems } elseif ($siteConfig -and $siteConfig.startupItems) { $siteConfig.startupItems } else { $null } if ($cfgItems) { $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 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+$') { $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 --- 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) }