From 8c9dac5e2eb0abe835e0550c33ba050ffbf2d2ea Mon Sep 17 00:00:00 2001 From: cproudlock Date: Tue, 3 Feb 2026 10:44:00 -0500 Subject: [PATCH] Add DeployUDCWebServerConfig task and rename UpdateEMxInfo to UpdateEMxAuthToken Add new maintenance task to deploy udc_webserver_settings.json to shopfloor PCs that have UDC installed. Checks C:\Program Files\UDC before pushing the config file, skipping PCs without UDC. Rename UpdateEMxInfo to UpdateEMxAuthToken to better describe the task's purpose. Co-Authored-By: Claude Opus 4.5 --- remote-execution/Invoke-RemoteMaintenance.ps1 | 560 ++++++++++++++++-- remote-execution/udc_webserver_settings.json | 8 + 2 files changed, 533 insertions(+), 35 deletions(-) create mode 100644 remote-execution/udc_webserver_settings.json diff --git a/remote-execution/Invoke-RemoteMaintenance.ps1 b/remote-execution/Invoke-RemoteMaintenance.ps1 index 7f29be9..d9daa95 100644 --- a/remote-execution/Invoke-RemoteMaintenance.ps1 +++ b/remote-execution/Invoke-RemoteMaintenance.ps1 @@ -4,7 +4,8 @@ .DESCRIPTION Executes maintenance tasks on remote shopfloor PCs using WinRM. - Supports system repair, disk optimization, cleanup, and database updates. + Supports system repair, disk optimization, cleanup, and reboots. + Can target PCs by name, file list, PC type, business unit, or all. .PARAMETER ComputerName Single computer name, IP address, or array of computers to target. @@ -15,6 +16,15 @@ .PARAMETER All Target all shopfloor PCs from ShopDB database. +.PARAMETER PcType + Target PCs by type (e.g., Dashboard, Lobby Display, CMM, Shopfloor). + Valid values: Standard, Engineer, Shopfloor, CMM, Wax / Trace, Keyence, + Genspect, Heat Treat, Part Marker, Dashboard, Lobby Display, Uncategorized + +.PARAMETER BusinessUnit + Target PCs by business unit (e.g., Blisk, HPT, Spools). + Valid values: TBD, Blisk, HPT, Spools, Inspection, Venture, Turn/Burn, DT + .PARAMETER Task Maintenance task to execute. Available tasks: @@ -38,7 +48,17 @@ - SyncTime : Force time sync with domain controller DNC: - - UpdateEMxInfo : Update eMxInfo.txt from network share (backs up old file first) + - UpdateEMxAuthToken : Update eMx auth token (eMxInfo.txt) from network share (backs up old file first) + - DeployUDCWebServerConfig : Deploy UDC web server settings to PCs with UDC installed + + SYSTEM: + - Reboot : Restart the computer (30 second delay) + + SOFTWARE DEPLOYMENT: + - InstallDashboard : Install GE Aerospace Dashboard kiosk app + - InstallLobbyDisplay : Install GE Aerospace Lobby Display kiosk app + - UninstallDashboard : Uninstall GE Aerospace Dashboard + - UninstallLobbyDisplay : Uninstall GE Aerospace Lobby Display .PARAMETER Credential PSCredential for remote authentication. Prompts if not provided. @@ -62,12 +82,32 @@ .\Invoke-RemoteMaintenance.ps1 -All -Task DiskCleanup .EXAMPLE - # Update database with disk health info - .\Invoke-RemoteMaintenance.ps1 -All -Task DiskHealth + # Reboot all Dashboard PCs + .\Invoke-RemoteMaintenance.ps1 -PcType Dashboard -Task Reboot .EXAMPLE - # Run all database update tasks - .\Invoke-RemoteMaintenance.ps1 -ComputerName "PC01" -Task AllDatabaseUpdates + # Reboot all Lobby Display PCs + .\Invoke-RemoteMaintenance.ps1 -PcType "Lobby Display" -Task Reboot + +.EXAMPLE + # Flush DNS on all PCs in Blisk business unit + .\Invoke-RemoteMaintenance.ps1 -BusinessUnit Blisk -Task FlushDNS + +.EXAMPLE + # Clear browser cache on all CMM PCs + .\Invoke-RemoteMaintenance.ps1 -PcType CMM -Task ClearBrowserCache + +.EXAMPLE + # Install Dashboard on specific PCs + .\Invoke-RemoteMaintenance.ps1 -ComputerName "PC001","PC002" -Task InstallDashboard + +.EXAMPLE + # Install Lobby Display from a list file + .\Invoke-RemoteMaintenance.ps1 -ComputerListFile ".\lobby-pcs.txt" -Task InstallLobbyDisplay + +.EXAMPLE + # Uninstall Dashboard from a PC + .\Invoke-RemoteMaintenance.ps1 -ComputerName "PC001" -Task UninstallDashboard .NOTES Author: Shop Floor Tools @@ -86,11 +126,21 @@ param( [Parameter(ParameterSetName='All')] [switch]$All, + [Parameter(ParameterSetName='ByPcType')] + [ValidateSet('Standard', 'Engineer', 'Shopfloor', 'CMM', 'Wax / Trace', 'Keyence', + 'Genspect', 'Heat Treat', 'Part Marker', 'Dashboard', 'Lobby Display', 'Uncategorized')] + [string]$PcType, + + [Parameter(ParameterSetName='ByBusinessUnit')] + [ValidateSet('TBD', 'Blisk', 'HPT', 'Spools', 'Inspection', 'Venture', 'Turn/Burn', 'DT')] + [string]$BusinessUnit, + [Parameter(Mandatory=$true)] [ValidateSet( 'DISM', 'SFC', 'OptimizeDisk', 'DiskCleanup', 'ClearUpdateCache', 'RestartSpooler', 'FlushDNS', 'ClearBrowserCache', 'RestartWinRM', - 'SetTimezone', 'SyncTime', 'UpdateEMxInfo' + 'SetTimezone', 'SyncTime', 'UpdateEMxAuthToken', 'DeployUDCWebServerConfig', 'Reboot', + 'InstallDashboard', 'InstallLobbyDisplay', 'UninstallDashboard', 'UninstallLobbyDisplay' )] [string]$Task, @@ -110,23 +160,11 @@ param( # ============================================================================= # SSL/TLS Configuration # ============================================================================= -try { - if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy').Type) { - Add-Type @" -using System.Net; -using System.Security.Cryptography.X509Certificates; -public class TrustAllCertsPolicy : ICertificatePolicy { - public bool CheckValidationResult( - ServicePoint srvPoint, X509Certificate certificate, - WebRequest request, int certificateProblem) { - return true; - } -} -"@ - } - [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy -} catch { } -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +# Enable all modern TLS versions +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls + +# Bypass SSL certificate validation (for self-signed certs) +[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { param($sender, $cert, $chain, $errors) return $true } # ============================================================================= # Helper Functions @@ -146,9 +184,30 @@ function Write-Log { } function Get-ShopfloorPCsFromApi { - param([string]$ApiUrl) + param( + [string]$ApiUrl, + [int]$PcTypeId = 0, + [int]$BusinessUnitId = 0 + ) try { - $response = Invoke-RestMethod -Uri "$ApiUrl`?action=getShopfloorPCs" -Method Get -ErrorAction Stop + # Force TLS 1.2 immediately before request + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + $queryParams = "action=getShopfloorPCs" + if ($PcTypeId -gt 0) { + $queryParams += "&pctypeid=$PcTypeId" + } + if ($BusinessUnitId -gt 0) { + $queryParams += "&businessunitid=$BusinessUnitId" + } + + $fullUrl = "$ApiUrl`?$queryParams" + + # Use WebClient - more reliable with TLS in script context + $webClient = New-Object System.Net.WebClient + $json = $webClient.DownloadString($fullUrl) + $response = $json | ConvertFrom-Json + if ($response.success -and $response.data) { return $response.data } @@ -159,6 +218,18 @@ function Get-ShopfloorPCsFromApi { } } +# Lookup tables for filtering +$PcTypeLookup = @{ + 'Standard' = 1; 'Engineer' = 2; 'Shopfloor' = 3; 'Uncategorized' = 4; + 'CMM' = 5; 'Wax / Trace' = 6; 'Keyence' = 7; 'Genspect' = 8; + 'Heat Treat' = 9; 'Part Marker' = 10; 'Dashboard' = 11; 'Lobby Display' = 12 +} + +$BusinessUnitLookup = @{ + 'TBD' = 1; 'Blisk' = 2; 'HPT' = 3; 'Spools' = 4; + 'Inspection' = 5; 'Venture' = 6; 'Turn/Burn' = 7; 'DT' = 8 +} + # ============================================================================= # Maintenance Task Scriptblocks @@ -685,15 +756,15 @@ $TaskScripts = @{ } # ------------------------------------------------------------------------- - # UpdateEMxInfo - Backup and prepare for file copy (runs on remote PC) + # UpdateEMxAuthToken - Backup and prepare for file copy (runs on remote PC) # The actual file is pushed via Copy-Item -ToSession from the caller # ------------------------------------------------------------------------- - 'UpdateEMxInfo' = { + 'UpdateEMxAuthToken' = { param($SourceFileContent) $result = @{ Success = $false - Task = 'UpdateEMxInfo' + Task = 'UpdateEMxAuthToken' Hostname = $env:COMPUTERNAME Output = "" Error = $null @@ -858,6 +929,233 @@ $TaskScripts = @{ return $result } + + # ------------------------------------------------------------------------- + # DeployUDCWebServerConfig - Deploy udc_webserver_settings.json to UDC PCs + # The actual file is pushed via Copy-Item -ToSession from the caller + # ------------------------------------------------------------------------- + 'DeployUDCWebServerConfig' = { + $result = @{ + Success = $false + Task = 'DeployUDCWebServerConfig' + Hostname = $env:COMPUTERNAME + Output = "" + Error = $null + FailReason = "" + UDCInstalled = $false + BackupCreated = $false + } + + $udcInstallDir = "C:\Program Files\UDC" + $destDir = "C:\ProgramData\UDC" + $destFile = "udc_webserver_settings.json" + $destPath = Join-Path $destDir $destFile + $tempPath = "C:\Windows\Temp\udc_webserver_settings.json" + + try { + # Check if UDC is installed + if (-not (Test-Path $udcInstallDir)) { + $result.FailReason = "UDC not installed ($udcInstallDir not found) - skipping" + $result.Output = $result.FailReason + Write-Output $result.FailReason + return $result + } + + $result.UDCInstalled = $true + Write-Output "UDC installation found at $udcInstallDir" + + # 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 + } + + # Create destination directory if it doesn't exist + if (-not (Test-Path $destDir)) { + New-Item -Path $destDir -ItemType Directory -Force | Out-Null + Write-Output "Created directory: $destDir" + } + + # Backup existing config if present + if (Test-Path $destPath) { + $dateStamp = Get-Date -Format "yyyyMMdd" + $backupName = "udc_webserver_settings-old-$dateStamp.json" + $backupPath = Join-Path $destDir $backupName + + Write-Output "Existing config found, backing up to $backupName..." + + try { + if (Test-Path $backupPath) { + Remove-Item $backupPath -Force -ErrorAction Stop + } + Rename-Item -Path $destPath -NewName $backupName -Force -ErrorAction Stop + $result.BackupCreated = $true + } catch { + Write-Output "Warning: Could not backup existing config - $($_.Exception.Message)" + } + } + + # Copy from temp location to destination + Copy-Item -Path $tempPath -Destination $destPath -Force -ErrorAction Stop + + # Verify the copy + if (Test-Path $destPath) { + $result.Success = $true + $result.Output = "Config deployed to $destPath" + if ($result.BackupCreated) { + $result.Output += " (backup created)" + } + Write-Output "SUCCESS: $($result.Output)" + } else { + $result.FailReason = "Copy succeeded but file not found at destination" + $result.Error = $result.FailReason + Write-Output "FAILED: $($result.FailReason)" + } + + # Clean up temp file + Remove-Item $tempPath -Force -ErrorAction SilentlyContinue + + } catch { + $result.FailReason = "Unexpected error: $($_.Exception.Message)" + $result.Error = $result.FailReason + Write-Output $result.FailReason + } + + return $result + } + + # ------------------------------------------------------------------------- + # InstallDashboard / InstallLobbyDisplay - Install kiosk app + # ------------------------------------------------------------------------- + 'InstallKioskApp' = { + param($InstallerPath, $AppName) + + $result = @{ + Success = $false + Task = 'InstallKioskApp' + Hostname = $env:COMPUTERNAME + Output = "" + Error = $null + AppName = $AppName + } + + try { + if (-not (Test-Path $InstallerPath)) { + $result.Error = "Installer not found at $InstallerPath" + Write-Output $result.Error + return $result + } + + Write-Output "Installing $AppName..." + $proc = Start-Process -FilePath $InstallerPath -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART" -Wait -PassThru -WindowStyle Hidden + + # Clean up installer + Remove-Item $InstallerPath -Force -ErrorAction SilentlyContinue + + if ($proc.ExitCode -eq 0) { + $result.Success = $true + $result.Output = "$AppName installed successfully" + } else { + $result.Error = "Installer exited with code $($proc.ExitCode)" + } + + Write-Output $result.Output + } catch { + $result.Error = $_.Exception.Message + Write-Output "Error: $($result.Error)" + } + + return $result + } + + # ------------------------------------------------------------------------- + # UninstallDashboard / UninstallLobbyDisplay - Uninstall kiosk app + # ------------------------------------------------------------------------- + 'UninstallKioskApp' = { + param($UninstallGuid, $AppName) + + $result = @{ + Success = $false + Task = 'UninstallKioskApp' + Hostname = $env:COMPUTERNAME + Output = "" + Error = $null + AppName = $AppName + } + + try { + Write-Output "Uninstalling $AppName..." + + # Find uninstaller in registry + $uninstallPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$UninstallGuid`_is1" + if (-not (Test-Path $uninstallPath)) { + $uninstallPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$UninstallGuid`_is1" + } + + if (Test-Path $uninstallPath) { + $uninstallString = (Get-ItemProperty $uninstallPath).UninstallString + if ($uninstallString) { + $uninstallExe = $uninstallString -replace '"', '' + $proc = Start-Process -FilePath $uninstallExe -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART" -Wait -PassThru -WindowStyle Hidden + + if ($proc.ExitCode -eq 0) { + $result.Success = $true + $result.Output = "$AppName uninstalled successfully" + } else { + $result.Error = "Uninstaller exited with code $($proc.ExitCode)" + } + } else { + $result.Error = "No uninstall string found in registry" + } + } else { + $result.Error = "$AppName not found in registry (may not be installed)" + } + + Write-Output $(if ($result.Success) { $result.Output } else { $result.Error }) + } catch { + $result.Error = $_.Exception.Message + Write-Output "Error: $($result.Error)" + } + + return $result + } + + # ------------------------------------------------------------------------- + # Reboot - Restart the computer + # ------------------------------------------------------------------------- + 'Reboot' = { + $result = @{ + Success = $false + Task = 'Reboot' + Hostname = $env:COMPUTERNAME + Output = "" + Error = $null + } + + try { + Write-Output "Initiating system restart..." + + # Use shutdown command with 30 second delay to allow WinRM to return + $shutdownResult = & shutdown.exe /r /t 30 /c "Remote maintenance reboot initiated" 2>&1 + $exitCode = $LASTEXITCODE + + if ($exitCode -eq 0) { + $result.Success = $true + $result.Output = "Reboot scheduled in 30 seconds" + Write-Output $result.Output + } else { + $result.Error = "Shutdown command failed with exit code $exitCode : $shutdownResult" + Write-Output $result.Error + } + } catch { + $result.Error = $_.Exception.Message + Write-Output "Error: $($result.Error)" + } + + return $result + } } # ============================================================================= @@ -888,6 +1186,18 @@ if ($All) { $shopfloorPCs = Get-ShopfloorPCsFromApi -ApiUrl $ApiUrl $computers = $shopfloorPCs | ForEach-Object { $_.hostname } | Where-Object { $_ } Write-Log "Found $($computers.Count) shopfloor PCs" -Level "INFO" +} elseif ($PcType) { + $pcTypeId = $PcTypeLookup[$PcType] + Write-Log "Querying ShopDB for PCs of type '$PcType' (ID: $pcTypeId)..." -Level "INFO" + $shopfloorPCs = Get-ShopfloorPCsFromApi -ApiUrl $ApiUrl -PcTypeId $pcTypeId + $computers = $shopfloorPCs | ForEach-Object { $_.hostname } | Where-Object { $_ } + Write-Log "Found $($computers.Count) PCs of type '$PcType'" -Level "INFO" +} elseif ($BusinessUnit) { + $businessUnitId = $BusinessUnitLookup[$BusinessUnit] + Write-Log "Querying ShopDB for PCs in business unit '$BusinessUnit' (ID: $businessUnitId)..." -Level "INFO" + $shopfloorPCs = Get-ShopfloorPCsFromApi -ApiUrl $ApiUrl -BusinessUnitId $businessUnitId + $computers = $shopfloorPCs | ForEach-Object { $_.hostname } | Where-Object { $_ } + Write-Log "Found $($computers.Count) PCs in business unit '$BusinessUnit'" -Level "INFO" } elseif ($ComputerListFile) { if (Test-Path $ComputerListFile) { $computers = Get-Content $ComputerListFile | Where-Object { $_.Trim() -and -not $_.StartsWith("#") } @@ -898,7 +1208,7 @@ if ($All) { } elseif ($ComputerName) { $computers = $ComputerName } else { - Write-Log "No computers specified. Use -ComputerName, -ComputerListFile, or -All" -Level "ERROR" + Write-Log "No computers specified. Use -ComputerName, -ComputerListFile, -All, -PcType, or -BusinessUnit" -Level "ERROR" exit 1 } @@ -922,12 +1232,12 @@ $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') { +# Special handling for UpdateEMxAuthToken - requires pushing file first +if ($Task -eq 'UpdateEMxAuthToken') { $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" + Write-Log "UpdateEMxAuthToken: Checking source file..." -Level "INFO" if (-not (Test-Path $sourcePath)) { Write-Log "Source file not found: $sourcePath" -Level "ERROR" @@ -953,7 +1263,7 @@ if ($Task -eq 'UpdateEMxInfo') { # Execute the scriptblock Write-Log " Executing update task..." -Level "INFO" - $result = Invoke-Command -Session $session -ScriptBlock $TaskScripts['UpdateEMxInfo'] -ErrorAction Stop + $result = Invoke-Command -Session $session -ScriptBlock $TaskScripts['UpdateEMxAuthToken'] -ErrorAction Stop # Close session Remove-PSSession $session -ErrorAction SilentlyContinue @@ -995,6 +1305,186 @@ if ($Task -eq 'UpdateEMxInfo') { exit 0 } +# Special handling for DeployUDCWebServerConfig - check for UDC installation, then push config file +if ($Task -eq 'DeployUDCWebServerConfig') { + $sourcePath = Join-Path $PSScriptRoot "udc_webserver_settings.json" + $remoteTempPath = "C:\Windows\Temp\udc_webserver_settings.json" + + Write-Log "DeployUDCWebServerConfig: 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: $sourcePath" -Level "INFO" + Write-Log "Will check each PC for UDC installation before deploying." -Level "INFO" + + $successCount = 0 + $failCount = 0 + $skippedCount = 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 + + # Check if UDC is installed before pushing the file + $udcInstalled = Invoke-Command -Session $session -ScriptBlock { Test-Path "C:\Program Files\UDC" } -ErrorAction Stop + + if (-not $udcInstalled) { + Write-Log "[SKIP] $fqdn - UDC not installed" -Level "INFO" + Remove-PSSession $session -ErrorAction SilentlyContinue + $skippedCount++ + continue + } + + # Push the config file to remote temp location + Write-Log " UDC installed - pushing config file..." -Level "INFO" + Copy-Item -Path $sourcePath -Destination $remoteTempPath -ToSession $session -Force -ErrorAction Stop + + # Execute the scriptblock + Write-Log " Executing deploy task..." -Level "INFO" + $result = Invoke-Command -Session $session -ScriptBlock $TaskScripts['DeployUDCWebServerConfig'] -ErrorAction Stop + + # Close session + Remove-PSSession $session -ErrorAction SilentlyContinue + + # Process result + if ($result.Success) { + Write-Log "[OK] $($result.Hostname): $($result.Output)" -Level "SUCCESS" + $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++ + 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 " Skipped: $skippedCount (UDC not installed)" -ForegroundColor Yellow + Write-Host " Failed: $failCount" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "White" }) + Write-Host ("=" * 70) -ForegroundColor Cyan + + exit 0 +} + +# Special handling for Install/Uninstall kiosk apps +$KioskAppConfig = @{ + 'InstallDashboard' = @{ + Action = 'Install' + InstallerName = 'GEAerospaceDashboardSetup.exe' + AppName = 'GE Aerospace Dashboard' + UninstallGuid = '{9D9EEE25-4D24-422D-98AF-2ADEDA4745ED}' + } + 'InstallLobbyDisplay' = @{ + Action = 'Install' + InstallerName = 'GEAerospaceLobbyDisplaySetup.exe' + AppName = 'GE Aerospace Lobby Display' + UninstallGuid = '{42FFB952-0B72-493F-8869-D957344CA305}' + } + 'UninstallDashboard' = @{ + Action = 'Uninstall' + InstallerName = 'GEAerospaceDashboardSetup.exe' + AppName = 'GE Aerospace Dashboard' + UninstallGuid = '{9D9EEE25-4D24-422D-98AF-2ADEDA4745ED}' + } + 'UninstallLobbyDisplay' = @{ + Action = 'Uninstall' + InstallerName = 'GEAerospaceLobbyDisplaySetup.exe' + AppName = 'GE Aerospace Lobby Display' + UninstallGuid = '{42FFB952-0B72-493F-8869-D957344CA305}' + } +} + +if ($KioskAppConfig.ContainsKey($Task)) { + $appConfig = $KioskAppConfig[$Task] + $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + + if ($appConfig.Action -eq 'Install') { + # Find installer + $installerPath = Join-Path $scriptDir $appConfig.InstallerName + if (-not (Test-Path $installerPath)) { + Write-Log "Installer not found: $installerPath" -Level "ERROR" + Write-Log "Place $($appConfig.InstallerName) in the script directory" -Level "ERROR" + exit 1 + } + Write-Log "$($appConfig.Action): $($appConfig.AppName)" -Level "INFO" + Write-Log "Installer: $installerPath" -Level "INFO" + } else { + Write-Log "$($appConfig.Action): $($appConfig.AppName)" -Level "INFO" + } + + $successCount = 0 + $failCount = 0 + + foreach ($fqdn in $targetFQDNs) { + Write-Host "" + Write-Log "Processing: $fqdn" -Level "TASK" + + try { + $session = New-PSSession -ComputerName $fqdn -Credential $Credential -SessionOption $sessionOption -Authentication Negotiate -ErrorAction Stop + + if ($appConfig.Action -eq 'Install') { + $remoteTempPath = "C:\Windows\Temp\$($appConfig.InstallerName)" + + Write-Log " Pushing installer to remote PC..." -Level "INFO" + Copy-Item -Path $installerPath -Destination $remoteTempPath -ToSession $session -Force -ErrorAction Stop + + Write-Log " Running installer silently..." -Level "INFO" + $result = Invoke-Command -Session $session -ScriptBlock $TaskScripts['InstallKioskApp'] -ArgumentList $remoteTempPath, $appConfig.AppName -ErrorAction Stop + } else { + Write-Log " Running uninstaller..." -Level "INFO" + $result = Invoke-Command -Session $session -ScriptBlock $TaskScripts['UninstallKioskApp'] -ArgumentList $appConfig.UninstallGuid, $appConfig.AppName -ErrorAction Stop + } + + Remove-PSSession $session -ErrorAction SilentlyContinue + + if ($result.Success) { + Write-Log "[OK] $($result.Hostname) - $($result.Output)" -Level "SUCCESS" + $successCount++ + } else { + $errorMsg = if ($result.Error) { $result.Error } else { "Unknown error" } + Write-Log "[FAIL] $($result.Hostname): $errorMsg" -Level "ERROR" + $failCount++ + } + + } catch { + Write-Log "[FAIL] ${fqdn}: $($_.Exception.Message)" -Level "ERROR" + $failCount++ + 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 " App: $($appConfig.AppName)" -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) { @@ -1053,7 +1543,7 @@ foreach ($currentTask in $tasksToRun) { 'DiskCleanup' { Write-Host " Space freed: $($result.SpaceFreed) GB" -ForegroundColor Gray } - 'UpdateEMxInfo' { + 'UpdateEMxAuthToken' { Write-Host " $($result.Output)" -ForegroundColor Gray if ($result.PathsFailed.Count -gt 0) { foreach ($fail in $result.PathsFailed) { diff --git a/remote-execution/udc_webserver_settings.json b/remote-execution/udc_webserver_settings.json new file mode 100644 index 0000000..7254a31 --- /dev/null +++ b/remote-execution/udc_webserver_settings.json @@ -0,0 +1,8 @@ +{ + "ServerAddress": "10.80.92.30", + "ServerPort": 5100, + "Enabled": true, + "ReconnectDelaySeconds": 10, + "ConnectTimeoutSeconds": 10, + "MaxQueue": 1000 +} \ No newline at end of file