From 7ed9f42f44ba6ad86947bee118b24b613e55056b Mon Sep 17 00:00:00 2001 From: cproudlock Date: Wed, 7 Jan 2026 16:59:01 -0500 Subject: [PATCH] Add UpdateEMxInfo task for remote DNC eMxInfo.txt updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New task to update eMxInfo.txt on shopfloor PCs via WinRM - Pushes file from local workstation to avoid double-hop auth issues - Checks both Program Files (x86) and Program Files paths - Backs up existing file with date stamp before replacing - Kills DNCMain.exe before copy, restarts LDnc.exe after success - Reports per-path success/failure with detailed error messages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- remote-execution/Invoke-RemoteMaintenance.ps1 | 264 +++++++++++++++++- 1 file changed, 262 insertions(+), 2 deletions(-) diff --git a/remote-execution/Invoke-RemoteMaintenance.ps1 b/remote-execution/Invoke-RemoteMaintenance.ps1 index e36f867..7f29be9 100644 --- a/remote-execution/Invoke-RemoteMaintenance.ps1 +++ b/remote-execution/Invoke-RemoteMaintenance.ps1 @@ -37,6 +37,9 @@ - SetTimezone : Set timezone to Eastern Standard Time - SyncTime : Force time sync with domain controller + DNC: + - UpdateEMxInfo : Update eMxInfo.txt from network share (backs up old file first) + .PARAMETER Credential PSCredential for remote authentication. Prompts if not provided. @@ -87,7 +90,7 @@ param( [ValidateSet( 'DISM', 'SFC', 'OptimizeDisk', 'DiskCleanup', 'ClearUpdateCache', 'RestartSpooler', 'FlushDNS', 'ClearBrowserCache', 'RestartWinRM', - 'SetTimezone', 'SyncTime' + 'SetTimezone', 'SyncTime', 'UpdateEMxInfo' )] [string]$Task, @@ -680,6 +683,181 @@ $TaskScripts = @{ return $result } + + # ------------------------------------------------------------------------- + # UpdateEMxInfo - Backup and prepare for file copy (runs on remote PC) + # The actual file is pushed via Copy-Item -ToSession from the caller + # ------------------------------------------------------------------------- + 'UpdateEMxInfo' = { + param($SourceFileContent) + + $result = @{ + Success = $false + Task = 'UpdateEMxInfo' + Hostname = $env:COMPUTERNAME + Output = "" + Error = $null + FailReason = "" + PathsUpdated = @() + PathsFailed = @() + BackupsCreated = @() + TempFile = "" + DNCKilled = $false + DNCRestarted = $false + } + + $destFile = "eMxInfo.txt" + $tempPath = "C:\Windows\Temp\eMxInfo-2026.txt" + + # Check both possible DNC installation paths + $destDirs = @( + "C:\Program Files (x86)\DNC\Server Files", + "C:\Program Files\DNC\Server Files" + ) + + try { + # Check if temp file was pushed + if (-not (Test-Path $tempPath)) { + $result.FailReason = "Source file not found at $tempPath - file push may have failed" + $result.Error = $result.FailReason + Write-Output $result.FailReason + return $result + } + + # Track which paths exist + $validPaths = @() + foreach ($destDir in $destDirs) { + if (Test-Path $destDir) { + $validPaths += $destDir + } + } + + if ($validPaths.Count -eq 0) { + $result.FailReason = "No DNC Server Files directory found in Program Files or Program Files (x86)" + $result.Error = $result.FailReason + Write-Output $result.FailReason + # Clean up temp file + Remove-Item $tempPath -Force -ErrorAction SilentlyContinue + return $result + } + + Write-Output "Found $($validPaths.Count) DNC installation(s)" + + # Kill DNCMain.exe before copying + Write-Output "Stopping DNCMain.exe..." + $dncProcess = Get-Process -Name "DNCMain" -ErrorAction SilentlyContinue + if ($dncProcess) { + try { + taskkill /IM DNCMain.exe /F 2>&1 | Out-Null + Start-Sleep -Seconds 2 + $result.DNCKilled = $true + Write-Output " DNCMain.exe stopped" + } catch { + Write-Output " Warning: Could not stop DNCMain.exe - $($_.Exception.Message)" + } + } else { + Write-Output " DNCMain.exe not running" + } + + # Process each valid path + foreach ($destDir in $validPaths) { + $destPath = Join-Path $destDir $destFile + $pathLabel = if ($destDir -like "*x86*") { "x86" } else { "x64" } + + Write-Output "Processing $pathLabel path: $destDir" + + # Check if destination file exists and back it up + if (Test-Path $destPath) { + $dateStamp = Get-Date -Format "yyyyMMdd" + $backupName = "eMxInfo-old-$dateStamp.txt" + $backupPath = Join-Path $destDir $backupName + + Write-Output " File exists, renaming to $backupName..." + + try { + # Remove existing backup if same date + if (Test-Path $backupPath) { + Remove-Item $backupPath -Force -ErrorAction Stop + } + + Rename-Item -Path $destPath -NewName $backupName -Force -ErrorAction Stop + $result.BackupsCreated += "$pathLabel`:$backupName" + } catch { + $result.PathsFailed += "$pathLabel`: Failed to rename - $($_.Exception.Message)" + Write-Output " FAILED to rename: $($_.Exception.Message)" + continue + } + } else { + Write-Output " File does not exist, creating new..." + } + + # Copy from temp location to destination + try { + Copy-Item -Path $tempPath -Destination $destPath -Force -ErrorAction Stop + + # Verify the copy + if (Test-Path $destPath) { + $result.PathsUpdated += $pathLabel + Write-Output " SUCCESS" + } else { + $result.PathsFailed += "$pathLabel`: Copy succeeded but file not found" + Write-Output " FAILED: File not found after copy" + } + } catch { + $result.PathsFailed += "$pathLabel`: Failed to copy - $($_.Exception.Message)" + Write-Output " FAILED to copy: $($_.Exception.Message)" + } + } + + # Clean up temp file + Remove-Item $tempPath -Force -ErrorAction SilentlyContinue + + # Determine overall success + if ($result.PathsUpdated.Count -gt 0) { + $result.Success = $true + $result.Output = "Updated: $($result.PathsUpdated -join ', ')" + if ($result.BackupsCreated.Count -gt 0) { + $result.Output += " | Backups: $($result.BackupsCreated -join ', ')" + } + if ($result.PathsFailed.Count -gt 0) { + $result.Output += " | Failed: $($result.PathsFailed.Count)" + } + + # Restart DNC - find LDnc.exe in one of the valid paths + Write-Output "Starting LDnc.exe..." + $ldncStarted = $false + foreach ($destDir in $validPaths) { + $ldncPath = Join-Path $destDir "LDnc.exe" + if (Test-Path $ldncPath) { + try { + Start-Process -FilePath $ldncPath -ErrorAction Stop + $result.DNCRestarted = $true + $ldncStarted = $true + Write-Output " LDnc.exe started from $destDir" + break + } catch { + Write-Output " Warning: Could not start LDnc.exe from $destDir - $($_.Exception.Message)" + } + } + } + if (-not $ldncStarted) { + Write-Output " Warning: LDnc.exe not found or could not be started" + } + + $result.Output += " | DNC restarted: $($result.DNCRestarted)" + } else { + $result.FailReason = "All paths failed: $($result.PathsFailed -join '; ')" + $result.Error = $result.FailReason + } + + } catch { + $result.FailReason = "Unexpected error: $($_.Exception.Message)" + $result.Error = $result.FailReason + Write-Output $result.FailReason + } + + return $result + } } # ============================================================================= @@ -744,6 +922,79 @@ $tasksToRun = @($Task) # Create session options $sessionOption = New-PSSessionOption -OpenTimeout 30000 -OperationTimeout 600000 -NoMachineProfile +# Special handling for UpdateEMxInfo - requires pushing file first +if ($Task -eq 'UpdateEMxInfo') { + $sourcePath = "\\tsgwp00525.rd.ds.ge.com\shared\cameron\eMxInfo-2026.txt" + $remoteTempPath = "C:\Windows\Temp\eMxInfo-2026.txt" + + Write-Log "UpdateEMxInfo: Checking source file..." -Level "INFO" + + if (-not (Test-Path $sourcePath)) { + Write-Log "Source file not found: $sourcePath" -Level "ERROR" + exit 1 + } + + Write-Log "Source file found. Will push to each PC before executing." -Level "INFO" + + $successCount = 0 + $failCount = 0 + + foreach ($fqdn in $targetFQDNs) { + Write-Host "" + Write-Log "Processing: $fqdn" -Level "TASK" + + try { + # Create session + $session = New-PSSession -ComputerName $fqdn -Credential $Credential -SessionOption $sessionOption -Authentication Negotiate -ErrorAction Stop + + # Push the file to remote temp location + Write-Log " Pushing file to remote PC..." -Level "INFO" + Copy-Item -Path $sourcePath -Destination $remoteTempPath -ToSession $session -Force -ErrorAction Stop + + # Execute the scriptblock + Write-Log " Executing update task..." -Level "INFO" + $result = Invoke-Command -Session $session -ScriptBlock $TaskScripts['UpdateEMxInfo'] -ErrorAction Stop + + # Close session + Remove-PSSession $session -ErrorAction SilentlyContinue + + # Process result + if ($result.Success) { + Write-Log "[OK] $($result.Hostname)" -Level "SUCCESS" + Write-Host " $($result.Output)" -ForegroundColor Gray + if ($result.PathsFailed.Count -gt 0) { + foreach ($fail in $result.PathsFailed) { + Write-Host " [!] $fail" -ForegroundColor Yellow + } + } + $successCount++ + } else { + $errorMsg = if ($result.FailReason) { $result.FailReason } else { $result.Error } + Write-Log "[FAIL] $($result.Hostname): $errorMsg" -Level "ERROR" + $failCount++ + } + + } catch { + Write-Log "[FAIL] ${fqdn}: $($_.Exception.Message)" -Level "ERROR" + $failCount++ + # Clean up session if it exists + if ($session) { Remove-PSSession $session -ErrorAction SilentlyContinue } + } + } + + # Summary + Write-Host "" + Write-Host ("=" * 70) -ForegroundColor Cyan + Write-Host " SUMMARY" -ForegroundColor Cyan + Write-Host ("=" * 70) -ForegroundColor Cyan + Write-Host " Task: $Task" -ForegroundColor White + Write-Host " Successful: $successCount" -ForegroundColor Green + Write-Host " Failed: $failCount" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "White" }) + Write-Host ("=" * 70) -ForegroundColor Cyan + + exit 0 +} + # Process each task foreach ($currentTask in $tasksToRun) { @@ -802,6 +1053,14 @@ foreach ($currentTask in $tasksToRun) { 'DiskCleanup' { Write-Host " Space freed: $($result.SpaceFreed) GB" -ForegroundColor Gray } + 'UpdateEMxInfo' { + Write-Host " $($result.Output)" -ForegroundColor Gray + if ($result.PathsFailed.Count -gt 0) { + foreach ($fail in $result.PathsFailed) { + Write-Host " [!] $fail" -ForegroundColor Yellow + } + } + } default { if ($result.Output) { Write-Host " $($result.Output)" -ForegroundColor Gray @@ -812,7 +1071,8 @@ foreach ($currentTask in $tasksToRun) { $successCount++ } else { - Write-Log "[FAIL] $($result.Hostname): $($result.Error)" -Level "ERROR" + $errorMsg = if ($result.FailReason) { $result.FailReason } else { $result.Error } + Write-Log "[FAIL] $($result.Hostname): $errorMsg" -Level "ERROR" $failCount++ } }