Add fixnetworkshare, winrm-setup-package, udc remote-execution suites

- NetworkDriveManager.ps1: S: drive repair utility
- winrm-setup-package: Invoke-RemoteTask helper + Setup-WinRM.bat + HTML guide
- remote-execution/udc: UDC_Update.ps1 and batch wrappers for updating
  DNC controllers on shop-floor PCs
- Invoke-RemoteMaintenance.ps1: substantial rework (~1650 lines)
- Schedule-Maintenance and complete-asset minor updates
- Bump edncfix gitlink to v1.6.0 (2748bfa)
- .gitignore: block inventory.csv/xlsx (CUI) and logs_*.txt (per-host logs)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-04-17 12:04:40 -04:00
parent 847ec402bd
commit 86b32d8597
24 changed files with 6945 additions and 1352 deletions

View File

@@ -0,0 +1,29 @@
# DeployOpenTextProfiles - Example Usage
# Source: \\tsgwp00525.wjs.geaerospace.net\shared\dt\csf\
# Single PC
.\Invoke-RemoteMaintenance.ps1 -ComputerName "G1ZTNCX3ESF" -Task DeployOpenTextProfiles
# Multiple PCs
.\Invoke-RemoteMaintenance.ps1 -ComputerName "G1ZTNCX3ESF","G1ZTNCX4ESF","G1ZTNCX5ESF" -Task DeployOpenTextProfiles
# All shopfloor PCs
.\Invoke-RemoteMaintenance.ps1 -PcType Shopfloor -Task DeployOpenTextProfiles
# Wax / Trace PCs
.\Invoke-RemoteMaintenance.ps1 -PcType "Wax / Trace" -Task DeployOpenTextProfiles
# Keyence PCs
.\Invoke-RemoteMaintenance.ps1 -PcType Keyence -Task DeployOpenTextProfiles
# Genspect PCs
.\Invoke-RemoteMaintenance.ps1 -PcType Genspect -Task DeployOpenTextProfiles
# Heat Treat PCs
.\Invoke-RemoteMaintenance.ps1 -PcType "Heat Treat" -Task DeployOpenTextProfiles
# CMM PCs
.\Invoke-RemoteMaintenance.ps1 -PcType CMM -Task DeployOpenTextProfiles
# Inspection PCs
.\Invoke-RemoteMaintenance.ps1 -PcType Inspection -Task DeployOpenTextProfiles

View File

