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:
784
scripts/Update-ShopfloorPCs-Remote.ps1
Normal file
784
scripts/Update-ShopfloorPCs-Remote.ps1
Normal 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
|
||||
81
search.asp
81
search.asp
@@ -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> </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
|
||||
|
||||
Reference in New Issue
Block a user