Fix search routing for PCs and WinRM remote update script

- search.asp: Route to displaypc.asp for PCs (machinetypeid 33-43 or
  machinetypeid 1 with hostname), displaymachine.asp for equipment
- search.asp: Add hostname search capability for PCs
- Update-ShopfloorPCs-Remote.ps1: Fix hashtable conversion bug that caused
  empty API errors - pass $result directly instead of PSObject.Properties

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-12-04 17:18:45 -05:00
parent 5413b20bba
commit b0c60ebbd5
2 changed files with 852 additions and 19 deletions

View File

@@ -0,0 +1,784 @@
<#
.SYNOPSIS
Remotely collects PC information from shopfloor PCs via WinRM and updates ShopDB.
.DESCRIPTION
This script uses WinRM (Invoke-Command) to remotely execute commands on shopfloor PCs,
collect system information, and POST it to the ShopDB API.
.PARAMETER ComputerName
Single computer name or array of computer names to update.
.PARAMETER All
Query ShopDB for all shopfloor PCs and update them.
.PARAMETER Credential
PSCredential object for authentication. If not provided, will prompt or use current user.
.PARAMETER ApiUrl
URL to the ShopDB API. Defaults to http://192.168.122.151:8080/api.asp
.EXAMPLE
# Update a single PC
.\Update-ShopfloorPCs-Remote.ps1 -ComputerName "SHOPFLOOR-PC01"
.EXAMPLE
# Update multiple PCs
.\Update-ShopfloorPCs-Remote.ps1 -ComputerName "PC01","PC02","PC03"
.EXAMPLE
# Update all shopfloor PCs from ShopDB
.\Update-ShopfloorPCs-Remote.ps1 -All
.EXAMPLE
# With specific credentials
$cred = Get-Credential
.\Update-ShopfloorPCs-Remote.ps1 -ComputerName "SHOPFLOOR-PC01" -Credential $cred
.NOTES
Requires:
- WinRM enabled on target PCs (Enable-PSRemoting)
- Admin credentials on target PCs
- Network access to target PCs on port 5985 (HTTP) or 5986 (HTTPS)
#>
[CmdletBinding(DefaultParameterSetName='ByName')]
param(
[Parameter(ParameterSetName='ByName', Position=0)]
[string[]]$ComputerName,
[Parameter(ParameterSetName='All')]
[switch]$All,
[Parameter(ParameterSetName='SetupTrust')]
[switch]$SetupTrustedHosts,
[Parameter()]
[PSCredential]$Credential,
[Parameter()]
[string]$ApiUrl = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp",
[Parameter()]
[string]$DnsSuffix = "logon.ds.ge.com",
[Parameter()]
[switch]$SkipDnsLookup,
[Parameter()]
[switch]$UseSSL,
[Parameter()]
[int]$ThrottleLimit = 10,
[Parameter()]
[switch]$WhatIf
)
#region 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" }
default { "White" }
}
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
}
function Get-ShopfloorPCsFromApi {
<#
.SYNOPSIS
Queries ShopDB API to get list of shopfloor PCs with details.
#>
param([string]$ApiUrl)
try {
Write-Log "Querying API: $ApiUrl`?action=getShopfloorPCs" -Level "INFO"
$response = Invoke-RestMethod -Uri "$ApiUrl`?action=getShopfloorPCs" -Method Get -ErrorAction Stop
if ($response.success -and $response.data) {
Write-Log "API returned $($response.count) shopfloor PCs" -Level "SUCCESS"
# Return full PC objects, not just hostnames
return $response.data
} else {
Write-Log "No shopfloor PCs returned from API" -Level "WARNING"
return @()
}
} catch {
Write-Log "Failed to query API for shopfloor PCs: $_" -Level "ERROR"
return @()
}
}
function Get-PCConnectionInfo {
<#
.SYNOPSIS
Builds FQDN and resolves IP for a PC hostname.
#>
param(
[Parameter(Mandatory)]
[string]$Hostname,
[Parameter()]
[string]$DnsSuffix = "logon.ds.ge.com",
[Parameter()]
[switch]$SkipDnsLookup
)
$result = @{
Hostname = $Hostname
FQDN = $null
IPAddress = $null
Reachable = $false
Error = $null
}
# Build FQDN - if hostname already contains dots, assume it's already an FQDN
if ($Hostname -like "*.*") {
$result.FQDN = $Hostname
} else {
$result.FQDN = "$Hostname.$DnsSuffix"
}
if (-not $SkipDnsLookup) {
try {
# Resolve DNS to get current IP
$dnsResult = Resolve-DnsName -Name $result.FQDN -Type A -ErrorAction Stop
$result.IPAddress = ($dnsResult | Where-Object { $_.Type -eq 'A' } | Select-Object -First 1).IPAddress
if ($result.IPAddress) {
# Quick connectivity test (WinRM port 5985)
$tcpTest = Test-NetConnection -ComputerName $result.FQDN -Port 5985 -WarningAction SilentlyContinue
$result.Reachable = $tcpTest.TcpTestSucceeded
}
} catch {
$result.Error = "DNS lookup failed: $($_.Exception.Message)"
}
} else {
# Skip DNS lookup, just use FQDN directly
$result.Reachable = $true # Assume reachable, let WinRM fail if not
}
return $result
}
function Test-WinRMConnectivity {
<#
.SYNOPSIS
Tests WinRM connectivity to a remote PC.
#>
param(
[Parameter(Mandatory)]
[string]$ComputerName,
[Parameter()]
[PSCredential]$Credential
)
$testParams = @{
ComputerName = $ComputerName
ErrorAction = 'Stop'
}
if ($Credential) {
$testParams.Credential = $Credential
}
try {
$result = Test-WSMan @testParams
return @{ Success = $true; Error = $null }
} catch {
return @{ Success = $false; Error = $_.Exception.Message }
}
}
function Add-TrustedHosts {
<#
.SYNOPSIS
Adds computers to WinRM TrustedHosts list.
#>
param(
[Parameter()]
[string[]]$ComputerNames,
[Parameter()]
[string]$DnsSuffix = "logon.ds.ge.com",
[Parameter()]
[switch]$TrustAllInDomain
)
# Check if running as admin
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Log "ERROR: Must run as Administrator to modify TrustedHosts" -Level "ERROR"
return $false
}
try {
# Get current trusted hosts
$currentHosts = (Get-Item WSMan:\localhost\Client\TrustedHosts -ErrorAction SilentlyContinue).Value
Write-Log "Current TrustedHosts: $(if ($currentHosts) { $currentHosts } else { '(empty)' })" -Level "INFO"
if ($TrustAllInDomain) {
# Trust all hosts in the domain (wildcard)
$newValue = "*.$DnsSuffix"
Write-Log "Adding wildcard trust for: $newValue" -Level "INFO"
} else {
# Build list of FQDNs to add
$fqdnsToAdd = @()
foreach ($computer in $ComputerNames) {
if ($computer -like "*.*") {
$fqdnsToAdd += $computer
} else {
$fqdnsToAdd += "$computer.$DnsSuffix"
}
}
# Merge with existing
$existingList = if ($currentHosts) { $currentHosts -split ',' } else { @() }
$mergedList = ($existingList + $fqdnsToAdd) | Select-Object -Unique
$newValue = $mergedList -join ','
Write-Log "Adding to TrustedHosts: $($fqdnsToAdd -join ', ')" -Level "INFO"
}
# Set the new value
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $newValue -Force
Write-Log "TrustedHosts updated successfully" -Level "SUCCESS"
# Show new value
$updatedHosts = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value
Write-Log "New TrustedHosts: $updatedHosts" -Level "INFO"
return $true
} catch {
Write-Log "Failed to update TrustedHosts: $_" -Level "ERROR"
return $false
}
}
function Show-TrustedHostsHelp {
<#
.SYNOPSIS
Shows help for setting up TrustedHosts.
#>
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " WinRM TrustedHosts Setup Guide" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Since you're not on the same domain as the shopfloor PCs," -ForegroundColor White
Write-Host "you need to add them to your TrustedHosts list." -ForegroundColor White
Write-Host ""
Write-Host "OPTION 1: Trust all PCs in the domain (Recommended)" -ForegroundColor Yellow
Write-Host " Run as Administrator:" -ForegroundColor Gray
Write-Host ' Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*.logon.ds.ge.com" -Force' -ForegroundColor Green
Write-Host ""
Write-Host "OPTION 2: Trust specific PCs" -ForegroundColor Yellow
Write-Host " Run as Administrator:" -ForegroundColor Gray
Write-Host ' Set-Item WSMan:\localhost\Client\TrustedHosts -Value "PC01.logon.ds.ge.com,PC02.logon.ds.ge.com" -Force' -ForegroundColor Green
Write-Host ""
Write-Host "OPTION 3: Use this script to set it up" -ForegroundColor Yellow
Write-Host " # Trust all in domain:" -ForegroundColor Gray
Write-Host " .\Update-ShopfloorPCs-Remote.ps1 -SetupTrustedHosts" -ForegroundColor Green
Write-Host ""
Write-Host " # Trust specific PCs:" -ForegroundColor Gray
Write-Host ' .\Update-ShopfloorPCs-Remote.ps1 -SetupTrustedHosts -ComputerName "PC01","PC02"' -ForegroundColor Green
Write-Host ""
Write-Host "VIEW CURRENT SETTINGS:" -ForegroundColor Yellow
Write-Host ' Get-Item WSMan:\localhost\Client\TrustedHosts' -ForegroundColor Green
Write-Host ""
Write-Host "CLEAR TRUSTEDHOSTS:" -ForegroundColor Yellow
Write-Host ' Set-Item WSMan:\localhost\Client\TrustedHosts -Value "" -Force' -ForegroundColor Green
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
}
function Get-RemotePCInfo {
<#
.SYNOPSIS
Script block to execute on remote PC to collect system information.
#>
# This scriptblock runs on the REMOTE computer
$scriptBlock = {
$result = @{
Success = $false
Hostname = $env:COMPUTERNAME
Error = $null
}
try {
# Get basic system info
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
$bios = Get-CimInstance -ClassName Win32_BIOS
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$result.SerialNumber = $bios.SerialNumber
$result.Manufacturer = $computerSystem.Manufacturer
$result.Model = $computerSystem.Model
$result.LoggedInUser = $computerSystem.UserName
$result.OSVersion = $os.Caption
# Get network interfaces
$networkAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration |
Where-Object { $_.IPEnabled -eq $true }
$networkInterfaces = @()
foreach ($adapter in $networkAdapters) {
if ($adapter.IPAddress) {
foreach ($i in 0..($adapter.IPAddress.Count - 1)) {
$ip = $adapter.IPAddress[$i]
# Only include IPv4 addresses
if ($ip -match '^\d+\.\d+\.\d+\.\d+$') {
$networkInterfaces += @{
IPAddress = $ip
MACAddress = $adapter.MACAddress
SubnetMask = if ($adapter.IPSubnet) { $adapter.IPSubnet[$i] } else { "" }
DefaultGateway = if ($adapter.DefaultIPGateway) { $adapter.DefaultIPGateway[0] } else { "" }
InterfaceName = $adapter.Description
IsMachineNetwork = ($ip -like "192.168.*" -or $ip -like "10.0.*")
}
}
}
}
}
$result.NetworkInterfaces = $networkInterfaces
# Get DNC configuration if it exists
$dncConfig = @{}
$dncIniPath = "C:\DNC\DNC.ini"
if (Test-Path $dncIniPath) {
$iniContent = Get-Content $dncIniPath -Raw
# Parse INI file for common DNC settings
if ($iniContent -match 'Site\s*=\s*(.+)') { $dncConfig.Site = $matches[1].Trim() }
if ($iniContent -match 'CNC\s*=\s*(.+)') { $dncConfig.CNC = $matches[1].Trim() }
if ($iniContent -match 'NCIF\s*=\s*(.+)') { $dncConfig.NCIF = $matches[1].Trim() }
if ($iniContent -match 'MachineNumber\s*=\s*(.+)') { $dncConfig.MachineNumber = $matches[1].Trim() }
if ($iniContent -match 'FTPHost\s*=\s*(.+)') { $dncConfig.FTPHostPrimary = $matches[1].Trim() }
}
$result.DNCConfig = $dncConfig
# Get machine number from registry or DNC config
$machineNo = $null
# Try registry first
$regPath = "HKLM:\SOFTWARE\GE\ShopFloor"
if (Test-Path $regPath) {
$machineNo = (Get-ItemProperty -Path $regPath -Name "MachineNumber" -ErrorAction SilentlyContinue).MachineNumber
}
# Fall back to DNC config
if (-not $machineNo -and $dncConfig.MachineNumber) {
$machineNo = $dncConfig.MachineNumber
}
$result.MachineNo = $machineNo
# Get serial port configuration
$comPorts = @()
$serialPorts = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue
foreach ($port in $serialPorts) {
$comPorts += @{
PortName = $port.DeviceID
Description = $port.Description
}
}
$result.SerialPorts = $comPorts
# Check for VNC installation
$hasVnc = $false
$regPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
foreach ($path in $regPaths) {
if (Test-Path $path) {
$vncApps = Get-ItemProperty $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like "*VNC Server*" -or $_.DisplayName -like "*VNC Connect*" -or $_.DisplayName -like "*RealVNC*" }
if ($vncApps) {
$hasVnc = $true
break
}
}
}
if (-not $hasVnc) {
$vncService = Get-Service -Name "vncserver*" -ErrorAction SilentlyContinue
if ($vncService) { $hasVnc = $true }
}
$result.HasVnc = $hasVnc
# Check for PC-DMIS installation (CMM software)
$hasPcDmis = $false
foreach ($path in $regPaths) {
if (Test-Path $path) {
$pcDmisApps = Get-ItemProperty $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like "*PC-DMIS*" -or $_.DisplayName -like "*PCDMIS*" }
if ($pcDmisApps) {
$hasPcDmis = $true
break
}
}
}
# Also check common PC-DMIS installation paths
if (-not $hasPcDmis) {
$pcDmisPaths = @(
"C:\Program Files\Hexagon\PC-DMIS*",
"C:\Program Files (x86)\Hexagon\PC-DMIS*",
"C:\Program Files\WAI\PC-DMIS*",
"C:\Program Files (x86)\WAI\PC-DMIS*"
)
foreach ($dmisPath in $pcDmisPaths) {
if (Test-Path $dmisPath) {
$hasPcDmis = $true
break
}
}
}
$result.HasPcDmis = $hasPcDmis
$result.Success = $true
} catch {
$result.Error = $_.Exception.Message
}
return $result
}
return $scriptBlock
}
function Send-PCDataToApi {
<#
.SYNOPSIS
Sends collected PC data to the ShopDB API.
#>
param(
[Parameter(Mandatory)]
[hashtable]$PCData,
[Parameter(Mandatory)]
[string]$ApiUrl
)
try {
# Determine PC type based on machine number
$pcType = if ($PCData.MachineNo) { "Shopfloor" } else { "Standard" }
# Build the POST body
$postData = @{
action = 'updateCompleteAsset'
hostname = $PCData.Hostname
serialNumber = $PCData.SerialNumber
manufacturer = $PCData.Manufacturer
model = $PCData.Model
pcType = $pcType
loggedInUser = $PCData.LoggedInUser
osVersion = $PCData.OSVersion
}
# Add machine number if available
if ($PCData.MachineNo) {
$postData.machineNo = $PCData.MachineNo
}
# Add VNC status
if ($PCData.HasVnc) {
$postData.hasVnc = "1"
} else {
$postData.hasVnc = "0"
}
# Add network interfaces as JSON
if ($PCData.NetworkInterfaces -and $PCData.NetworkInterfaces.Count -gt 0) {
$postData.networkInterfaces = ($PCData.NetworkInterfaces | ConvertTo-Json -Compress)
}
# Add DNC config if available
if ($PCData.DNCConfig -and $PCData.DNCConfig.Keys.Count -gt 0) {
$postData.dncConfig = ($PCData.DNCConfig | ConvertTo-Json -Compress)
}
# Send to API
$response = Invoke-RestMethod -Uri $ApiUrl -Method Post -Body $postData -ErrorAction Stop
return @{
Success = $response.success
Message = $response.message
MachineId = $response.machineid
}
} catch {
return @{
Success = $false
Message = $_.Exception.Message
MachineId = $null
}
}
}
#endregion Functions
#region Main
# Handle TrustedHosts setup mode
if ($SetupTrustedHosts) {
Write-Log "=== TrustedHosts Setup Mode ===" -Level "INFO"
if ($ComputerName -and $ComputerName.Count -gt 0) {
# Trust specific computers
$result = Add-TrustedHosts -ComputerNames $ComputerName -DnsSuffix $DnsSuffix
} else {
# Trust all in domain (wildcard)
$result = Add-TrustedHosts -DnsSuffix $DnsSuffix -TrustAllInDomain
}
if (-not $result) {
Show-TrustedHostsHelp
}
exit 0
}
Write-Log "=== ShopDB Remote PC Update Script ===" -Level "INFO"
Write-Log "API URL: $ApiUrl" -Level "INFO"
Write-Log "DNS Suffix: $DnsSuffix" -Level "INFO"
# Prompt for credentials if not provided
if (-not $Credential) {
Write-Log "No credentials provided. Prompting for credentials..." -Level "INFO"
$Credential = Get-Credential -Message "Enter credentials for remote PCs"
if (-not $Credential) {
Write-Log "Credentials required. Exiting." -Level "ERROR"
exit 1
}
}
# Show credential info (username only, not password)
Write-Log "Using credentials: $($Credential.UserName)" -Level "INFO"
# Determine list of computers to process
$computers = @()
if ($All) {
Write-Log "Querying ShopDB for all shopfloor PCs..." -Level "INFO"
$shopfloorPCs = Get-ShopfloorPCsFromApi -ApiUrl $ApiUrl
if ($shopfloorPCs.Count -eq 0) {
Write-Log "No shopfloor PCs found in ShopDB. Exiting." -Level "WARNING"
exit 0
}
# Display summary table
Write-Host ""
Write-Host " Shopfloor PCs from ShopDB:" -ForegroundColor Cyan
Write-Host " $("-" * 90)" -ForegroundColor Gray
Write-Host (" {0,-20} {1,-15} {2,-15} {3,-20} {4,-15}" -f "Hostname", "Machine #", "IP Address", "Last Updated", "Logged In") -ForegroundColor Cyan
Write-Host " $("-" * 90)" -ForegroundColor Gray
foreach ($pc in $shopfloorPCs) {
$lastUpdated = if ($pc.lastupdated) { $pc.lastupdated } else { "Never" }
$loggedIn = if ($pc.loggedinuser) { $pc.loggedinuser } else { "-" }
$machineNo = if ($pc.machinenumber) { $pc.machinenumber } else { "-" }
$ipAddr = if ($pc.ipaddress) { $pc.ipaddress } else { "-" }
Write-Host (" {0,-20} {1,-15} {2,-15} {3,-20} {4,-15}" -f $pc.hostname, $machineNo, $ipAddr, $lastUpdated, $loggedIn)
}
Write-Host " $("-" * 90)" -ForegroundColor Gray
Write-Host ""
# Extract hostnames for processing
$computers = $shopfloorPCs | ForEach-Object { $_.hostname } | Where-Object { $_ -ne "" -and $_ -ne $null }
Write-Log "Found $($computers.Count) shopfloor PCs with hostnames" -Level "INFO"
} elseif ($ComputerName) {
$computers = $ComputerName
} else {
Write-Log "No computers specified. Use -ComputerName or -All parameter." -Level "ERROR"
Write-Host ""
Write-Host "Usage Examples:" -ForegroundColor Yellow
Write-Host " # Update all shopfloor PCs from database:" -ForegroundColor Gray
Write-Host " .\Update-ShopfloorPCs-Remote.ps1 -All -Credential `$cred" -ForegroundColor Green
Write-Host ""
Write-Host " # Update specific PC:" -ForegroundColor Gray
Write-Host " .\Update-ShopfloorPCs-Remote.ps1 -ComputerName 'PC01' -Credential `$cred" -ForegroundColor Green
Write-Host ""
Write-Host " # Setup TrustedHosts first (run as admin):" -ForegroundColor Gray
Write-Host " .\Update-ShopfloorPCs-Remote.ps1 -SetupTrustedHosts" -ForegroundColor Green
Write-Host ""
exit 1
}
Write-Log "Processing $($computers.Count) computer(s)..." -Level "INFO"
# Build FQDNs directly - skip slow DNS/connectivity checks
# Let WinRM handle connection failures (much faster)
Write-Log "Building FQDN list..." -Level "INFO"
$targetComputers = @()
foreach ($computer in $computers) {
# Build FQDN - if hostname already contains dots, assume it's already an FQDN
$fqdn = if ($computer -like "*.*") { $computer } else { "$computer.$DnsSuffix" }
$targetComputers += @{
Hostname = $computer
FQDN = $fqdn
IPAddress = $null
}
}
if ($targetComputers.Count -eq 0) {
Write-Log "No computers to process. Exiting." -Level "ERROR"
exit 1
}
Write-Log "Will attempt connection to $($targetComputers.Count) PC(s)" -Level "INFO"
$skippedComputers = @()
if ($WhatIf) {
Write-Log "WhatIf mode - no changes will be made" -Level "WARNING"
Write-Log "Would process: $($targetComputers.FQDN -join ', ')" -Level "INFO"
exit 0
}
# Build session options - use FQDNs for WinRM connection
$fqdnList = $targetComputers | ForEach-Object { $_.FQDN }
# Create session options with short timeout (15 seconds per PC)
$sessionOption = New-PSSessionOption -OpenTimeout 15000 -OperationTimeout 30000 -NoMachineProfile
$sessionParams = @{
ComputerName = $fqdnList
ScriptBlock = (Get-RemotePCInfo)
SessionOption = $sessionOption
Authentication = 'Negotiate'
ErrorAction = 'SilentlyContinue'
ErrorVariable = 'remoteErrors'
}
if ($Credential) {
$sessionParams.Credential = $Credential
Write-Log "Credential added to session params: $($Credential.UserName)" -Level "INFO"
} else {
Write-Log "WARNING: No credential in session params!" -Level "WARNING"
}
if ($ThrottleLimit -and $PSVersionTable.PSVersion.Major -ge 7) {
$sessionParams.ThrottleLimit = $ThrottleLimit
}
# Execute remote commands (runs in parallel by default)
Write-Log "Connecting to remote PCs via WinRM (timeout: 15s per PC)..." -Level "INFO"
$results = Invoke-Command @sessionParams
# Process results
$successCount = 0
$failCount = 0
$successPCs = @()
$failedPCs = @()
Write-Host ""
Write-Log "Processing WinRM results..." -Level "INFO"
foreach ($result in $results) {
if ($result.Success) {
Write-Log "[OK] $($result.Hostname)" -Level "SUCCESS"
Write-Log " Serial: $($result.SerialNumber) | Model: $($result.Model) | OS: $($result.OSVersion)" -Level "INFO"
if ($result.NetworkInterfaces -and $result.NetworkInterfaces.Count -gt 0) {
$ips = ($result.NetworkInterfaces | ForEach-Object { $_.IPAddress }) -join ", "
Write-Log " IPs: $ips" -Level "INFO"
}
if ($result.MachineNo) {
Write-Log " Machine #: $($result.MachineNo)" -Level "INFO"
}
# Pass the result hashtable directly to the API function
# Note: $result from Invoke-Command is already a hashtable,
# PSObject.Properties gives metadata (Keys, Values, Count) not the actual data
# Send to API
Write-Log " Sending to API..." -Level "INFO"
$apiResult = Send-PCDataToApi -PCData $result -ApiUrl $ApiUrl
if ($apiResult.Success) {
Write-Log " -> Updated in ShopDB (MachineID: $($apiResult.MachineId))" -Level "SUCCESS"
$successCount++
$successPCs += @{
Hostname = $result.Hostname
MachineId = $apiResult.MachineId
Serial = $result.SerialNumber
}
} else {
Write-Log " -> API Error: $($apiResult.Message)" -Level "ERROR"
$failCount++
$failedPCs += @{
Hostname = $result.Hostname
Error = "API: $($apiResult.Message)"
}
}
} else {
Write-Log "[FAIL] $($result.Hostname): $($result.Error)" -Level "ERROR"
$failCount++
$failedPCs += @{
Hostname = $result.Hostname
Error = $result.Error
}
}
Write-Host ""
}
# Report any connection errors
foreach ($err in $remoteErrors) {
$targetPC = if ($err.TargetObject) { $err.TargetObject } else { "Unknown" }
Write-Log "[FAIL] ${targetPC}: $($err.Exception.Message)" -Level "ERROR"
$failCount++
$failedPCs += @{
Hostname = $targetPC
Error = $err.Exception.Message
}
}
# Final Summary
Write-Host ""
Write-Host "=" * 70 -ForegroundColor Cyan
Write-Host " SUMMARY" -ForegroundColor Cyan
Write-Host "=" * 70 -ForegroundColor Cyan
Write-Host ""
Write-Host " Total Processed: $($successCount + $failCount)" -ForegroundColor White
Write-Host " Successful: $successCount" -ForegroundColor Green
Write-Host " Failed: $failCount" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "White" })
Write-Host " Skipped (DNS): $($skippedComputers.Count)" -ForegroundColor $(if ($skippedComputers.Count -gt 0) { "Yellow" } else { "White" })
Write-Host ""
if ($successPCs.Count -gt 0) {
Write-Host " Successfully Updated:" -ForegroundColor Green
foreach ($pc in $successPCs) {
Write-Host " - $($pc.Hostname) (ID: $($pc.MachineId))" -ForegroundColor Gray
}
Write-Host ""
}
if ($failedPCs.Count -gt 0) {
Write-Host " Failed:" -ForegroundColor Red
foreach ($pc in $failedPCs) {
Write-Host " - $($pc.Hostname): $($pc.Error)" -ForegroundColor Gray
}
Write-Host ""
}
if ($skippedComputers.Count -gt 0) {
Write-Host " Skipped (DNS/Connectivity):" -ForegroundColor Yellow
foreach ($pc in $skippedComputers) {
Write-Host " - $pc" -ForegroundColor Gray
}
Write-Host ""
}
Write-Host "=" * 70 -ForegroundColor Cyan
#endregion Main

