Update PowerShell scripts for production API and SSL bypass

- Changed Invoke-RemoteAssetCollection.ps1 default URL to production:
  https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp
- Added SSL/TLS certificate bypass for HTTPS connections in both:
  - Update-PC-CompleteAsset.ps1
  - Invoke-RemoteAssetCollection.ps1
- Set TLS 1.2 as minimum protocol version
- Security group logon\g03078610 confirmed correct

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-12-09 13:12:49 -05:00
parent 869788c0e3
commit 179d39eb99
3 changed files with 2348 additions and 0 deletions

View File

@@ -0,0 +1,480 @@
# Functions to collect shopfloor PC network and communication configurations
# Function to get all network interfaces and their configurations
function Get-NetworkInterfaceConfig {
Write-Host " Collecting network interface information..." -ForegroundColor Yellow
$interfaces = @()
try {
# Get all network adapters with IP configurations
$adapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' }
foreach ($adapter in $adapters) {
$ipConfig = Get-NetIPConfiguration -InterfaceIndex $adapter.ifIndex -ErrorAction SilentlyContinue
if ($ipConfig -and $ipConfig.IPv4Address) {
foreach ($ip in $ipConfig.IPv4Address) {
$gateway = if ($ipConfig.IPv4DefaultGateway) { $ipConfig.IPv4DefaultGateway[0].NextHop } else { $null }
# Determine if this is a machine network (192.168.*.*)
$isMachineNetwork = $ip.IPAddress -match '^192\.168\.'
$interface = @{
InterfaceName = $adapter.Name
IPAddress = $ip.IPAddress
SubnetMask = $ip.PrefixLength # Will need conversion
DefaultGateway = $gateway
MACAddress = $adapter.MacAddress
IsDHCP = if ($ipConfig.NetIPv4Interface.Dhcp -eq 'Enabled') { 1 } else { 0 }
IsActive = 1
IsMachineNetwork = if ($isMachineNetwork) { 1 } else { 0 }
}
$interfaces += $interface
if ($isMachineNetwork) {
Write-Host " Found machine network: $($ip.IPAddress) on $($adapter.Name)" -ForegroundColor Cyan
}
else {
Write-Host " Found network: $($ip.IPAddress) on $($adapter.Name)" -ForegroundColor Gray
}
}
}
}
}
catch {
Write-Host " Error collecting network info: $_" -ForegroundColor Red
}
# Alternative method using WMI if NetAdapter cmdlets fail
if ($interfaces.Count -eq 0) {
try {
$wmiAdapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -eq $true }
foreach ($adapter in $wmiAdapters) {
if ($adapter.IPAddress) {
foreach ($i in 0..($adapter.IPAddress.Count - 1)) {
$ip = $adapter.IPAddress[$i]
# Skip IPv6 addresses
if ($ip -match ':') { continue }
$isMachineNetwork = $ip -match '^192\.168\.'
$interface = @{
InterfaceName = $adapter.Description
IPAddress = $ip
SubnetMask = $adapter.IPSubnet[$i]
DefaultGateway = if ($adapter.DefaultIPGateway) { $adapter.DefaultIPGateway[0] } else { $null }
MACAddress = $adapter.MACAddress
IsDHCP = $adapter.DHCPEnabled
IsActive = 1
IsMachineNetwork = if ($isMachineNetwork) { 1 } else { 0 }
}
$interfaces += $interface
if ($isMachineNetwork) {
Write-Host " Found machine network: $ip on $($adapter.Description)" -ForegroundColor Cyan
}
}
}
}
}
catch {
Write-Host " WMI method also failed: $_" -ForegroundColor Red
}
}
return $interfaces
}
# Function to get serial port configurations from registry
function Get-SerialPortConfig {
Write-Host " Collecting serial port configurations..." -ForegroundColor Yellow
$configs = @()
# Registry paths to check
$registryPaths = @{
'Serial' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\Serial', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\Serial')
'Mark' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\Mark', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\Mark')
'PPDCS' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\PPDCS', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\PPDCS')
'TQM9030' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\TQM9030', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\TQM9030')
'TQMCaron' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\TQMCaron', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\TQMCaron')
}
foreach ($configType in $registryPaths.Keys) {
foreach ($path in $registryPaths[$configType]) {
if (Test-Path $path) {
try {
$regValues = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
if ($regValues) {
$config = @{
ConfigType = $configType
PortID = $regValues.'Port Id' -replace 'Port Id2', ''
Baud = $regValues.Baud
DataBits = $regValues.'Data Bits'
StopBits = $regValues.'Stop Bits'
Parity = $regValues.Parity
CRLF = $regValues.CRLF
IPAddress = $null
SocketNo = $null
AdditionalSettings = @{}
}
# Collect any additional settings
$standardKeys = @('Port Id', 'Baud', 'Data Bits', 'Stop Bits', 'Parity', 'CRLF', 'PSPath', 'PSParentPath', 'PSChildName', 'PSProvider')
foreach ($prop in $regValues.PSObject.Properties) {
if ($prop.Name -notin $standardKeys -and $prop.Value) {
$config.AdditionalSettings[$prop.Name] = $prop.Value
}
}
# Convert additional settings to JSON
if ($config.AdditionalSettings.Count -gt 0) {
$config.AdditionalSettings = $config.AdditionalSettings | ConvertTo-Json -Compress
}
else {
$config.AdditionalSettings = $null
}
if ($config.PortID) {
$configs += $config
Write-Host " Found $configType config: Port $($config.PortID), Baud $($config.Baud)" -ForegroundColor Cyan
}
}
}
catch {
Write-Host " Error reading $configType registry: $_" -ForegroundColor Red
}
}
}
}
# Check for eFocas configuration (network-based)
$efocasPaths = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\eFocas', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\eFocas')
foreach ($path in $efocasPaths) {
if (Test-Path $path) {
try {
$regValues = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
if ($regValues -and $regValues.IpAddr) {
$config = @{
ConfigType = 'eFocas'
PortID = $null
Baud = $null
DataBits = $null
StopBits = $null
Parity = $null
CRLF = $null
IPAddress = $regValues.IpAddr
SocketNo = $regValues.SocketNo
AdditionalSettings = @{
DualPath = $regValues.DualPath
Path1Name = $regValues.Path1Name
Path2Name = $regValues.Path2Name
Danobat = $regValues.Danobat
DataServer = $regValues.DataServer
} | ConvertTo-Json -Compress
}
$configs += $config
Write-Host " Found eFocas config: IP $($config.IPAddress), Socket $($config.SocketNo)" -ForegroundColor Cyan
}
}
catch {
Write-Host " Error reading eFocas registry: $_" -ForegroundColor Red
}
}
}
return $configs
}
# Function to get GE Aircraft Engines registry information and DualPath configuration
function Get-GERegistryInfo {
Write-Host " Collecting GE Aircraft Engines registry information..." -ForegroundColor Yellow
$geInfo = @{
Registry32Bit = $false
Registry64Bit = $false
DualPathEnabled = $null
Path1Name = $null
Path2Name = $null
RegistryNotes = @{}
}
# Check both 32-bit and 64-bit registry paths
$registryPaths = @{
'32bit' = 'HKLM:\SOFTWARE\GE Aircraft Engines'
'64bit' = 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines'
}
foreach ($pathType in $registryPaths.Keys) {
$basePath = $registryPaths[$pathType]
Write-Host " Checking $pathType registry: $basePath" -ForegroundColor Gray
if (Test-Path $basePath) {
Write-Host " [FOUND] GE Aircraft Engines in $pathType registry" -ForegroundColor Green
if ($pathType -eq '32bit') {
$geInfo.Registry32Bit = $true
} else {
$geInfo.Registry64Bit = $true
}
# Collect information about what's under GE Aircraft Engines
try {
$subKeys = Get-ChildItem -Path $basePath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name | ForEach-Object { Split-Path $_ -Leaf }
$geInfo.RegistryNotes[$pathType] = @{
BasePath = $basePath
SubKeys = $subKeys -join ', '
Found = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
Write-Host " Sub-keys found: $($subKeys -join ', ')" -ForegroundColor Cyan
}
catch {
Write-Host " Error reading sub-keys: $_" -ForegroundColor Red
$geInfo.RegistryNotes[$pathType] = @{
Error = $_.Exception.Message
}
}
# Check for DualPath configuration in eFocas
$efocasPath = "$basePath\DNC\eFocas"
if (Test-Path $efocasPath) {
Write-Host " Checking eFocas configuration for DualPath..." -ForegroundColor Yellow
try {
$efocasValues = Get-ItemProperty -Path $efocasPath -ErrorAction SilentlyContinue
if ($efocasValues.DualPath) {
Write-Host " [FOUND] DualPath = $($efocasValues.DualPath)" -ForegroundColor Green
# Handle case where both registry locations exist - prioritize settings from first found
if ($geInfo.DualPathEnabled -eq $null) {
$geInfo.DualPathEnabled = $efocasValues.DualPath -eq 'YES'
Write-Host " Setting DualPath from $pathType registry: $($geInfo.DualPathEnabled)" -ForegroundColor Cyan
} else {
Write-Host " DualPath already set from other registry location, keeping existing value" -ForegroundColor Yellow
}
# Handle Path1Name - use first non-empty value found
if (!$geInfo.Path1Name -and $efocasValues.Path1Name) {
$geInfo.Path1Name = $efocasValues.Path1Name
Write-Host " Path1Name = $($efocasValues.Path1Name)" -ForegroundColor Cyan
}
# Handle Path2Name - use first non-empty value found
if (!$geInfo.Path2Name -and $efocasValues.Path2Name) {
$geInfo.Path2Name = $efocasValues.Path2Name
Write-Host " Path2Name = $($efocasValues.Path2Name)" -ForegroundColor Cyan
}
# Store additional eFocas settings
$geInfo.RegistryNotes["$pathType-eFocas"] = @{
DualPath = $efocasValues.DualPath
Path1Name = $efocasValues.Path1Name
Path2Name = $efocasValues.Path2Name
IpAddr = $efocasValues.IpAddr
SocketNo = $efocasValues.SocketNo
Danobat = $efocasValues.Danobat
DataServer = $efocasValues.DataServer
}
}
}
catch {
Write-Host " Error reading eFocas configuration: $_" -ForegroundColor Red
}
}
} else {
Write-Host " [NOT FOUND] No GE Aircraft Engines in $pathType registry" -ForegroundColor Gray
}
}
# Summary
Write-Host " GE Registry Summary:" -ForegroundColor Green
Write-Host " 32-bit registry: $(if ($geInfo.Registry32Bit) { 'YES' } else { 'NO' })" -ForegroundColor Cyan
Write-Host " 64-bit registry: $(if ($geInfo.Registry64Bit) { 'YES' } else { 'NO' })" -ForegroundColor Cyan
Write-Host " DualPath enabled: $(if ($geInfo.DualPathEnabled -eq $null) { 'NOT FOUND' } elseif ($geInfo.DualPathEnabled) { 'YES' } else { 'NO' })" -ForegroundColor Cyan
if ($geInfo.Path1Name) { Write-Host " Path1Name: $($geInfo.Path1Name)" -ForegroundColor Cyan }
if ($geInfo.Path2Name) { Write-Host " Path2Name: $($geInfo.Path2Name)" -ForegroundColor Cyan }
return $geInfo
}
# Function to get DNC configuration from registry
function Get-DNCConfig {
Write-Host " Collecting DNC configuration..." -ForegroundColor Yellow
$dncConfig = $null
$paths = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General')
Write-Host " Checking registry paths for DNC config..." -ForegroundColor Gray
foreach ($path in $paths) {
Write-Host " Checking path: $path" -ForegroundColor Gray
if (Test-Path $path) {
Write-Host " Path exists! Reading values..." -ForegroundColor Green
try {
$general = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
$mxPath = $path -replace 'General', 'MX'
$mx = Get-ItemProperty -Path $mxPath -ErrorAction SilentlyContinue
$fmsPath = $path -replace 'General', 'FMS'
$fms = Get-ItemProperty -Path $fmsPath -ErrorAction SilentlyContinue
if ($general) {
Write-Host " Found General config values!" -ForegroundColor Green
$dncConfig = @{
Site = $general.Site
CNC = $general.Cnc
NcIF = $general.NcIF
MachineNo = $general.MachineNo
HostType = $general.HostType
FtpHostPrimary = if ($mx) { $mx.FtpHostPrimary } else { $null }
FtpHostSecondary = if ($mx) { $mx.FtpHostSecondary } else { $null }
FtpAccount = if ($mx) { $mx.FtpAccount } else { $null }
Debug = $general.Debug
Uploads = $general.Uploads
Scanner = $general.Scanner
Dripfeed = $general.Dripfeed
AdditionalSettings = @{
Mode = $general.Mode
'Unit/Area' = $general.'Unit/Area'
DvUpldDir = $general.DvUpldDir
Ncedt = $general.Ncedt
Maint = $general.Maint
ChangeWorkstation = $general.ChangeWorkstation
FMSHostPrimary = if ($fms) { $fms.FMSHostPrimary } else { $null }
FMSHostSecondary = if ($fms) { $fms.FMSHostSecondary } else { $null }
} | ConvertTo-Json -Compress
}
Write-Host " Found DNC config: Site=$($dncConfig.Site), MachineNo=$($dncConfig.MachineNo), CNC=$($dncConfig.CNC)" -ForegroundColor Cyan
Write-Host " DNC Config JSON: $($dncConfig | ConvertTo-Json -Compress)" -ForegroundColor Gray
break
}
}
catch {
Write-Host " Error reading DNC registry: $_" -ForegroundColor Red
}
} else {
Write-Host " Path does not exist" -ForegroundColor Yellow
}
}
if (-not $dncConfig) {
Write-Host " No DNC configuration found in registry" -ForegroundColor Yellow
}
return $dncConfig
}
# Main function to collect all shopfloor configurations
function Get-ShopfloorConfigurations {
Write-Host "`nCollecting shopfloor-specific configurations..." -ForegroundColor Yellow
$configurations = @{
NetworkInterfaces = Get-NetworkInterfaceConfig
CommConfigs = Get-SerialPortConfig
DNCConfig = Get-DNCConfig
GERegistryInfo = Get-GERegistryInfo
}
# Summary
Write-Host "`n Configuration Summary:" -ForegroundColor Green
Write-Host " Network Interfaces: $($configurations.NetworkInterfaces.Count)" -ForegroundColor Cyan
Write-Host " Comm Configs: $($configurations.CommConfigs.Count)" -ForegroundColor Cyan
Write-Host " DNC Config: $(if ($configurations.DNCConfig) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
Write-Host " GE Registry (32-bit): $(if ($configurations.GERegistryInfo.Registry32Bit) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
Write-Host " GE Registry (64-bit): $(if ($configurations.GERegistryInfo.Registry64Bit) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
Write-Host " DualPath Enabled: $(if ($configurations.GERegistryInfo.DualPathEnabled -eq $null) { 'Not Found' } elseif ($configurations.GERegistryInfo.DualPathEnabled) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
return $configurations
}
function Get-InstalledApplications {
<#
.SYNOPSIS
Detects UDC and CLM applications and their active status on shopfloor PCs
.DESCRIPTION
Checks for UDC (UDC.exe) and CLM (ppdcs.exe) processes and returns data
compatible with your existing installedapps table structure.
#>
Write-Host " Scanning for UDC and CLM applications..." -ForegroundColor Yellow
$detectedApps = @()
try {
# Define the two applications we're looking for
$appsToCheck = @{
'UDC' = @{
AppID = 2 # Universal Data Collector
ProcessName = 'UDC' # UDC.exe shows as 'UDC' in Get-Process
Description = 'Universal Data Collector'
}
'CLM' = @{
AppID = 4 # Legacy UDC (CLM)
ProcessName = 'ppdcs' # ppdcs.exe shows as 'ppdcs' in Get-Process
Description = 'Legacy UDC (Cell Level Manager)'
}
}
foreach ($appName in $appsToCheck.Keys) {
$app = $appsToCheck[$appName]
Write-Host " Checking for $appName (AppID: $($app.AppID))..." -ForegroundColor Gray
# Check if the process is running
$isActive = $false
$processInfo = $null
try {
$processes = Get-Process -Name $app.ProcessName -ErrorAction SilentlyContinue
if ($processes) {
$isActive = $true
$processInfo = $processes[0] # Take first instance if multiple
Write-Host " [ACTIVE] Found running process: $($app.ProcessName).exe (PID: $($processInfo.Id))" -ForegroundColor Green
} else {
Write-Host " [NOT ACTIVE] Process $($app.ProcessName).exe not running" -ForegroundColor Gray
}
} catch {
Write-Host " [ERROR] Failed to check process $($app.ProcessName): $($_.Exception.Message)" -ForegroundColor Yellow
}
# Always return app info (both active and inactive)
$detectedApps += @{
AppID = $app.AppID
AppName = $appName
Description = $app.Description
ProcessName = $app.ProcessName
IsActive = $isActive
ProcessID = if ($processInfo) { $processInfo.Id } else { $null }
ProcessStartTime = if ($processInfo) { $processInfo.StartTime } else { $null }
}
}
# Business rule validation: Only one should be active
$activeApps = $detectedApps | Where-Object { $_.IsActive -eq $true }
if ($activeApps.Count -gt 1) {
Write-Host " [WARNING] Multiple applications active simultaneously:" -ForegroundColor Red
foreach ($activeApp in $activeApps) {
Write-Host " - $($activeApp.AppName) (PID: $($activeApp.ProcessID))" -ForegroundColor Red
}
} elseif ($activeApps.Count -eq 1) {
$activeApp = $activeApps[0]
Write-Host " [OK] Single active application: $($activeApp.AppName) (PID: $($activeApp.ProcessID))" -ForegroundColor Green
} else {
Write-Host " [INFO] No UDC or CLM applications currently running" -ForegroundColor Gray
}
return $detectedApps
} catch {
Write-Host " [ERROR] Failed to scan for applications: $($_.Exception.Message)" -ForegroundColor Red
return @()
}
}

View File

@@ -0,0 +1,484 @@
#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
}

File diff suppressed because it is too large Load Diff