# Prompt-MachineNumber.ps1 - User-context GUI script for the two-task # machine number flow. Triggered AtLogOn for any BUILTIN\Users member. # # Flow: # 1. Read current UDC + eDNC values (read-only - no privileges needed). # 2. If neither is 9999, unregister self and exit (this PC is set up). # 3. Show InputBox for new machine number. # 4. Write number to C:\Logs\SFLD\machine-number-request.txt. # 5. Trigger the SYSTEM-context Apply-MachineNumber task via # schtasks /run. SYSTEM has full HKLM + ProgramData access so the # actual write happens with proper privileges - the prompted user # never needs HKLM write rights (security improvement over the old # 02-MachineNumberACLs.ps1 ACL-grant hack). # 6. Poll for C:\Logs\SFLD\machine-number-result.json (30s timeout). # 7. Show result MessageBox. Unregister self on success. # # Why this script doesn't do the writes itself: GUI is required (InputBox), # but GUI requires user-context (SYSTEM can't render to user desktop on # modern Windows). The user-context dialog gathers input; the SYSTEM task # does privileged writes. # --- Transcript --- $logDir = 'C:\Logs\SFLD' if (-not (Test-Path -LiteralPath $logDir)) { try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch { $logDir = $env:TEMP } } $transcript = Join-Path $logDir 'Prompt-MachineNumber.log' try { Start-Transcript -Path $transcript -Append -Force | Out-Null } catch {} Write-Host "Prompt-MachineNumber.ps1 starting $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Write-Host "Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" . "$PSScriptRoot\lib\Get-PCProfile.ps1" . "$PSScriptRoot\lib\Update-MachineNumber.ps1" Add-Type -AssemblyName Microsoft.VisualBasic Add-Type -AssemblyName System.Windows.Forms $taskName = 'Prompt Machine Number' $applyTaskName = 'Apply Machine Number' $requestFile = Join-Path $logDir 'machine-number-request.txt' $resultFile = Join-Path $logDir 'machine-number-result.json' $site = if ($siteConfig) { $siteConfig.siteName } else { 'West Jefferson' } # --- Read current values (read-only, no perms needed) --- $currentMN = Get-CurrentMachineNumber $currentUdc = $currentMN.Udc $currentEdnc = $currentMN.Ednc 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 Prompt 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() if ($new -notmatch '^\d+$') { Write-Host "Invalid input: '$new' (not digits only). Showing error and re-prompting 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 } # --- Hand off to SYSTEM task --- # Clean any stale request / result files first so we read fresh ones. Remove-Item -LiteralPath $requestFile, $resultFile -Force -ErrorAction SilentlyContinue try { Set-Content -LiteralPath $requestFile -Value $new -Encoding ascii -Force -ErrorAction Stop } catch { [System.Windows.Forms.MessageBox]::Show( "Could not write request file at $requestFile`n`n$_`n`nThe prompt will appear again at next logon.", "Machine Number Request Failed", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error ) | Out-Null try { Stop-Transcript | Out-Null } catch {} exit 1 } Write-Host "Wrote $requestFile with '$new'. Triggering SYSTEM apply task..." & schtasks.exe /run /tn $applyTaskName 2>&1 | ForEach-Object { Write-Host " schtasks: $_" } # --- Wait for result --- $deadline = (Get-Date).AddSeconds(60) $result = $null while ((Get-Date) -lt $deadline) { if (Test-Path -LiteralPath $resultFile) { try { $result = Get-Content -LiteralPath $resultFile -Raw -ErrorAction Stop | ConvertFrom-Json break } catch { Start-Sleep -Milliseconds 200 } } Start-Sleep -Milliseconds 500 } # Make the result MessageBox topmost so it shows above the FormTracePak / # DNC windows and isn't missed. $tmpForm = New-Object System.Windows.Forms.Form $tmpForm.TopMost = $true $tmpForm.WindowState = 'Minimized' $tmpForm.ShowInTaskbar = $false $tmpForm.Opacity = 0 $tmpForm.Show() if (-not $result) { Write-Host "Timed out waiting for SYSTEM apply task to produce result file ($resultFile)." [System.Windows.Forms.MessageBox]::Show( $tmpForm, "Timed out waiting for the SYSTEM update task to complete.`n`nCheck:`n C:\Logs\SFLD\Apply-MachineNumber.log`n C:\Logs\SFLD\Prompt-MachineNumber.log`n`nThe prompt will appear again at next logon.", "Machine Number Update Timed Out", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning ) | Out-Null $tmpForm.Close() try { Stop-Transcript | Out-Null } catch {} exit 1 } # Build summary from result JSON $lines = @() $lines += "Requested: $($result.Requested)" $lines += "" if ($result.UdcUpdated) { $lines += "UDC updated to $($result.Requested)" } else { $lines += "UDC: not updated (UDC may not be installed)" } if ($result.EdncUpdated) { $lines += "eDNC updated to $($result.Requested)" } else { $lines += "eDNC: not updated" } if ($result.UdcSettingsRestored) { $lines += "UDC settings restored from SFLD" } if ($result.UdcRestored) { $lines += "UDC live data restored from SFLD" } if ($result.MachineNumberTxtUpdated) { $lines += "machine-number.txt updated" } if ($result.MTConnectUpdated -and $result.MTConnectUpdated.Count -gt 0) { $lines += "" $lines += "MTConnect Devices.xml updates:" $result.MTConnectUpdated | ForEach-Object { $lines += " - $_" } } if ($result.Errors -and $result.Errors.Count -gt 0) { $lines += "" $lines += "FAILURES:" $result.Errors | ForEach-Object { $lines += " - $_" } } $lines += "" $lines += "Status: $($result.Status)" $lines += "Logs: C:\Logs\SFLD\Apply-MachineNumber.log" $lines += " C:\Logs\SFLD\Prompt-MachineNumber.log" $lines += "" $lines += "To apply eDNC changes, restart any running DncMain.exe." $summary = $lines -join "`n" $icon = if ($result.Status -eq 'OK') { [System.Windows.Forms.MessageBoxIcon]::Information } else { [System.Windows.Forms.MessageBoxIcon]::Warning } [System.Windows.Forms.MessageBox]::Show( $tmpForm, $summary, "Machine Number Update Result", [System.Windows.Forms.MessageBoxButtons]::OK, $icon ) | Out-Null $tmpForm.Close() # Clean up result file for the next round. Remove-Item -LiteralPath $resultFile -Force -ErrorAction SilentlyContinue # Only unregister the Prompt task on full success (no errors AND eDNC # updated to the requested value). If anything failed, leave it registered # for next logon retry. if ($result.Status -eq 'OK' -and $result.EdncUpdated) { Write-Host "All updates succeeded. Unregistering Prompt task." try { Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue } catch {} } else { Write-Host "Some updates failed or skipped. Prompt task stays registered for next logon retry." } Write-Host "Prompt-MachineNumber.ps1 finished $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" try { Stop-Transcript | Out-Null } catch {} exit 0