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 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-02-03 10:44:00 -05:00
parent 7ed9f42f44
commit 8c9dac5e2e
2 changed files with 533 additions and 35 deletions

View File

@@ -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) {

View File

@@ -0,0 +1,8 @@
{
"ServerAddress": "10.80.92.30",
"ServerPort": 5100,
"Enabled": true,
"ReconnectDelaySeconds": 10,
"ConnectTimeoutSeconds": 10,
"MaxQueue": 1000
}