Add maintenance toolkit, DNC/OnGuard utilities

- Add Invoke-RemoteMaintenance.ps1: Remote maintenance tasks (DISM, SFC, disk cleanup, etc.)
- Add DNC/, dncfix/, edncfix/: DNC configuration utilities
- Add onguard/: OnGuard integration scripts
- Add tools/: Additional utility scripts
- Update remote-execution/README.md with maintenance toolkit docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-12-30 13:08:08 -05:00
parent 0b470ef177
commit 7c30939234
8 changed files with 1127 additions and 22 deletions

12
DNC/update_dnc.bat Normal file
View File

@@ -0,0 +1,12 @@
@echo off
echo Updating DNC files...
echo Stopping DncMain.exe if running...
taskkill /F /IM DncMain.exe 2>nul
copy /Y "S:\DT\DNC\DNCdll.dll" "C:\Program Files (x86)\dnc\bin\"
copy /Y "S:\DT\DNC\DncMain.exe" "C:\Program Files (x86)\dnc\bin\"
copy /Y "S:\DT\DNC\mxTransactionDll.dll" "C:\Program Files (x86)\dnc\bin\"
echo Done.
pause

3
dncfix/fixvtm.bat Normal file
View File

@@ -0,0 +1,3 @@
@echo off
powershell.exe -ExecutionPolicy Bypass -File ".\vtmfix.ps1"
pause

64
dncfix/vtmfix.ps1 Normal file
View File

@@ -0,0 +1,64 @@
# Real-time file watcher to strip 0xFF from .pun files
# Watches folder and cleans files as soon as they're created/modified
$watchFolder = "C:\Dnc_Files\Q" # Change to your DNC program folder
$fileFilter = "*.pun" # Watch .pun files
Write-Host "Watching $watchFolder for new/modified $fileFilter files..."
Write-Host "Press Ctrl+C to stop"
# Create file system watcher
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $watchFolder
$watcher.Filter = $fileFilter
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
# Define what to do when file is created or changed
$action = {
$path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
Write-Host "$(Get-Date -Format 'HH:mm:ss') - $changeType detected: $path"
# Wait a moment for file to finish writing
Start-Sleep -Milliseconds 500
try {
# Read file as bytes
$bytes = [System.IO.File]::ReadAllBytes($path)
$originalCount = $bytes.Count
# Remove all 0xFF bytes
$cleaned = $bytes | Where-Object { $_ -ne 255 }
$newCount = $cleaned.Count
# Only rewrite if we found 0xFF
if ($originalCount -ne $newCount) {
[System.IO.File]::WriteAllBytes($path, $cleaned)
$removed = $originalCount - $newCount
Write-Host " [OK] Cleaned! Removed $removed byte(s) [0xFF]" -ForegroundColor Green
} else {
Write-Host " [SKIP] No 0xFF found, file OK" -ForegroundColor Gray
}
}
catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
}
}
# Register event handlers
Register-ObjectEvent -InputObject $watcher -EventName Created -Action $action
Register-ObjectEvent -InputObject $watcher -EventName Changed -Action $action
# Keep script running
try {
while ($true) {
Start-Sleep -Seconds 1
}
}
finally {
# Cleanup on exit
$watcher.Dispose()
Write-Host "`nStopped watching folder"
}

1
edncfix Submodule

Submodule edncfix added at 28641c47c5

93
onguard/silentInstall.ps1 Normal file
View File