View File

@@ -63,17 +63,33 @@
Call HandleValidationError("default.asp", "INVALID_INPUT")
End If
' ------------------------------- Search For Machine Number ----------------------------------------------------------
' ------------------------------- Search For Machine Number or Hostname ----------------------------------------------------------
strSQL = "SELECT machineid FROM machines WHERE (machinenumber = ? OR alias LIKE ?) AND machines.isactive = 1"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(search, "%" & search & "%"))
' Also search by hostname for PCs, and get machinetypeid from models table to determine PC vs Equipment
strSQL = "SELECT m.machineid, m.hostname, mo.machinetypeid FROM machines m " & _
"LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid " & _
"WHERE (m.machinenumber = ? OR m.alias LIKE ? OR m.hostname = ?) AND m.isactive = 1"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(search, "%" & search & "%", search))
If Not rs.EOF Then
machineid = rs("machineid")
Dim searchMachTypeId, searchIsPC, searchRedirectPage, searchHostname
searchMachTypeId = 0
If Not IsNull(rs("machinetypeid")) Then searchMachTypeId = CLng(rs("machinetypeid"))
searchHostname = rs("hostname") & ""
' PC if machinetypeid 33-43, OR if machinetypeid is 1 (default) and has a hostname
searchIsPC = (searchMachTypeId >= 33 And searchMachTypeId <= 43) Or (searchMachTypeId = 1 And searchHostname <> "")
rs.Close
Set rs = Nothing
Call CleanupResources()
' Route to appropriate detail page based on PC vs Equipment
If searchIsPC Then
Response.Redirect "./displaypc.asp?machineid=" & machineid
Else
Response.Redirect "./displaymachine.asp?machineid=" & machineid
End If
Response.End
End If
@@ -477,30 +493,46 @@ rsNotif.Close
Set rsNotif = Nothing
' Now get Machines (by machine number, alias, notes, machine type, or vendor)
' NOTE: machinetypeid is now sourced from models table (models.machinetypeid) not machines table
' Also search by hostname for PCs
Dim rsMachines
strSQL = "SELECT m.machineid, m.machinenumber, m.alias, mt.machinetype " & _
strSQL = "SELECT m.machineid, m.machinenumber, m.alias, m.hostname, mo.machinetypeid, mt.machinetype " & _
"FROM machines m " & _
"INNER JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid " & _
"LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid " & _
"LEFT JOIN machinetypes mt ON mo.machinetypeid = mt.machinetypeid " & _
"LEFT JOIN vendors v ON mo.vendorid = v.vendorid " & _
"WHERE (m.machinenumber LIKE ? OR m.alias LIKE ? OR m.machinenotes LIKE ? OR mt.machinetype LIKE ? OR v.vendor LIKE ?) " & _
"WHERE (m.machinenumber LIKE ? OR m.alias LIKE ? OR m.machinenotes LIKE ? OR mt.machinetype LIKE ? OR v.vendor LIKE ? OR m.hostname LIKE ?) " & _
" AND m.isactive = 1 " & _
"LIMIT 10"
Set rsMachines = ExecuteParameterizedQuery(objConn, strSQL, Array("%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%"))
Set rsMachines = ExecuteParameterizedQuery(objConn, strSQL, Array("%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%"))
Response.Write("<!-- DEBUG: Checking machines for search term -->")
Do While Not rsMachines.EOF And totalCount < 100
machineDispText = rsMachines("machinenumber")
' Determine display text - use hostname for PCs, machinenumber for equipment
' PCs have machinetypeid 33-43, OR machinetypeid 1 (default) with a hostname
Dim isPC, machTypeId, loopHostname
machTypeId = 0
If Not IsNull(rsMachines("machinetypeid")) Then machTypeId = CLng(rsMachines("machinetypeid"))
loopHostname = rsMachines("hostname") & ""
isPC = (machTypeId >= 33 And machTypeId <= 43) Or (machTypeId = 1 And loopHostname <> "")
If isPC Then
machineDispText = rsMachines("hostname") & ""
If machineDispText = "" Then machineDispText = rsMachines("machinenumber") & ""
Else
machineDispText = rsMachines("machinenumber") & ""
If Not IsNull(rsMachines("alias")) And rsMachines("alias") <> "" Then
machineDispText = machineDispText & " (" & rsMachines("alias") & ")"
End If
End If
Response.Write("<!-- DEBUG: Found machine - ID: " & rsMachines("machineid") & ", Number: " & rsMachines("machinenumber") & " -->")
Response.Write("<!-- DEBUG: Found machine - ID: " & rsMachines("machineid") & ", Number: " & rsMachines("machinenumber") & ", TypeID: " & machTypeId & ", IsPC: " & isPC & " -->")
appResults(totalCount, 0) = "machine"
appResults(totalCount, 1) = rsMachines("machineid") & "|" & machineDispText & "|" & rsMachines("machinetype")
' Include isPC flag in data: machineid|machineDisplay|machinetype|isPC
appResults(totalCount, 1) = rsMachines("machineid") & "|" & machineDispText & "|" & rsMachines("machinetype") & "|" & CStr(isPC)
' Score of 15 for machines (moderate priority)
appResults(totalCount, 2) = 15
appResults(totalCount, 3) = rsMachines("machineid")
@@ -708,10 +740,27 @@ Next
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='shareNotificationResult(" & notifId & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this notification'><i class='zmdi zmdi-share'></i></button></td>")
ElseIf resultType = "machine" Then
' Machine format: machineid|machineDisplay|machinetype
' Machine format: machineid|machineDisplay|machinetype|isPC
machineId = dataFields(0)
machineDispText = dataFields(1)
machineType = dataFields(2)
Dim machineIsPC, machineDetailPage, machineListPage, machineIcon, machineIconColor, machineLabel
machineIsPC = (UBound(dataFields) >= 3 And dataFields(3) = "True")
' Set page, icon and label based on PC vs Equipment
If machineIsPC Then
machineDetailPage = "displaypc.asp"
machineListPage = "displaypcs.asp"
machineIcon = "zmdi-desktop-windows"
machineIconColor = "text-primary"
machineLabel = "PC"
Else
machineDetailPage = "displaymachine.asp"
machineListPage = "displaymachines.asp"
machineIcon = "zmdi-reader"
machineIconColor = "text-warning"
machineLabel = "Machine"
End If
rowId = "machine-result-" & machineId
If highlightId <> "" And CStr(machineId) = CStr(highlightId) And Request.QueryString("type") = "machine" Then
@@ -725,17 +774,17 @@ Next
' Column 1: Empty for machines
Response.Write("<td>&nbsp;</td>")
' Column 2: Machine icon - link to machines page (matches sidebar)
Response.Write("<td><a href='./displaymachines.asp'><i class='zmdi zmdi-reader text-warning' style='margin-right: 5px;'></i>Machine</a></td>")
' Column 2: Machine/PC icon - link to appropriate list page (matches sidebar)
Response.Write("<td><a href='./" & machineListPage & "'><i class='zmdi " & machineIcon & " " & machineIconColor & "' style='margin-right: 5px;'></i>" & machineLabel & "</a></td>")
' Column 3: Machine number/alias links to machine detail
Response.Write("<td><a href='./displaymachine.asp?machineid=" & machineId & "'>" & Server.HTMLEncode(machineDispText) & " <span class='badge badge-info'>" & Server.HTMLEncode(machineType) & "</span></a></td>")
' Column 3: Machine number/alias or hostname links to appropriate detail page
Response.Write("<td><a href='./" & machineDetailPage & "?machineid=" & machineId & "'>" & Server.HTMLEncode(machineDispText) & " <span class='badge badge-info'>" & Server.HTMLEncode(machineType) & "</span></a></td>")
' Column 4: Relevance badge
Response.Write("<td><span class='badge " & badgeClass & "'><i class='zmdi zmdi-trending-up'></i> " & FormatNumber(relevanceScore, 1) & "</span></td>")
' Column 5: Share button
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='shareMachineResult(" & machineId & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this machine'><i class='zmdi zmdi-share'></i></button></td>")
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='shareMachineResult(" & machineId & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this " & LCase(machineLabel) & "'><i class='zmdi zmdi-share'></i></button></td>")
ElseIf resultType = "printer" Then
' Printer format: printerid|printerDisplay|modelnumber|serialnumber