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:
12
DNC/update_dnc.bat
Normal file
12
DNC/update_dnc.bat
Normal 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
3
dncfix/fixvtm.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
powershell.exe -ExecutionPolicy Bypass -File ".\vtmfix.ps1"
|
||||
pause
|
||||
64
dncfix/vtmfix.ps1
Normal file
64
dncfix/vtmfix.ps1
Normal 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
1
edncfix
Submodule
Submodule edncfix added at 28641c47c5
93
onguard/silentInstall.ps1
Normal file
93
onguard/silentInstall.ps1
Normal 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"
|
||||
836
remote-execution/Invoke-RemoteMaintenance.ps1
Normal file
836
remote-execution/Invoke-RemoteMaintenance.ps1
Normal 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
|
||||
@@ -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
40
tools/Enable-WinRM.bat
Normal 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
|
||||
Reference in New Issue
Block a user