Structure: - asset-collection/: Local PC data collection scripts - remote-execution/: WinRM remote execution scripts - setup-utilities/: Configuration and testing utilities - registry-backup/: GE registry backup scripts - winrm-https/: WinRM HTTPS certificate setup - docs/: Complete documentation Each folder includes a README with detailed documentation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
484 lines
17 KiB
PowerShell
484 lines
17 KiB
PowerShell
#Requires -RunAsAdministrator
|
|
<#
|
|
.SYNOPSIS
|
|
Remotely executes asset collection script on shopfloor PCs using WinRM.
|
|
|
|
.DESCRIPTION
|
|
This script uses WinRM to remotely execute the Update-PC-CompleteAsset.ps1 script
|
|
on multiple shopfloor PCs. It handles:
|
|
1. WinRM configuration and trusted hosts setup
|
|
2. Credential management for remote connections
|
|
3. Parallel or sequential execution across multiple PCs
|
|
4. Error handling and logging for remote operations
|
|
5. Collection of results from each remote PC
|
|
|
|
.PARAMETER ComputerList
|
|
Array of computer names or IP addresses to collect data from.
|
|
|
|
.PARAMETER ComputerListFile
|
|
Path to a text file containing computer names/IPs (one per line).
|
|
|
|
.PARAMETER Credential
|
|
PSCredential object for authenticating to remote computers.
|
|
If not provided, will prompt for credentials.
|
|
|
|
.PARAMETER MaxConcurrent
|
|
Maximum number of concurrent remote sessions (default: 5).
|
|
|
|
.PARAMETER ProxyURL
|
|
URL for the warranty proxy server (passed to remote script).
|
|
|
|
.PARAMETER DashboardURL
|
|
URL for the dashboard API (passed to remote script).
|
|
|
|
.PARAMETER SkipWarranty
|
|
Skip warranty lookups on remote PCs (passed to remote script).
|
|
|
|
.PARAMETER LogPath
|
|
Path for log files (default: .\logs\remote-collection.log).
|
|
|
|
.PARAMETER TestConnections
|
|
Test remote connections without running the full collection.
|
|
|
|
.PARAMETER ScriptPath
|
|
Path to the Update-PC-CompleteAsset.ps1 script on remote computers.
|
|
Default: C:\Scripts\Update-PC-CompleteAsset.ps1
|
|
|
|
.EXAMPLE
|
|
# Collect from specific computers with prompted credentials
|
|
.\Invoke-RemoteAssetCollection.ps1 -ComputerList @("10.48.130.100", "10.48.130.101")
|
|
|
|
.EXAMPLE
|
|
# Collect from computers listed in file with stored credentials
|
|
$cred = Get-Credential
|
|
.\Invoke-RemoteAssetCollection.ps1 -ComputerListFile ".\shopfloor-pcs.txt" -Credential $cred
|
|
|
|
.EXAMPLE
|
|
# Test connections only
|
|
.\Invoke-RemoteAssetCollection.ps1 -ComputerList @("10.48.130.100") -TestConnections
|
|
|
|
.NOTES
|
|
Author: System Administrator
|
|
Date: 2025-09-26
|
|
Version: 1.0
|
|
|
|
Prerequisites:
|
|
1. WinRM must be enabled on target computers
|
|
2. PowerShell remoting must be enabled on target computers
|
|
3. Update-PC-CompleteAsset.ps1 must be present on target computers
|
|
4. Credentials with admin rights on target computers
|
|
|
|
WinRM Setup Commands (run on management server):
|
|
Enable-PSRemoting -Force
|
|
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force
|
|
|
|
WinRM Setup Commands (run on target computers):
|
|
Enable-PSRemoting -Force
|
|
Set-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)" -Enabled True
|
|
#>
|
|
|
|
param(
|
|
[Parameter(Mandatory=$false)]
|
|
[string[]]$ComputerList = @(),
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$ComputerListFile,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[PSCredential]$Credential,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[int]$MaxConcurrent = 5,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$ProxyURL = "http://10.48.130.158/vendor-api-proxy.php",
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$DashboardURL = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp",
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[switch]$SkipWarranty = $true,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$LogPath = ".\logs\remote-collection.log",
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[switch]$TestConnections = $false,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$ScriptPath = "C:\Scripts\Update-PC-CompleteAsset.ps1"
|
|
)
|
|
|
|
# =============================================================================
|
|
# SSL/TLS Certificate Bypass for HTTPS connections
|
|
# =============================================================================
|
|
# This allows connections to servers with self-signed or untrusted certificates
|
|
try {
|
|
# For PowerShell 5.x - use .NET callback
|
|
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 {
|
|
# Silently continue if already set
|
|
}
|
|
|
|
# Set TLS 1.2 as minimum (required for modern HTTPS)
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
|
|
# Initialize logging
|
|
function Initialize-Logging {
|
|
param([string]$LogPath)
|
|
|
|
$logDir = Split-Path $LogPath -Parent
|
|
if (-not (Test-Path $logDir)) {
|
|
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
|
|
}
|
|
|
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
Add-Content -Path $LogPath -Value "[$timestamp] Remote asset collection started"
|
|
}
|
|
|
|
function Write-Log {
|
|
param([string]$Message, [string]$LogPath, [string]$Level = "INFO")
|
|
|
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
$logEntry = "[$timestamp] [$Level] $Message"
|
|
|
|
Add-Content -Path $LogPath -Value $logEntry
|
|
|
|
switch ($Level) {
|
|
"ERROR" { Write-Host $logEntry -ForegroundColor Red }
|
|
"WARN" { Write-Host $logEntry -ForegroundColor Yellow }
|
|
"SUCCESS" { Write-Host $logEntry -ForegroundColor Green }
|
|
default { Write-Host $logEntry -ForegroundColor White }
|
|
}
|
|
}
|
|
|
|
function Get-ComputerTargets {
|
|
param([string[]]$ComputerList, [string]$ComputerListFile)
|
|
|
|
$targets = @()
|
|
|
|
# Add computers from direct list
|
|
if ($ComputerList.Count -gt 0) {
|
|
$targets += $ComputerList
|
|
}
|
|
|
|
# Add computers from file
|
|
if (-not [string]::IsNullOrEmpty($ComputerListFile)) {
|
|
if (Test-Path $ComputerListFile) {
|
|
$fileTargets = Get-Content $ComputerListFile | Where-Object { $_.Trim() -ne "" -and -not $_.StartsWith("#") }
|
|
$targets += $fileTargets
|
|
} else {
|
|
Write-Log "Computer list file not found: $ComputerListFile" $LogPath "ERROR"
|
|
}
|
|
}
|
|
|
|
# Remove duplicates and return
|
|
return ($targets | Sort-Object -Unique)
|
|
}
|
|
|
|
function Test-WinRMConnection {
|
|
param([string]$ComputerName, [PSCredential]$Credential)
|
|
|
|
try {
|
|
$session = New-PSSession -ComputerName $ComputerName -Credential $Credential -ErrorAction Stop
|
|
Remove-PSSession $session
|
|
return $true
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Test-RemoteScriptExists {
|
|
param([string]$ComputerName, [PSCredential]$Credential, [string]$ScriptPath)
|
|
|
|
try {
|
|
$result = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock {
|
|
param($Path)
|
|
Test-Path $Path
|
|
} -ArgumentList $ScriptPath
|
|
|
|
return $result
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Invoke-RemoteAssetScript {
|
|
param(
|
|
[string]$ComputerName,
|
|
[PSCredential]$Credential,
|
|
[string]$ScriptPath,
|
|
[string]$ProxyURL,
|
|
[string]$DashboardURL,
|
|
[bool]$SkipWarranty,
|
|
[string]$LogPath
|
|
)
|
|
|
|
try {
|
|
Write-Log "Starting asset collection on $ComputerName" $LogPath "INFO"
|
|
|
|
# Execute the script remotely
|
|
$result = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock {
|
|
param($ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty)
|
|
|
|
# Change to script directory
|
|
$scriptDir = Split-Path $ScriptPath -Parent
|
|
Set-Location $scriptDir
|
|
|
|
# Build parameters
|
|
$params = @{
|
|
ProxyURL = $ProxyURL
|
|
DashboardURL = $DashboardURL
|
|
}
|
|
|
|
if ($SkipWarranty) {
|
|
$params.SkipWarranty = $true
|
|
}
|
|
|
|
# Execute the script and capture output
|
|
try {
|
|
& $ScriptPath @params
|
|
return @{
|
|
Success = $true
|
|
Output = "Script completed successfully"
|
|
Error = $null
|
|
}
|
|
}
|
|
catch {
|
|
return @{
|
|
Success = $false
|
|
Output = $null
|
|
Error = $_.Exception.Message
|
|
}
|
|
}
|
|
} -ArgumentList $ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty
|
|
|
|
if ($result.Success) {
|
|
Write-Log "Asset collection completed successfully on $ComputerName" $LogPath "SUCCESS"
|
|
|
|
# Update WinRM status in database - WinRM connection was successful
|
|
# Get the actual hostname from the remote PC (in case we connected via IP)
|
|
try {
|
|
$remoteHostname = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock {
|
|
$env:COMPUTERNAME
|
|
} -ErrorAction SilentlyContinue
|
|
|
|
if ([string]::IsNullOrEmpty($remoteHostname)) {
|
|
$remoteHostname = $ComputerName
|
|
}
|
|
|
|
$winrmBody = @{
|
|
action = "updateWinRMStatus"
|
|
hostname = $remoteHostname
|
|
hasWinRM = "1"
|
|
}
|
|
Write-Log "Updating WinRM status for hostname: $remoteHostname (connected as: $ComputerName)" $LogPath "INFO"
|
|
$winrmResponse = Invoke-RestMethod -Uri $DashboardURL -Method Post -Body $winrmBody -ErrorAction Stop
|
|
if ($winrmResponse.success) {
|
|
Write-Log "WinRM status updated for $remoteHostname" $LogPath "INFO"
|
|
} else {
|
|
Write-Log "WinRM update response: $($winrmResponse | ConvertTo-Json -Compress)" $LogPath "WARN"
|
|
}
|
|
}
|
|
catch {
|
|
Write-Log "Failed to update WinRM status for $ComputerName - $($_.Exception.Message)" $LogPath "WARN"
|
|
}
|
|
|
|
return @{ Success = $true; Computer = $ComputerName; Message = $result.Output }
|
|
} else {
|
|
Write-Log "Asset collection failed on $ComputerName: $($result.Error)" $LogPath "ERROR"
|
|
return @{ Success = $false; Computer = $ComputerName; Message = $result.Error }
|
|
}
|
|
}
|
|
catch {
|
|
$errorMsg = "Failed to execute on $ComputerName: $($_.Exception.Message)"
|
|
Write-Log $errorMsg $LogPath "ERROR"
|
|
return @{ Success = $false; Computer = $ComputerName; Message = $errorMsg }
|
|
}
|
|
}
|
|
|
|
function Show-WinRMSetupInstructions {
|
|
Write-Host "=== WinRM Setup Instructions ===" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
Write-Host "On Management Server (this computer):" -ForegroundColor Yellow
|
|
Write-Host " Enable-PSRemoting -Force" -ForegroundColor White
|
|
Write-Host " Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force" -ForegroundColor White
|
|
Write-Host ""
|
|
Write-Host "On Target Computers:" -ForegroundColor Yellow
|
|
Write-Host " Enable-PSRemoting -Force" -ForegroundColor White
|
|
Write-Host " Set-NetFirewallRule -DisplayName 'Windows Remote Management (HTTP-In)' -Enabled True" -ForegroundColor White
|
|
Write-Host ""
|
|
Write-Host "For specific computer trust:" -ForegroundColor Yellow
|
|
Write-Host " Set-Item WSMan:\localhost\Client\TrustedHosts -Value '10.48.130.100,10.48.130.101' -Force" -ForegroundColor White
|
|
Write-Host ""
|
|
}
|
|
|
|
# Main execution
|
|
try {
|
|
Write-Host "=== Remote Asset Collection Script ===" -ForegroundColor Cyan
|
|
Write-Host "Starting at $(Get-Date)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
|
|
# Initialize logging
|
|
Initialize-Logging -LogPath $LogPath
|
|
|
|
# Get target computers
|
|
$computers = Get-ComputerTargets -ComputerList $ComputerList -ComputerListFile $ComputerListFile
|
|
|
|
if ($computers.Count -eq 0) {
|
|
Write-Log "No target computers specified. Use -ComputerList or -ComputerListFile parameter." $LogPath "ERROR"
|
|
Show-WinRMSetupInstructions
|
|
exit 1
|
|
}
|
|
|
|
Write-Log "Target computers: $($computers -join ', ')" $LogPath "INFO"
|
|
|
|
# Get credentials if not provided
|
|
if (-not $Credential) {
|
|
Write-Host "Enter credentials for remote computer access:" -ForegroundColor Yellow
|
|
$Credential = Get-Credential
|
|
if (-not $Credential) {
|
|
Write-Log "No credentials provided" $LogPath "ERROR"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Test connections if requested
|
|
if ($TestConnections) {
|
|
Write-Host "`nTesting connections only..." -ForegroundColor Yellow
|
|
foreach ($computer in $computers) {
|
|
Write-Host "Testing $computer..." -NoNewline
|
|
if (Test-WinRMConnection -ComputerName $computer -Credential $Credential) {
|
|
Write-Host " [OK]" -ForegroundColor Green
|
|
Write-Log "Connection test successful for $computer" $LogPath "SUCCESS"
|
|
} else {
|
|
Write-Host " [FAIL]" -ForegroundColor Red
|
|
Write-Log "Connection test failed for $computer" $LogPath "ERROR"
|
|
}
|
|
}
|
|
exit 0
|
|
}
|
|
|
|
# Validate all connections and script existence before starting collection
|
|
Write-Host "`nValidating remote connections and script availability..." -ForegroundColor Yellow
|
|
$validComputers = @()
|
|
|
|
foreach ($computer in $computers) {
|
|
Write-Host "Validating $computer..." -NoNewline
|
|
|
|
if (-not (Test-WinRMConnection -ComputerName $computer -Credential $Credential)) {
|
|
Write-Host " [CONNECTION FAILED]" -ForegroundColor Red
|
|
Write-Log "Cannot connect to $computer via WinRM" $LogPath "ERROR"
|
|
continue
|
|
}
|
|
|
|
if (-not (Test-RemoteScriptExists -ComputerName $computer -Credential $Credential -ScriptPath $ScriptPath)) {
|
|
Write-Host " [SCRIPT NOT FOUND]" -ForegroundColor Red
|
|
Write-Log "Script not found on $computer at $ScriptPath" $LogPath "ERROR"
|
|
continue
|
|
}
|
|
|
|
Write-Host " [OK]" -ForegroundColor Green
|
|
$validComputers += $computer
|
|
}
|
|
|
|
if ($validComputers.Count -eq 0) {
|
|
Write-Log "No valid computers found for data collection" $LogPath "ERROR"
|
|
Show-WinRMSetupInstructions
|
|
exit 1
|
|
}
|
|
|
|
Write-Log "Valid computers for collection: $($validComputers -join ', ')" $LogPath "INFO"
|
|
|
|
# Execute asset collection
|
|
Write-Host "`nStarting asset collection on $($validComputers.Count) computers..." -ForegroundColor Cyan
|
|
Write-Host "Max concurrent sessions: $MaxConcurrent" -ForegroundColor Gray
|
|
Write-Host ""
|
|
|
|
$results = @()
|
|
$jobs = @()
|
|
$completed = 0
|
|
|
|
# Process computers in batches
|
|
for ($i = 0; $i -lt $validComputers.Count; $i += $MaxConcurrent) {
|
|
$batch = $validComputers[$i..($i + $MaxConcurrent - 1)]
|
|
|
|
Write-Host "Processing batch: $($batch -join ', ')" -ForegroundColor Yellow
|
|
|
|
# Start jobs for current batch
|
|
foreach ($computer in $batch) {
|
|
$job = Start-Job -ScriptBlock {
|
|
param($Computer, $Credential, $ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty, $LogPath, $Functions)
|
|
|
|
# Import functions into job scope
|
|
Invoke-Expression $Functions
|
|
|
|
Invoke-RemoteAssetScript -ComputerName $Computer -Credential $Credential `
|
|
-ScriptPath $ScriptPath -ProxyURL $ProxyURL -DashboardURL $DashboardURL `
|
|
-SkipWarranty $SkipWarranty -LogPath $LogPath
|
|
|
|
} -ArgumentList $computer, $Credential, $ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty, $LogPath, (Get-Content $PSCommandPath | Out-String)
|
|
|
|
$jobs += $job
|
|
}
|
|
|
|
# Wait for batch to complete
|
|
$jobs | Wait-Job | Out-Null
|
|
|
|
# Collect results
|
|
foreach ($job in $jobs) {
|
|
$result = Receive-Job $job
|
|
$results += $result
|
|
Remove-Job $job
|
|
$completed++
|
|
|
|
$computer = $result.Computer
|
|
if ($result.Success) {
|
|
Write-Host "[✓] $computer - Completed successfully" -ForegroundColor Green
|
|
} else {
|
|
Write-Host "[✗] $computer - Failed: $($result.Message)" -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
$jobs = @()
|
|
Write-Host "Batch completed. Progress: $completed/$($validComputers.Count)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
}
|
|
|
|
# Summary
|
|
$successful = ($results | Where-Object { $_.Success }).Count
|
|
$failed = ($results | Where-Object { -not $_.Success }).Count
|
|
|
|
Write-Host "=== Collection Summary ===" -ForegroundColor Cyan
|
|
Write-Host "Total computers: $($validComputers.Count)" -ForegroundColor White
|
|
Write-Host "Successful: $successful" -ForegroundColor Green
|
|
Write-Host "Failed: $failed" -ForegroundColor Red
|
|
|
|
if ($failed -gt 0) {
|
|
Write-Host "`nFailed computers:" -ForegroundColor Yellow
|
|
$results | Where-Object { -not $_.Success } | ForEach-Object {
|
|
Write-Host " $($_.Computer): $($_.Message)" -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
Write-Log "Collection completed. Success: $successful, Failed: $failed" $LogPath "INFO"
|
|
|
|
} catch {
|
|
Write-Log "Fatal error: $($_.Exception.Message)" $LogPath "ERROR"
|
|
exit 1
|
|
} |