@@ -13,8 +13,11 @@ REQUIREMENTS
- PowerShell 5.1+
- Run as Administrator (required for scheduling only)
- Invoke-RemoteMaintenance.ps1 in the same folder
- A PC list text file (one hostname per line)
- ALL scripts must be in the SAME folder:
Invoke-RemoteMaintenance.ps1
Schedule-Maintenance.ps1
Export-PCList.ps1
shopfloor-pcs.txt (or your PC list file)
============================================================
@@ -34,53 +37,92 @@ PARAMETERS
============================================================
USAGE
STEP 1: Save Credentials (one time)
============================================================
1. SAVE CREDENTIALS (one time, does not require admin)
.\Schedule-Maintenance.ps1 -SaveCredential -Username "DS\570005354" -Password "MyP@ssw0rd"
.\Schedule-Maintenance.ps1 -SaveCredential -Username "DS\570005354" -Password "MyP@ssw0rd"
- Encrypted with Windows DPAPI
- Only your user account on this machine can decrypt
- Re-run if your password changes
2. RUN IMMEDIATELY (does not require admin)
.\Schedule-Maintenance.ps1 -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot
3. SCHEDULE A ONE-TIME TASK (requires admin)
# Reboot one PC today at 3:00 PM
.\Schedule-Maintenance.ps1 -CreateScheduledTask -ComputerListFile ".\test-reboot.txt" -Task Reboot -TaskFrequency Once -TaskTime "15:00" -TaskDate "2026-02-19"
# Reboot all PCs Sunday Feb 22 at 12:01 AM
.\Schedule-Maintenance.ps1 -CreateScheduledTask -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot -TaskFrequency Once -TaskTime "00:01" -TaskDate "2026-02-22"
4. SCHEDULE A RECURRING TASK (requires admin)
# Every Sunday at 12:01 AM
.\Schedule-Maintenance.ps1 -CreateScheduledTask -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot -TaskFrequency Weekly -TaskDay Sunday -TaskTime "00:01"
# Every day at 2:00 AM
.\Schedule-Maintenance.ps1 -CreateScheduledTask -ComputerListFile ".\shopfloor-pcs.txt" -Task DiskCleanup -TaskFrequency Daily -TaskTime "02:00"
5. MANAGE SCHEDULED TASKS
Get-ScheduledTask | Where-Object { $_.TaskName -like "ShopfloorMaintenance*" }
Start-ScheduledTask -TaskName "ShopfloorMaintenance-Reboot"
Unregister-ScheduledTask -TaskName "ShopfloorMaintenance-Reboot"
- Encrypted with AES-256 key
- Works from normal or admin PowerShell
- Stored in .creds\ folder (not plaintext)
- Re-run if your password changes
============================================================
LOGS
STEP 2: Generate PC List
============================================================
.\logs\maintenance-YYYY-MM-DD_HHMMSS-TaskName.log
# All shopfloor PCs from API
.\Export-PCList.ps1
# Filter by type
.\Export-PCList.ps1 -PcType Shopfloor
# Single PC for testing
"G63TVG04ESF" | Out-File -FilePath ".\test-reboot.txt" -Encoding UTF8
============================================================
STEP 3: Run or Schedule
============================================================
RUN IMMEDIATELY (no admin needed):
.\Schedule-Maintenance.ps1 -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot
SCHEDULE ONE-TIME (admin required):
.\Schedule-Maintenance.ps1 -CreateScheduledTask -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot -TaskFrequency Once -TaskTime "00:01" -TaskDate "2026-02-22"
SCHEDULE RECURRING (admin required):
.\Schedule-Maintenance.ps1 -CreateScheduledTask -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot -TaskFrequency Weekly -TaskDay Sunday -TaskTime "00:01"
============================================================
CHECKING RESULTS
============================================================
AFTER A SCHEDULED RUN:
Get-Content ".\logs\LAST-RUN-SUMMARY.txt"
FULL LOG (most recent):
Get-ChildItem ".\logs\" -Filter "maintenance-*-Reboot.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Get-Content
CHECK IF TASK RAN:
Get-ScheduledTask -TaskName "ShopfloorMaintenance-Reboot" | Get-ScheduledTaskInfo
LastTaskResult = 0 means success
Anything else means it errored before writing logs
LOGS LOCATION:
.\logs\ (inside your scripts folder)
============================================================
MANAGING SCHEDULED TASKS
============================================================
# List maintenance tasks
Get-ScheduledTask | Where-Object { $_.TaskName -like "ShopfloorMaintenance*" }
# Run now (don't wait for schedule)
Start-ScheduledTask -TaskName "ShopfloorMaintenance-Reboot"
# Delete a task
Unregister-ScheduledTask -TaskName "ShopfloorMaintenance-Reboot"
# Or use: Task Scheduler GUI (taskschd.msc)
============================================================
AVAILABLE TASKS
============================================================
Reboot, DISM, SFC, OptimizeDisk, DiskCleanup,
ClearUpdateCache, ClearBrowserCache, RestartSpooler,
FlushDNS, RestartWinRM, SetTimezone, SyncTime,
UpdateEMxAuthToken, DeployUDCWebServerConfig,
UpdateDNCMXHosts,
InstallDashboard, InstallLobbyDisplay,
UninstallDashboard, UninstallLobbyDisplay
============================================================
@@ -88,13 +130,24 @@ TROUBLESHOOTING
============================================================
"No saved credentials found"
-> Run -SaveCredential with -Username and -Password
-> .\Schedule-Maintenance.ps1 -SaveCredential -Username "DS\user" -Password "pass"
"Access is denied" when scheduling
-> Right-click PowerShell -> Run as Administrator
"No credentials provided. Exiting."
-> GUI prompt failed. Use -Username and -Password flags
-> Use -Username and -Password flags instead of GUI prompt
No logs folder / empty logs
-> Task may not have run yet. Check:
Get-ScheduledTask -TaskName "ShopfloorMaintenance-Reboot" | Get-ScheduledTaskInfo
Password changed
-> Re-run -SaveCredential with new password
-> .\Schedule-Maintenance.ps1 -SaveCredential -Username "DS\user" -Password "newpass"
NOTE ABOUT "Running as AEROAD\SSO":
This is normal. The scheduled task runs as your Windows login.
It still uses your SAVED credentials for WinRM connections
to the remote shopfloor PCs. Two separate accounts:
1. Your Windows login = runs the script
2. Saved credentials = connects to remote PCs

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
@echo off
:: Resume-Download — Resumable download for SharePoint and general URLs
:: Uses domain credentials for SharePoint auth
if "%~1"=="" (
set /p "URL=Enter download URL: "
) else (
set "URL=%~1"
)
if "%~2"=="" (
set /p "DEST=Save as (full path, e.g. C:\Temp\file.iso): "
) else (
set "DEST=%~2"
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0Resume-Download.ps1" -Url "%URL%" -Destination "%DEST%"
pause

View File

@@ -0,0 +1,117 @@
param(
[Parameter(Mandatory=$true)]
[string]$Url,
[Parameter(Mandatory=$true)]
[string]$Destination
)
$ErrorActionPreference = 'Stop'
Write-Host "============================================"
Write-Host " Resumable File Download"
Write-Host "============================================"
Write-Host ""
Write-Host "Destination: $Destination"
Write-Host ""
try {
# Check for partial file to resume
$startBytes = 0
if (Test-Path $Destination) {
$startBytes = (Get-Item $Destination).Length
if ($startBytes -gt 0) {
Write-Host "Partial file found: $([math]::Round($startBytes / 1MB, 1)) MB already downloaded"
Write-Host "Resuming..."
Write-Host ""
}
}
# Ensure destination directory exists
$destDir = Split-Path $Destination -Parent
if ($destDir -and !(Test-Path $destDir)) {
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
}
# Build headers
$headers = @{}
if ($startBytes -gt 0) {
$headers["Range"] = "bytes=$startBytes-"
}
# Use Invoke-WebRequest with domain credentials for SharePoint auth
$params = @{
Uri = $Url
OutFile = $Destination
UseDefaultCredentials = $true
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
Headers = $headers
UseBasicParsing = $true
}
# If resuming, we need to handle appending manually
if ($startBytes -gt 0) {
# Download to a temp file, then append
$tempFile = "$Destination.partial"
$params.OutFile = $tempFile
Write-Host "Downloading..."
Invoke-WebRequest @params
# Check if we got actual content
$tempSize = (Get-Item $tempFile).Length
if ($tempSize -eq 0) {
Remove-Item $tempFile -Force
Write-Host ""
Write-Host "WARNING: Server returned 0 bytes. The URL may have expired."
Write-Host "Get a fresh SharePoint link and try again."
exit 1
}
# Append to existing file
$existingBytes = [System.IO.File]::ReadAllBytes($Destination)
$newBytes = [System.IO.File]::ReadAllBytes($tempFile)
$combined = New-Object byte[] ($existingBytes.Length + $newBytes.Length)
[System.Buffer]::BlockCopy($existingBytes, 0, $combined, 0, $existingBytes.Length)
[System.Buffer]::BlockCopy($newBytes, 0, $combined, $existingBytes.Length, $newBytes.Length)
[System.IO.File]::WriteAllBytes($Destination, $combined)
Remove-Item $tempFile -Force
$totalMB = [math]::Round(($existingBytes.Length + $newBytes.Length) / 1MB, 1)
Write-Host ""
Write-Host "Download complete: $totalMB MB saved to $Destination"
}
else {
Write-Host "Downloading..."
Invoke-WebRequest @params
# Verify we got actual content
$fileSize = (Get-Item $Destination).Length
if ($fileSize -eq 0) {
Remove-Item $Destination -Force
Write-Host ""
Write-Host "WARNING: Downloaded 0 bytes. Possible causes:"
Write-Host " - SharePoint URL expired or requires browser login"
Write-Host " - URL is a redirect/login page, not the actual file"
Write-Host ""
Write-Host "Try this: In Edge, start the download, then go to"
Write-Host "edge://downloads and copy the source URL from there."
exit 1
}
$totalMB = [math]::Round($fileSize / 1MB, 1)
Write-Host ""
Write-Host "Download complete: $totalMB MB saved to $Destination"
}
} catch {
Write-Host ""
Write-Host "Error: $_"
Write-Host ""
if (Test-Path $Destination) {
$partialMB = [math]::Round((Get-Item $Destination).Length / 1MB, 1)
Write-Host "Partial file kept: $partialMB MB"
}
Write-Host "Re-run with the same arguments to resume."
exit 1
}

View File

@@ -0,0 +1,32 @@
@echo off
REM Run-UpdateDNCMXHosts.bat
REM Updates FtpHostPrimary/Secondary in DNC\MX registry on remote shopfloor PCs
REM Usage:
REM Run-UpdateDNCMXHosts.bat -> runs on all PCs in shopfloor-pcs.txt
REM Run-UpdateDNCMXHosts.bat G5N9PWM3ESF -> runs on a single PC (for testing)
echo ============================================================
echo UpdateDNCMXHosts - FTP Host Migration
echo tsgwp00525.us.ae.ge.com -^> tsgwp00525.wjs.geaerospace.net
echo ============================================================
echo.
if not "%~1"=="" (
echo Target: %~1
echo.
powershell.exe -ExecutionPolicy Bypass -File "%~dp0Invoke-RemoteMaintenance.ps1" -ComputerName "%~1" -Task UpdateDNCMXHosts -LogFile
) else (
if not exist "%~dp0shopfloor-pcs.txt" (
echo ERROR: shopfloor-pcs.txt not found in %~dp0
echo Either place shopfloor-pcs.txt in the same folder or pass a PC name:
echo Run-UpdateDNCMXHosts.bat G5N9PWM3ESF
pause
exit /b 1
)
echo Target: shopfloor-pcs.txt
echo.
powershell.exe -ExecutionPolicy Bypass -File "%~dp0Invoke-RemoteMaintenance.ps1" -ComputerListFile "%~dp0shopfloor-pcs.txt" -Task UpdateDNCMXHosts -LogFile
)
echo.
pause

View File

@@ -68,11 +68,41 @@ param(
[string]$Password
)
$credFile = Join-Path $PSScriptRoot ".maintenance-cred.xml"
$credDir = Join-Path $PSScriptRoot ".creds"
$keyFile = Join-Path $credDir "aes.key"
$userFile = Join-Path $credDir "username.txt"
$passFile = Join-Path $credDir "password.txt"
$scriptDir = $PSScriptRoot
# ---------------------------------------------------------------------------
# Save credentials (DPAPI - only decryptable by this user on this machine)
# Helper: load or create AES key (works across all user contexts on this PC)
# ---------------------------------------------------------------------------
function Get-AESKey {
if (-not (Test-Path $credDir)) {
New-Item -Path $credDir -ItemType Directory -Force | Out-Null
}
if (Test-Path $keyFile) {
return [byte[]](Get-Content $keyFile)
}
$key = New-Object byte[] 32
[System.Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)
$key | Set-Content $keyFile
return $key
}
function Load-SavedCredential {
if (-not (Test-Path $userFile) -or -not (Test-Path $passFile)) {
return $null
}
$key = Get-AESKey
$user = Get-Content $userFile
$encPass = Get-Content $passFile
$secPass = $encPass | ConvertTo-SecureString -Key $key
return New-Object System.Management.Automation.PSCredential($user, $secPass)
}
# ---------------------------------------------------------------------------
# Save credentials (AES key file - works from any user context on this PC)
# ---------------------------------------------------------------------------
if ($SaveCredential) {
if ($Username -and $Password) {
@@ -87,9 +117,11 @@ if ($SaveCredential) {
Write-Host "No credentials provided. Exiting." -ForegroundColor Red
exit 1
}
$cred | Export-Clixml -Path $credFile
Write-Host "Credentials saved to: $credFile" -ForegroundColor Green
Write-Host "Encrypted with DPAPI - only YOUR user account on THIS machine can decrypt them." -ForegroundColor Yellow
$key = Get-AESKey
$cred.UserName | Set-Content $userFile
$cred.Password | ConvertFrom-SecureString -Key $key | Set-Content $passFile
Write-Host "Credentials saved to: $credDir" -ForegroundColor Green
Write-Host "Encrypted with AES-256 key - works from any user context (normal or admin)." -ForegroundColor Yellow
Write-Host ""
Write-Host "You can now run tasks unattended:" -ForegroundColor Cyan
Write-Host " .\Schedule-Maintenance.ps1 -ComputerListFile '.\shopfloor-pcs.txt' -Task Reboot"
@@ -113,7 +145,7 @@ if ($CreateScheduledTask) {
$absListFile = (Resolve-Path $ComputerListFile -ErrorAction Stop).Path
$absScript = Join-Path $scriptDir "Schedule-Maintenance.ps1"
if (-not (Test-Path $credFile)) {
if (-not (Test-Path $passFile)) {
Write-Host "ERROR: No saved credentials found. Run with -SaveCredential first." -ForegroundColor Red
exit 1
}
@@ -177,20 +209,13 @@ if (-not $ComputerListFile) {
}
# Load saved credentials
if (-not (Test-Path $credFile)) {
Write-Host "ERROR: No saved credentials found at $credFile" -ForegroundColor Red
$cred = Load-SavedCredential
if (-not $cred) {
Write-Host "ERROR: No saved credentials found in $credDir" -ForegroundColor Red
Write-Host "Run with -SaveCredential first to store credentials." -ForegroundColor Yellow
exit 1
}
try {
$cred = Import-Clixml -Path $credFile
Write-Host "Loaded saved credentials for: $($cred.UserName)" -ForegroundColor Green
} catch {
Write-Host "ERROR: Failed to load credentials: $_" -ForegroundColor Red
Write-Host "Re-run with -SaveCredential to save new credentials." -ForegroundColor Yellow
exit 1
}
Write-Host "Loaded saved credentials for: $($cred.UserName)" -ForegroundColor Green
# Log output
$logDir = Join-Path $scriptDir "logs"
@@ -202,7 +227,41 @@ Write-Host "Log: $logFile" -ForegroundColor Gray
$mainScript = Join-Path $scriptDir "Invoke-RemoteMaintenance.ps1"
& $mainScript -ComputerListFile $ComputerListFile -Task $Task -Credential $cred 2>&1 | Tee-Object -FilePath $logFile
Start-Transcript -Path $logFile -Force | Out-Null
& $mainScript -ComputerListFile $ComputerListFile -Task $Task -Credential $cred
Stop-Transcript | Out-Null
# Parse log for results summary
$logContent = Get-Content $logFile -ErrorAction SilentlyContinue
$okPCs = @($logContent | Select-String '\[OK\]\s+(\S+)' | ForEach-Object { $_.Matches[0].Groups[1].Value })
$failPCs = @($logContent | Select-String '\[FAIL\]\s+(\S+)' | ForEach-Object { $_.Matches[0].Groups[1].Value } | Where-Object { $_ -ne ':' } | Sort-Object -Unique)
$summaryFile = Join-Path $logDir "LAST-RUN-SUMMARY.txt"
$summary = @()
$summary += "============================================"
$summary += " MAINTENANCE RESULTS - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
$summary += " Task: $Task"
$summary += "============================================"
$summary += ""
$summary += "SUCCEEDED: $($okPCs.Count)"
foreach ($pc in $okPCs) { $summary += " [OK] $pc" }
$summary += ""
$summary += "FAILED: $($failPCs.Count)"
foreach ($pc in $failPCs) { $summary += " [FAIL] $pc" }
$summary += ""
$summary += "Full log: $logFile"
$summary | Out-File -FilePath $summaryFile -Encoding UTF8
Write-Host ""
Write-Host "Complete. Log saved to: $logFile" -ForegroundColor Green
Write-Host ""
Write-Host "=== RESULTS ===" -ForegroundColor White
Write-Host " Succeeded: $($okPCs.Count)" -ForegroundColor Green
foreach ($pc in $okPCs) { Write-Host " $pc" -ForegroundColor Green }
if ($failPCs.Count -gt 0) {
Write-Host " Failed: $($failPCs.Count)" -ForegroundColor Red
foreach ($pc in $failPCs) { Write-Host " $pc" -ForegroundColor Red }
}
Write-Host ""
Write-Host "Summary also saved to: $summaryFile" -ForegroundColor Yellow

View File

@@ -0,0 +1,279 @@
# ============================================
# UDC Application Update Script (PowerShell)
# ============================================
param(
[string]$Version
)
# Set error action preference
$ErrorActionPreference = "Stop"
# Set variables
$UDC_PATH = "C:\Program Files\UDC"
$VERSION_FILE = "S:\SPC\UDC\UDC_Update.txt"
$LOG_DIR = "S:\DT\Cameron\UDC\logs"
# Get hostname and timestamp
$HOSTNAME = $env:COMPUTERNAME
$TIMESTAMP = Get-Date -Format "yyyyMMdd_HHmmss"
$LOG_FILE = "$LOG_DIR\logs_$($HOSTNAME)_$TIMESTAMP.txt"
# Flag to track if we should skip the update
$SKIP_UPDATE = $false
# Function to write logs
function Write-Log {
param([string]$Message)
$LogMessage = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Message"
Write-Host $Message
Add-Content -Path $LOG_FILE -Value $LogMessage -ErrorAction SilentlyContinue
}
# Create log directory if needed
try {
if (-not (Test-Path $LOG_DIR)) {
New-Item -Path $LOG_DIR -ItemType Directory -Force | Out-Null
}
} catch {
Write-Host "WARNING: Cannot access log directory $LOG_DIR"
Write-Host "Update will continue but logging is disabled."
}
Write-Log "============================================"
Write-Log "UDC Update Script Started on $HOSTNAME"
Write-Log "============================================"
# ============================================
# Step 1: Check if PPMon.exe or ppdcs.exe are running
# ============================================
Write-Host "Checking for conflicting processes..."
$conflictingProcesses = Get-Process -Name "PPMon", "ppdcs" -ErrorAction SilentlyContinue
if ($conflictingProcesses) {
foreach ($proc in $conflictingProcesses) {
Write-Log "$($proc.Name).exe is running. Skipping update process."
Write-Host "$($proc.Name).exe is running. Update will be skipped."
}
$SKIP_UPDATE = $true
}
# ============================================
# Step 2: Check if UDC directory exists
# ============================================
Write-Host "Checking if UDC is installed..."
if (-not (Test-Path $UDC_PATH)) {
Write-Log "UDC directory not found. Exiting."
Write-Host "UDC is not installed on this machine."
exit 0
}
# ============================================
# Step 3: Check if UDC.exe exists
# ============================================
$UDC_EXE = "$UDC_PATH\UDC.exe"
if (-not (Test-Path $UDC_EXE)) {
Write-Log "UDC.exe not found. Exiting."
Write-Host "UDC.exe not found in installation directory."
exit 0
}
# ============================================
# ONLY PROCEED WITH UPDATE IF NOT SKIPPED
# ============================================
if (-not $SKIP_UPDATE) {
# ============================================
# Step 4: Determine target version
# ============================================
if ($Version) {
$NETWORK_VERSION = $Version
Write-Host "Using override version: $NETWORK_VERSION"
Write-Log "Override version specified: $NETWORK_VERSION"
} else {
if (-not (Test-Path $VERSION_FILE)) {
Write-Log "Version file not found: $VERSION_FILE"
Write-Host "ERROR: Version file not found."
exit 1
}
Write-Host "Reading version information..."
try {
$versionContent = Get-Content $VERSION_FILE | Where-Object { $_ -match "Version:" }
$NETWORK_VERSION = ($versionContent -split ":")[1].Trim()
if ([string]::IsNullOrEmpty($NETWORK_VERSION)) {
throw "Could not parse network version"
}
Write-Host "Network version: $NETWORK_VERSION"
Write-Log "Network version: $NETWORK_VERSION"
} catch {
Write-Log "Could not read network version. Error: $_"
Write-Host "ERROR: Could not determine network version."
exit 1
}
}
# Build source path from version
$SOURCE_PATH = "S:\SPC\UDC\UDC_$NETWORK_VERSION"
# ============================================
# Step 5: Check if source files exist
# ============================================
if (-not (Test-Path $SOURCE_PATH)) {
Write-Log "Source path not found: $SOURCE_PATH"
Write-Host "ERROR: Update source files not found at $SOURCE_PATH"
exit 1
}
# ============================================
# Step 6: Get local UDC.exe version
# ============================================
try {
$fileVersion = (Get-Item $UDC_EXE).VersionInfo.FileVersion
# Trim to 3 parts if it has 4 (e.g., 1.0.30.0 -> 1.0.30)
$versionParts = $fileVersion.Split('.')
if ($versionParts.Count -eq 4) {
$LOCAL_VERSION = "$($versionParts[0]).$($versionParts[1]).$($versionParts[2])"
} else {
$LOCAL_VERSION = $fileVersion
}
Write-Host "Local version: $LOCAL_VERSION"
Write-Log "Local version: $LOCAL_VERSION"
} catch {
Write-Log "Could not read local version. Error: $_"
Write-Host "ERROR: Could not determine local version."
exit 1
}
# ============================================
# Step 7: Compare versions
# ============================================
try {
$netVer = [version]$NETWORK_VERSION
$localVer = [version]$LOCAL_VERSION
if ($netVer -eq $localVer) {
Write-Log "Versions match ($LOCAL_VERSION). No update needed."
Write-Host "Version is current. No update required."
$SKIP_UPDATE = $true
}
elseif ($netVer -le $localVer) {
Write-Log "Network version ($NETWORK_VERSION) not newer than local ($LOCAL_VERSION)."
Write-Host "Local version is current or newer. No update required."
$SKIP_UPDATE = $true
}
else {
Write-Host "Update required: $LOCAL_VERSION -> $NETWORK_VERSION"
Write-Log "Update required: $LOCAL_VERSION -> $NETWORK_VERSION"
}
} catch {
Write-Log "Error comparing versions. Error: $_"
Write-Host "ERROR: Could not compare versions."
exit 1
}
# ============================================
# Step 8: Perform update if needed
# ============================================
if (-not $SKIP_UPDATE) {
# Kill UDC.exe if running
Write-Host "Checking if UDC.exe is running..."
$udcProcess = Get-Process -Name "UDC" -ErrorAction SilentlyContinue
if ($udcProcess) {
Write-Host "UDC.exe is running. Stopping process..."
Write-Log "Killing UDC.exe"
try {
Stop-Process -Name "UDC" -Force
Start-Sleep -Seconds 2
Write-Log "UDC.exe stopped successfully"
} catch {
Write-Log "Warning: Could not stop UDC.exe. Error: $_"
Write-Host "WARNING: Could not stop UDC.exe"
}
}
# Copy update files
Write-Host "Copying update files..."
Write-Log "Copying files from $SOURCE_PATH to $UDC_PATH"
try {
# Use robocopy for better performance and logging
$robocopyArgs = @(
$SOURCE_PATH,
$UDC_PATH,
"/E", # Copy subdirectories including empty ones
"/NFL", # No file list
"/NDL", # No directory list
"/NJH", # No job header
"/NJS", # No job summary
"/NC", # No class
"/NS", # No size
"/NP" # No progress
)
$result = robocopy @robocopyArgs
# Robocopy exit codes: 0-7 are success, 8+ are errors
if ($LASTEXITCODE -ge 8) {
throw "Robocopy failed with exit code $LASTEXITCODE"
}
Write-Host "Files copied successfully."
Write-Log "Files copied successfully."
} catch {
Write-Log "ERROR: File copy failed. Error: $_"
Write-Host "ERROR: Failed to copy update files."
exit 1
}
}
}
# ============================================
# Step 9: Ensure UDC.exe is running (ONLY if no conflicting processes)
# ============================================
# Re-check for conflicting processes before starting UDC
$conflictingProcesses = Get-Process -Name "PPMon", "ppdcs" -ErrorAction SilentlyContinue
if ($conflictingProcesses) {
Write-Log "Conflicting processes still running. Will not start UDC.exe"
Write-Host "Conflicting processes detected. UDC.exe will not be started."
} else {
Write-Host "Verifying UDC.exe is running..."
$udcProcess = Get-Process -Name "UDC" -ErrorAction SilentlyContinue
if (-not $udcProcess) {
Write-Host "UDC.exe is not running. Starting it now..."
Write-Log "UDC.exe not running. Starting UDC.exe"
try {
Start-Process -FilePath $UDC_EXE
Start-Sleep -Seconds 2
# Verify it started
$udcProcess = Get-Process -Name "UDC" -ErrorAction SilentlyContinue
if ($udcProcess) {
Write-Log "UDC.exe started successfully."
Write-Host "UDC.exe started successfully!"
} else {
Write-Log "WARNING: UDC.exe may not have started."
Write-Host "WARNING: UDC.exe may not have started properly."
}
} catch {
Write-Log "WARNING: Could not start UDC.exe. Error: $_"
Write-Host "WARNING: Could not start UDC.exe"
}
} else {
Write-Log "UDC.exe is already running."
Write-Host "UDC.exe is already running."
}
}
Write-Log "UDC Update Script Completed"
Write-Log "============================================"
exit 0

View File

@@ -0,0 +1,8 @@
@echo off
:: ============================================
:: UDC Update PowerShell Launcher
:: ============================================
powershell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""U:/Scripts/UDC/UDC_Update.ps1""' -Verb RunAs}"
exit /b %ERRORLEVEL%

View File

@@ -0,0 +1,11 @@
@echo off
:: ============================================
:: UDC Update Override Launcher
:: Edit the version below to force a specific update
:: ============================================
set "UDC_VERSION=1.0.32"
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& 'U:/Scripts/UDC/UDC_Update.ps1' -Version '%UDC_VERSION%'"
exit /b %ERRORLEVEL%