@@ -0,0 +1,93 @@
#Requires -RunAsAdministrator
param(
[Parameter(Mandatory=$True)]
[string]
$LicenseServer,
[Parameter(Mandatory=$True)]
[string]
$DatabaseServer
)
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
# Only Client installations are supported at this time
# $Features can optionally be assigned to an array of specific Client Features from within available features
# The available client features are: AlarmMonitoring AreaAccessManager BadgeDesigner DeviceDicoveryService DeviceDiscovery FormsDesigner IDCredentialCenter MapDesigner SystemAdministration VideoViewer VisitorManagement
# example: $Features = @("AlarmMonitoring","IDCredentialCenter","SystemAdministration")
$Features = "Client"
$availableFeatures = @("AlarmMonitoring","AreaAccessManager","BadgeDesigner","DeviceDicoveryService","DeviceDiscovery","FormsDesigner","IDCredentialCenter","MapDesigner","SystemAdministration","VideoViewer","VisitorManagement","ApplicationServer","ClientUpdateServer","CommunicationServer","DataConduITService","DataExchangeServer","EnterpriseAdministration","GlobalOutputServer","IDAllocationService","Import","LicenseSystemServer","LoginDriver","OpenAccess","Replicator","ReportsDashboard","SetupDB","UniversalTimeConversionUtility","VideoArchiveServer")
# Formats the feature list parameter based on the installation type or features passed in
if ( "Client" -in $Features ) {
Write-Host "Installing client features silently."
$featureParam = "ADDLOCAL=""AlarmMonitoring,AreaAccessManager,BadgeDesigner,DeviceDicoveryService,DeviceDiscovery,FormsDesigner,IDCredentialCenter,MapDesigner,SystemAdministration,VideoViewer,VisitorManagement"" REMOVE=""ApplicationServer,ClientUpdateServer,CommunicationServer,DataConduITService,DataExchangeServer,EnterpriseAdministration,GlobalOutputServer,IDAllocationService,Import,LicenseSystemServer,LoginDriver,OpenAccess,Replicator,ReportsDashboard,SetupDB,UniversalTimeConversionUtility,VideoArchiveServer"""
Write-Host $featureParam
}
elseif ( -not @($Features | where {$availableFeatures -notcontains $_}).Count ) {
# Ensures any arguments are in the available features
# Features can optionabe an array of available Client Features Only
Write-Host "Installing a custom set of features."
$removeFeatures = @()
foreach ($feature in $availableFeatures) {
if ($args -notcontains $feature) {
$removeFeatures += $feature
}
}
$featureParam = "ADDLOCAL=""$($args -join ",")"" REMOVE=""$($removeFeatures -join ",")"""
}
else {
Throw "An error was encountered with the arguments"
}
if ( test-path 'HKLM:\SOFTWARE\WOW6432Node\Lenel\OnGuard') {
$productCode = Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Lenel\OnGuard -Name "ProductCode"
$systemtypedetect="PREV101102=""102"""
}
else {
$systemtypedetect="SYSTEMTYPE=""C"""
}
# When an existing instance is installed: backup the registry, remove the product, restore the registry values for the reinstall to pickup
If ( $productCode ) {
Md Registry::HKLM\SOFTWARE\WOW6432Node\LenelBackup -Force
Copy-Item -Path Registry::HKLM\SOFTWARE\WOW6432Node\Lenel\OnGuard -Destination Registry::HKLM\SOFTWARE\WOW6432Node\LenelBackup -Recurse
$Og_productCode=$productCode.ProductCode.ToString()
cmd /c msiexec.exe /x $Og_productCode /qn
Write-Host $productCode.ProductCode
Copy-Item -Path Registry::HKLM\SOFTWARE\WOW6432Node\LenelBackup\OnGuard -Destination Registry::HKLM\SOFTWARE\WOW6432Node\Lenel -Recurse
}
$currentpath = (Get-Item -Path ".\").FullName
# Install Microsoft Direct X for Managed Code
Write-Host "Installing Direct X for Managed Code..."
cmd /c msiexec.exe /i "$($currentpath)\Windows\Temp\DXManaged\mdxredist.msi" /qn
# Install Acuant Capture/Scanning SDK
Write-Host "Install Acuant Capture/Scanning SDK..."
cmd /c "$($currentpath)\Windows\Temp\CSSN_SDK\sdk_setup_is.exe" /s /a /s /f1 "$($currentpath)\Windows\Temp\CSSN_SDK\setup.iss"
# Install Crystal Reports Runtime
Write-Host "Installing Crystal Reports Runtime"
cmd /c msiexec.exe /i "$($currentpath)\Windows\Temp\Crystal\CRRuntime_32bit_13_0_32.msi" /qn UPGRADE=1
# Install Microsoft Windows Media Encoder
Write-Host "Installing Microsoft Windows Media Encoder"
cmd /c msiexec.exe /i "$($currentpath)\Windows\Temp\WMEncoder\WMEncoder.msi" /qn
# Install UltrView SDK
Write-Host "Installing UltrView SDK"
cmd /c "$($currentpath)\Windows\Temp\UltraView\UltraViewSoftwareDevelopmentKit.exe" -silent
# Setting environmental variables
cmd /c setx /M PATH "%PATH%;C:\Program Files (x86)\Common Files\Lenel;C:\Program Files (x86)\Acuant\SDK" | Out-Null
# Install OnGuard
Write-Host "Installing OnGuard"
cmd /c "$($currentpath)\setup.exe" /s /v"/qn /L*V "$($env:LOCALAPPDATA)\OnGuardSetup.log" $systemtypedetect LICENSESERVER=$LicenseServer DSN=$DatabaseServer DATABASETYPE="SQL" REBOOT=Suppress $featureParam"

View File

@@ -0,0 +1,836 @@
<#
.SYNOPSIS
Remote maintenance toolkit for shopfloor PCs via WinRM.
.DESCRIPTION
Executes maintenance tasks on remote shopfloor PCs using WinRM.
Supports system repair, disk optimization, cleanup, and database updates.
.PARAMETER ComputerName
Single computer name, IP address, or array of computers to target.
.PARAMETER ComputerListFile
Path to text file containing computer names/IPs (one per line).
.PARAMETER All
Target all shopfloor PCs from ShopDB database.
.PARAMETER Task
Maintenance task to execute. Available tasks:
REPAIR:
- DISM : Run DISM /Online /Cleanup-Image /RestoreHealth
- SFC : Run SFC /scannow (System File Checker)
OPTIMIZATION:
- OptimizeDisk : TRIM for SSD, Defrag for HDD
- DiskCleanup : Windows Disk Cleanup (temp files, updates)
- ClearUpdateCache : Clear Windows Update cache (fixes stuck updates)
- ClearBrowserCache: Clear Chrome/Edge cache files
SERVICES:
- RestartSpooler : Restart Print Spooler service
- FlushDNS : Clear DNS resolver cache
- RestartWinRM : Restart WinRM service
TIME/DATE:
- SetTimezone : Set timezone to Eastern Standard Time
- SyncTime : Force time sync with domain controller
.PARAMETER Credential
PSCredential for remote authentication. Prompts if not provided.
.PARAMETER ApiUrl
ShopDB API URL for database updates.
.PARAMETER ThrottleLimit
Maximum concurrent remote sessions (default: 5).
.EXAMPLE
# Run DISM on a single PC
.\Invoke-RemoteMaintenance.ps1 -ComputerName "G1ZTNCX3ESF" -Task DISM
.EXAMPLE
# Optimize disks on multiple PCs
.\Invoke-RemoteMaintenance.ps1 -ComputerName "PC01","PC02" -Task OptimizeDisk
.EXAMPLE
# Run disk cleanup on all shopfloor PCs
.\Invoke-RemoteMaintenance.ps1 -All -Task DiskCleanup
.EXAMPLE
# Update database with disk health info
.\Invoke-RemoteMaintenance.ps1 -All -Task DiskHealth
.EXAMPLE
# Run all database update tasks
.\Invoke-RemoteMaintenance.ps1 -ComputerName "PC01" -Task AllDatabaseUpdates
.NOTES
Author: Shop Floor Tools
Date: 2025-12-26
Requires: PowerShell 5.1+, WinRM enabled on targets, Admin credentials
#>
[CmdletBinding(DefaultParameterSetName='ByName')]
param(
[Parameter(ParameterSetName='ByName', Position=0)]
[string[]]$ComputerName,
[Parameter(ParameterSetName='ByFile')]
[string]$ComputerListFile,
[Parameter(ParameterSetName='All')]
[switch]$All,
[Parameter(Mandatory=$true)]
[ValidateSet(
'DISM', 'SFC', 'OptimizeDisk', 'DiskCleanup', 'ClearUpdateCache',
'RestartSpooler', 'FlushDNS', 'ClearBrowserCache', 'RestartWinRM',
'SetTimezone', 'SyncTime'
)]
[string]$Task,
[Parameter()]
[PSCredential]$Credential,
[Parameter()]
[string]$ApiUrl = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp",
[Parameter()]
[string]$DnsSuffix = "logon.ds.ge.com",
[Parameter()]
[int]$ThrottleLimit = 5
)
# =============================================================================
# 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
# =============================================================================
# Helper Functions
# =============================================================================
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$color = switch ($Level) {
"ERROR" { "Red" }
"WARNING" { "Yellow" }
"SUCCESS" { "Green" }
"TASK" { "Cyan" }
default { "White" }
}
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
}
function Get-ShopfloorPCsFromApi {
param([string]$ApiUrl)
try {
$response = Invoke-RestMethod -Uri "$ApiUrl`?action=getShopfloorPCs" -Method Get -ErrorAction Stop
if ($response.success -and $response.data) {
return $response.data
}
return @()
} catch {
Write-Log "Failed to query API: $_" -Level "ERROR"
return @()
}
}
# =============================================================================
# Maintenance Task Scriptblocks
# =============================================================================
$TaskScripts = @{
# -------------------------------------------------------------------------
# DISM - Deployment Image Servicing and Management
# -------------------------------------------------------------------------
'DISM' = {
$result = @{
Success = $false
Task = 'DISM'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
StartTime = Get-Date
Duration = 0
}
try {
Write-Output "Starting DISM /Online /Cleanup-Image /RestoreHealth..."
Write-Output "This may take 10-30 minutes..."
$dismResult = & dism.exe /Online /Cleanup-Image /RestoreHealth 2>&1
$result.Output = $dismResult -join "`n"
$result.ExitCode = $LASTEXITCODE
$result.Success = ($LASTEXITCODE -eq 0)
if ($result.Success) {
Write-Output "DISM completed successfully."
} else {
Write-Output "DISM completed with exit code: $LASTEXITCODE"
}
} catch {
$result.Error = $_.Exception.Message
}
$result.EndTime = Get-Date
$result.Duration = [math]::Round(((Get-Date) - $result.StartTime).TotalMinutes, 2)
return $result
}
# -------------------------------------------------------------------------
# SFC - System File Checker
# -------------------------------------------------------------------------
'SFC' = {
$result = @{
Success = $false
Task = 'SFC'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
StartTime = Get-Date
Duration = 0
}
try {
Write-Output "Starting SFC /scannow..."
Write-Output "This may take 10-20 minutes..."
$sfcResult = & sfc.exe /scannow 2>&1
$result.Output = $sfcResult -join "`n"
$result.ExitCode = $LASTEXITCODE
# SFC exit codes: 0 = no issues, 1 = issues found and fixed
$result.Success = ($LASTEXITCODE -eq 0 -or $LASTEXITCODE -eq 1)
# Parse output for summary
if ($result.Output -match "found corrupt files and successfully repaired") {
$result.Summary = "Corrupt files found and repaired"
} elseif ($result.Output -match "did not find any integrity violations") {
$result.Summary = "No integrity violations found"
} elseif ($result.Output -match "found corrupt files but was unable to fix") {
$result.Summary = "Corrupt files found but could not be repaired"
$result.Success = $false
} else {
$result.Summary = "Scan completed"
}
} catch {
$result.Error = $_.Exception.Message
}
$result.EndTime = Get-Date
$result.Duration = [math]::Round(((Get-Date) - $result.StartTime).TotalMinutes, 2)
return $result
}
# -------------------------------------------------------------------------
# OptimizeDisk - TRIM for SSD, Defrag for HDD
# -------------------------------------------------------------------------
'OptimizeDisk' = {
$result = @{
Success = $false
Task = 'OptimizeDisk'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
Drives = @()
}
try {
# Get all fixed drives
$volumes = Get-Volume | Where-Object { $_.DriveType -eq 'Fixed' -and $_.DriveLetter }
foreach ($vol in $volumes) {
$driveLetter = $vol.DriveLetter
$driveResult = @{
DriveLetter = $driveLetter
Success = $false
MediaType = "Unknown"
Action = ""
}
# Detect if SSD or HDD
$physicalDisk = Get-PhysicalDisk | Where-Object {
$diskNum = (Get-Partition -DriveLetter $driveLetter -ErrorAction SilentlyContinue).DiskNumber
$_.DeviceId -eq $diskNum
}
if ($physicalDisk) {
$driveResult.MediaType = $physicalDisk.MediaType
}
Write-Output "Optimizing drive ${driveLetter}: ($($driveResult.MediaType))..."
try {
if ($driveResult.MediaType -eq 'SSD') {
# TRIM for SSD
Optimize-Volume -DriveLetter $driveLetter -ReTrim -ErrorAction Stop
$driveResult.Action = "TRIM"
} else {
# Defrag for HDD
Optimize-Volume -DriveLetter $driveLetter -Defrag -ErrorAction Stop
$driveResult.Action = "Defrag"
}
$driveResult.Success = $true
Write-Output " ${driveLetter}: $($driveResult.Action) completed"
} catch {
$driveResult.Error = $_.Exception.Message
Write-Output " ${driveLetter}: Failed - $($_.Exception.Message)"
}
$result.Drives += $driveResult
}
$result.Success = ($result.Drives | Where-Object { $_.Success }).Count -gt 0
$result.Output = "Optimized $($result.Drives.Count) drive(s)"
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# DiskCleanup - Windows Disk Cleanup
# -------------------------------------------------------------------------
'DiskCleanup' = {
$result = @{
Success = $false
Task = 'DiskCleanup'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
SpaceFreed = 0
}
try {
# Get initial free space
$initialFree = (Get-PSDrive C).Free
Write-Output "Running Disk Cleanup..."
# Set cleanup flags in registry for automated cleanup
$cleanupPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
$categories = @(
"Temporary Files",
"Temporary Setup Files",
"Old ChkDsk Files",
"Setup Log Files",
"Windows Update Cleanup",
"Windows Defender",
"Thumbnail Cache",
"Recycle Bin"
)
foreach ($cat in $categories) {
$catPath = Join-Path $cleanupPath $cat
if (Test-Path $catPath) {
Set-ItemProperty -Path $catPath -Name "StateFlags0100" -Value 2 -ErrorAction SilentlyContinue
}
}
# Run cleanmgr with sagerun
$cleanupProcess = Start-Process -FilePath "cleanmgr.exe" -ArgumentList "/sagerun:100" -Wait -PassThru -WindowStyle Hidden
# Also clear temp folders directly
$tempPaths = @(
"$env:TEMP",
"$env:SystemRoot\Temp",
"$env:SystemRoot\Prefetch"
)
$filesDeleted = 0
foreach ($path in $tempPaths) {
if (Test-Path $path) {
$files = Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue
foreach ($file in $files) {
try {
Remove-Item $file.FullName -Force -Recurse -ErrorAction SilentlyContinue
$filesDeleted++
} catch { }
}
}
}
# Calculate space freed
Start-Sleep -Seconds 2
$finalFree = (Get-PSDrive C).Free
$result.SpaceFreed = [math]::Round(($finalFree - $initialFree) / 1GB, 2)
$result.Success = $true
$result.Output = "Cleanup completed. Space freed: $($result.SpaceFreed) GB. Temp files deleted: $filesDeleted"
Write-Output $result.Output
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# ClearUpdateCache - Clear Windows Update cache
# -------------------------------------------------------------------------
'ClearUpdateCache' = {
$result = @{
Success = $false
Task = 'ClearUpdateCache'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
}
try {
Write-Output "Stopping Windows Update service..."
Stop-Service -Name wuauserv -Force -ErrorAction SilentlyContinue
Stop-Service -Name bits -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Write-Output "Clearing SoftwareDistribution folder..."
$swDistPath = "$env:SystemRoot\SoftwareDistribution"
if (Test-Path $swDistPath) {
Remove-Item "$swDistPath\*" -Recurse -Force -ErrorAction SilentlyContinue
}
Write-Output "Clearing catroot2 folder..."
$catroot2Path = "$env:SystemRoot\System32\catroot2"
if (Test-Path $catroot2Path) {
Remove-Item "$catroot2Path\*" -Recurse -Force -ErrorAction SilentlyContinue
}
Write-Output "Starting Windows Update service..."
Start-Service -Name wuauserv -ErrorAction SilentlyContinue
Start-Service -Name bits -ErrorAction SilentlyContinue
$result.Success = $true
$result.Output = "Windows Update cache cleared successfully"
Write-Output $result.Output
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# RestartSpooler - Restart Print Spooler service
# -------------------------------------------------------------------------
'RestartSpooler' = {
$result = @{
Success = $false
Task = 'RestartSpooler'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
}
try {
Write-Output "Stopping Print Spooler..."
Stop-Service -Name Spooler -Force -ErrorAction Stop
# Clear print queue
$printQueuePath = "$env:SystemRoot\System32\spool\PRINTERS"
if (Test-Path $printQueuePath) {
Remove-Item "$printQueuePath\*" -Force -ErrorAction SilentlyContinue
}
Write-Output "Starting Print Spooler..."
Start-Service -Name Spooler -ErrorAction Stop
$spoolerStatus = (Get-Service -Name Spooler).Status
$result.Success = ($spoolerStatus -eq 'Running')
$result.Output = "Print Spooler restarted. Status: $spoolerStatus"
Write-Output $result.Output
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# FlushDNS - Clear DNS resolver cache
# -------------------------------------------------------------------------
'FlushDNS' = {
$result = @{
Success = $false
Task = 'FlushDNS'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
}
try {
Write-Output "Flushing DNS cache..."
$flushResult = & ipconfig /flushdns 2>&1
$result.Output = $flushResult -join "`n"
$result.Success = ($LASTEXITCODE -eq 0)
Write-Output "DNS cache flushed successfully"
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# ClearBrowserCache - Clear Chrome/Edge cache
# -------------------------------------------------------------------------
'ClearBrowserCache' = {
$result = @{
Success = $false
Task = 'ClearBrowserCache'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
BrowsersCleared = @()
}
try {
# Get all user profiles
$userProfiles = Get-ChildItem "C:\Users" -Directory | Where-Object { $_.Name -notin @('Public', 'Default', 'Default User') }
foreach ($profile in $userProfiles) {
$userName = $profile.Name
# Chrome cache paths
$chromeCachePaths = @(
"$($profile.FullName)\AppData\Local\Google\Chrome\User Data\Default\Cache",
"$($profile.FullName)\AppData\Local\Google\Chrome\User Data\Default\Code Cache"
)
# Edge cache paths
$edgeCachePaths = @(
"$($profile.FullName)\AppData\Local\Microsoft\Edge\User Data\Default\Cache",
"$($profile.FullName)\AppData\Local\Microsoft\Edge\User Data\Default\Code Cache"
)
$allPaths = $chromeCachePaths + $edgeCachePaths
foreach ($cachePath in $allPaths) {
if (Test-Path $cachePath) {
try {
Remove-Item "$cachePath\*" -Recurse -Force -ErrorAction SilentlyContinue
$browserType = if ($cachePath -like "*Chrome*") { "Chrome" } else { "Edge" }
$result.BrowsersCleared += "$userName-$browserType"
} catch { }
}
}
}
$result.Success = $true
$result.Output = "Cleared cache for: $($result.BrowsersCleared -join ', ')"
Write-Output $result.Output
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# RestartWinRM - Restart WinRM service
# -------------------------------------------------------------------------
'RestartWinRM' = {
$result = @{
Success = $false
Task = 'RestartWinRM'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
}
try {
Write-Output "Restarting WinRM service..."
Restart-Service -Name WinRM -Force -ErrorAction Stop
Start-Sleep -Seconds 2
$winrmStatus = (Get-Service -Name WinRM).Status
$result.Success = ($winrmStatus -eq 'Running')
$result.Output = "WinRM service restarted. Status: $winrmStatus"
Write-Output $result.Output
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# SetTimezone - Set timezone to Eastern Standard Time
# -------------------------------------------------------------------------
'SetTimezone' = {
$result = @{
Success = $false
Task = 'SetTimezone'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
PreviousTimezone = ""
NewTimezone = ""
}
try {
# Get current timezone
$currentTz = Get-TimeZone
$result.PreviousTimezone = $currentTz.Id
$targetTz = "Eastern Standard Time"
if ($currentTz.Id -eq $targetTz) {
$result.Success = $true
$result.NewTimezone = $currentTz.Id
$result.Output = "Timezone already set to $targetTz"
Write-Output $result.Output
} else {
Write-Output "Changing timezone from $($currentTz.Id) to $targetTz..."
Set-TimeZone -Id $targetTz -ErrorAction Stop
# Verify change
$newTz = Get-TimeZone
$result.NewTimezone = $newTz.Id
$result.Success = ($newTz.Id -eq $targetTz)
$result.Output = "Timezone changed: $($currentTz.Id) -> $($newTz.Id)"
Write-Output $result.Output
}
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
# -------------------------------------------------------------------------
# SyncTime - Sync time with domain controller
# -------------------------------------------------------------------------
'SyncTime' = {
$result = @{
Success = $false
Task = 'SyncTime'
Hostname = $env:COMPUTERNAME
Output = ""
Error = $null
TimeBefore = ""
TimeAfter = ""
TimeSource = ""
}
try {
$result.TimeBefore = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
Write-Output "Syncing time with domain controller..."
# Get current time source
$w32tmSource = & w32tm /query /source 2>&1
$result.TimeSource = ($w32tmSource -join " ").Trim()
# Force time resync
$resyncResult = & w32tm /resync /force 2>&1
$resyncOutput = $resyncResult -join "`n"
# Check if successful
if ($resyncOutput -match "The command completed successfully" -or $LASTEXITCODE -eq 0) {
Start-Sleep -Seconds 1
$result.TimeAfter = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$result.Success = $true
$result.Output = "Time synced with $($result.TimeSource). Time: $($result.TimeAfter)"
} else {
$result.Output = "Sync attempted. Result: $resyncOutput"
$result.Success = $false
}
Write-Output $result.Output
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
}
# =============================================================================
# Main Execution
# =============================================================================
Write-Host ""
Write-Host "=" * 70 -ForegroundColor Cyan
Write-Host " Remote Maintenance Tool - Task: $Task" -ForegroundColor Cyan
Write-Host "=" * 70 -ForegroundColor Cyan
Write-Host ""
# Get credentials
if (-not $Credential) {
Write-Log "Enter credentials for remote PCs:" -Level "INFO"
$Credential = Get-Credential -Message "Enter admin credentials for remote PCs"
if (-not $Credential) {
Write-Log "Credentials required. Exiting." -Level "ERROR"
exit 1
}
}
# Build computer list
$computers = @()
if ($All) {
Write-Log "Querying ShopDB for all shopfloor PCs..." -Level "INFO"
$shopfloorPCs = Get-ShopfloorPCsFromApi -ApiUrl $ApiUrl
$computers = $shopfloorPCs | ForEach-Object { $_.hostname } | Where-Object { $_ }
Write-Log "Found $($computers.Count) shopfloor PCs" -Level "INFO"
} elseif ($ComputerListFile) {
if (Test-Path $ComputerListFile) {
$computers = Get-Content $ComputerListFile | Where-Object { $_.Trim() -and -not $_.StartsWith("#") }
} else {
Write-Log "Computer list file not found: $ComputerListFile" -Level "ERROR"
exit 1
}
} elseif ($ComputerName) {
$computers = $ComputerName
} else {
Write-Log "No computers specified. Use -ComputerName, -ComputerListFile, or -All" -Level "ERROR"
exit 1
}
if ($computers.Count -eq 0) {
Write-Log "No computers to process." -Level "ERROR"
exit 1
}
Write-Log "Target computers: $($computers.Count)" -Level "INFO"
Write-Log "Task: $Task" -Level "TASK"
Write-Host ""
# Build FQDNs
$targetFQDNs = $computers | ForEach-Object {
if ($_ -like "*.*") { $_ } else { "$_.$DnsSuffix" }
}
# Determine which tasks to run
$tasksToRun = @($Task)
# Create session options
$sessionOption = New-PSSessionOption -OpenTimeout 30000 -OperationTimeout 600000 -NoMachineProfile
# Process each task
foreach ($currentTask in $tasksToRun) {
if ($tasksToRun.Count -gt 1) {
Write-Host ""
Write-Log "Running task: $currentTask" -Level "TASK"
}
$scriptBlock = $TaskScripts[$currentTask]
if (-not $scriptBlock) {
Write-Log "Unknown task: $currentTask" -Level "ERROR"
continue
}
# Execute on remote computers
$sessionParams = @{
ComputerName = $targetFQDNs
ScriptBlock = $scriptBlock
Credential = $Credential
SessionOption = $sessionOption
ErrorAction = 'SilentlyContinue'
ErrorVariable = 'remoteErrors'
}
if ($ThrottleLimit -and $PSVersionTable.PSVersion.Major -ge 7) {
$sessionParams.ThrottleLimit = $ThrottleLimit
}
Write-Log "Executing on $($targetFQDNs.Count) computer(s)..." -Level "INFO"
$results = Invoke-Command @sessionParams
# Process results
$successCount = 0
$failCount = 0
foreach ($result in $results) {
if ($result.Success) {
Write-Log "[OK] $($result.Hostname)" -Level "SUCCESS"
# Display task-specific output
switch ($currentTask) {
'OptimizeDisk' {
foreach ($drive in $result.Drives) {
$status = if ($drive.Success) { "OK" } else { "FAIL" }
Write-Host " Drive $($drive.DriveLetter): $($drive.MediaType) - $($drive.Action) [$status]" -ForegroundColor Gray
}
}
'DISM' {
Write-Host " Duration: $($result.Duration) minutes, Exit code: $($result.ExitCode)" -ForegroundColor Gray
}
'SFC' {
Write-Host " $($result.Summary), Duration: $($result.Duration) minutes" -ForegroundColor Gray
}
'DiskCleanup' {
Write-Host " Space freed: $($result.SpaceFreed) GB" -ForegroundColor Gray
}
default {
if ($result.Output) {
Write-Host " $($result.Output)" -ForegroundColor Gray
}
}
}
$successCount++
} else {
Write-Log "[FAIL] $($result.Hostname): $($result.Error)" -Level "ERROR"
$failCount++
}
}
# Handle connection errors
foreach ($err in $remoteErrors) {
$target = if ($err.TargetObject) { $err.TargetObject } else { "Unknown" }
Write-Log "[FAIL] ${target}: $($err.Exception.Message)" -Level "ERROR"
$failCount++
}
}
# 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

View File

@@ -25,6 +25,57 @@ Or run PowerShell directly:
## PowerShell Scripts
### Invoke-RemoteMaintenance.ps1
**Remote maintenance toolkit** - Execute maintenance tasks on shopfloor PCs via WinRM.
**Available Tasks:**
| Category | Task | Description |
|----------|------|-------------|
| **Repair** | `DISM` | Run DISM /Online /Cleanup-Image /RestoreHealth |
| | `SFC` | Run SFC /scannow (System File Checker) |
| **Optimization** | `OptimizeDisk` | TRIM for SSD, Defrag for HDD |
| | `DiskCleanup` | Windows Disk Cleanup (temp files, updates) |
| | `ClearUpdateCache` | Clear Windows Update cache (fixes stuck updates) |
| | `ClearBrowserCache` | Clear Chrome/Edge cache files |
| **Services** | `RestartSpooler` | Restart Print Spooler service |
| | `FlushDNS` | Clear DNS resolver cache |
| | `RestartWinRM` | Restart WinRM service |
| **Time/Date** | `SetTimezone` | Set timezone to Eastern Standard Time |
| | `SyncTime` | Force time sync with domain controller |
**Usage:**
```powershell
# Run DISM on a single PC
.\Invoke-RemoteMaintenance.ps1 -ComputerName "G1ZTNCX3ESF" -Task DISM
# Optimize disks on multiple PCs
.\Invoke-RemoteMaintenance.ps1 -ComputerName "PC01","PC02" -Task OptimizeDisk
# Run disk cleanup on all shopfloor PCs
.\Invoke-RemoteMaintenance.ps1 -All -Task DiskCleanup
# Clear Windows Update cache (fixes stuck updates)
.\Invoke-RemoteMaintenance.ps1 -ComputerName "PC01" -Task ClearUpdateCache
```
**Parameters:**
| Parameter | Default | Description |
|-----------|---------|-------------|
| `-ComputerName` | - | Single or multiple computer names/IPs |
| `-ComputerListFile` | - | Path to text file with computer list |
| `-All` | - | Target all shopfloor PCs from ShopDB |
| `-Task` | (required) | Maintenance task to execute |
| `-Credential` | (prompts) | PSCredential for authentication |
| `-ThrottleLimit` | `5` | Maximum concurrent sessions |
**Notes:**
- DISM and SFC tasks can take 10-30 minutes per PC
- OptimizeDisk automatically detects SSD vs HDD
- ClearUpdateCache stops Windows Update service, clears cache, restarts service
---
### Invoke-RemoteAssetCollection.ps1
**Remote collection via WinRM HTTP** - Execute asset collection on multiple PCs using WinRM over HTTP (port 5985).
@@ -151,28 +202,33 @@ $cred = Get-Credential
## Architecture
```
┌─────────────────────────────────────┐
┌──────────────────────────────────────────────────────────────
│ Management Server │
│ ┌───────────────────────────────┐ │
│ │ Invoke-RemoteAssetCollection │ │
│ │ Update-ShopfloorPCs-Remote │ │
└──────────────┬────────────────┘
└────────────────────────────────────┘
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Update-ShopfloorPCs-Remote.ps1 - Data collection │ │
│ │ Invoke-RemoteMaintenance.ps1 - Maintenance tasks │ │
│ Invoke-RemoteAssetCollection.ps1 - General execution │
└────────────────────────┬───────────────────────────────┘
└───────────────────────────┼──────────────────────────────────┘
│ WinRM (5985/5986)
┌─────────────────────────────────────┐
Shopfloor PC 1
│ ┌───────────────────────────────┐ │
│ │ Update-PC-CompleteAsset.ps1 │ │
└───────────────────────────────┘
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
Shopfloor PC 2
┌───────────────────────────────┐
│ Update-PC-CompleteAsset.ps1 │
└───────────────────────────────┘
└─────────────────────────────────────┘
... (parallel execution)
┌──────────────────────────────────────────────────────────────
Shopfloor PCs
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Data Collection: │ │
│ - System info, network, DNC config, installed apps │
│ │ │ │
│ │ Maintenance Tasks: │ │
- DISM, SFC, Disk Cleanup, Optimize Disk
│ - Restart Spooler, Flush DNS, Clear Caches │
└────────────────────────────────────────────────────────┘
──────────────────────────────────────────────────────────────┘
▼ HTTPS POST
┌──────────────────────────────────────────────────────────────┐
│ ShopDB API │
│ api.asp -> MySQL (machines, communications, dncconfig) │
└──────────────────────────────────────────────────────────────┘
```
## WinRM Setup

40
tools/Enable-WinRM.bat Normal file
View File

@@ -0,0 +1,40 @@
@echo off
:: Enable WinRM for Remote Management
:: Run as Administrator
echo ============================================
echo Enable WinRM for ShopDB Remote Management
echo ============================================
echo.
:: Check for admin rights
net session >nul 2>&1
if %errorLevel% neq 0 (
echo ERROR: Please run as Administrator!
echo Right-click and select "Run as administrator"
pause
exit /b 1
)
echo Enabling WinRM...
powershell -Command "Enable-PSRemoting -Force -SkipNetworkProfileCheck" 2>nul
echo.
echo Setting firewall rules...
powershell -Command "Set-NetFirewallRule -Name 'WINRM-HTTP-In-TCP' -Enabled True -Profile Any" 2>nul
netsh advfirewall firewall add rule name="WinRM-HTTP" dir=in localport=5985 protocol=TCP action=allow >nul 2>&1
echo.
echo Starting WinRM service...
sc config winrm start= auto >nul
net start winrm >nul 2>&1
echo.
echo ============================================
echo WinRM Enabled Successfully!
echo This PC can now be remotely managed.
echo ============================================
echo.
echo Hostname: %COMPUTERNAME%
echo.
pause