# Admin not required for data collection - WinRM setup will be skipped if not admin <# .SYNOPSIS Complete PC asset collection and database update via hybrid proxy/dashboard architecture. .DESCRIPTION This script performs comprehensive PC asset management: 1. Collects system information (hostname, serial, manufacturer, user, etc.) 2. Determines PC type (Engineer/Shopfloor/Standard) 3. For shopfloor PCs: Collects machine assignment and network/comm configurations 4. Gets Dell warranty information via proxy server (bypasses network restrictions) 5. Stores all collected data in database via dashboard API .NOTES Author: System Administrator Date: 2025 Version: 1.0 Architecture: PowerShell -> Proxy (warranty) -> Dashboard API (storage) #> param( [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, # DEFAULT TO TRUE - Skip warranty lookups [Parameter(Mandatory=$false)] [switch]$TestConnections = $false ) # ============================================================================= # 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 # Import shopfloor configuration functions . "$PSScriptRoot\Get-ShopfloorConfig.ps1" # Setup logging $script:LogFile = "$PSScriptRoot\logs\Update-PC-CompleteAsset-$(Get-Date -Format 'yyyy-MM-dd').log" $script:LogDir = "$PSScriptRoot\logs" function Write-Log { param( [string]$Message, [string]$Level = "INFO", [string]$ForegroundColor = "White" ) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logMessage = "[$timestamp] [$Level] $Message" # Write to console Write-Host $Message -ForegroundColor $ForegroundColor # Write to log file try { if (-not (Test-Path $script:LogDir)) { New-Item -ItemType Directory -Path $script:LogDir -Force | Out-Null } Add-Content -Path $script:LogFile -Value $logMessage -ErrorAction SilentlyContinue } catch { # Silently continue if logging fails } } function Reset-WinRMConfiguration { <# .SYNOPSIS Resets and configures WinRM for HTTP remote management .DESCRIPTION Removes existing WinRM listeners and configures fresh HTTP listener on port 5985 #> Write-Log " Resetting WinRM configuration..." -Level "INFO" -ForegroundColor Yellow try { # First, check and fix network profile if set to Public Write-Log " Checking network profile..." -Level "INFO" -ForegroundColor Gray $profiles = Get-NetConnectionProfile foreach ($profile in $profiles) { Write-Log " Interface '$($profile.Name)': $($profile.NetworkCategory)" -Level "INFO" -ForegroundColor Gray } # Fix #1: Set machine network interfaces (192.168.x.x) to Private # These shouldn't try to detect domain - they're for machine communication Write-Log " Checking for machine network interfaces..." -Level "INFO" -ForegroundColor Gray $adapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } foreach ($adapter in $adapters) { $ipConfig = Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue if ($ipConfig.IPAddress -match '^192\.168\.') { Write-Log " Found machine network: $($adapter.Name) ($($ipConfig.IPAddress))" -Level "INFO" -ForegroundColor Cyan $profile = Get-NetConnectionProfile -InterfaceIndex $adapter.ifIndex -ErrorAction SilentlyContinue if ($profile -and $profile.NetworkCategory -ne 'Private') { Write-Log " Setting machine network '$($adapter.Name)' to Private..." -Level "INFO" -ForegroundColor Gray Set-NetConnectionProfile -InterfaceIndex $adapter.ifIndex -NetworkCategory Private -ErrorAction SilentlyContinue Write-Log " [OK] Machine network set to Private" -Level "SUCCESS" -ForegroundColor Green } } } # Fix #5: Check and repair machine account trust relationship Write-Log " Checking domain trust relationship..." -Level "INFO" -ForegroundColor Gray $secureChannel = Test-ComputerSecureChannel -ErrorAction SilentlyContinue if ($secureChannel) { Write-Log " [OK] Domain trust relationship is healthy" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log " [WARN] Domain trust may be broken, attempting repair..." -Level "WARN" -ForegroundColor Yellow try { # This requires domain admin creds or machine account to still be somewhat valid $repairResult = Test-ComputerSecureChannel -Repair -ErrorAction SilentlyContinue if ($repairResult) { Write-Log " [OK] Domain trust repaired successfully" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log " [WARN] Could not auto-repair trust - may need manual rejoin to domain" -Level "WARN" -ForegroundColor Yellow } } catch { Write-Log " [WARN] Trust repair failed: $($_.Exception.Message)" -Level "WARN" -ForegroundColor Yellow } } # Now handle remaining Public profiles on corporate network $publicProfiles = Get-NetConnectionProfile | Where-Object { $_.NetworkCategory -eq 'Public' } if ($publicProfiles) { Write-Log " Found Public network profile(s), attempting to fix..." -Level "INFO" -ForegroundColor Yellow # Restart NLA service to re-detect domain Write-Log " Restarting NLA service to detect domain..." -Level "INFO" -ForegroundColor Gray Restart-Service NlaSvc -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 5 # Re-check profiles after NLA restart $publicProfiles = Get-NetConnectionProfile | Where-Object { $_.NetworkCategory -eq 'Public' } foreach ($profile in $publicProfiles) { # Check if this is a corporate network (not 192.168.x.x) $adapter = Get-NetAdapter -InterfaceIndex $profile.InterfaceIndex -ErrorAction SilentlyContinue $ipConfig = Get-NetIPAddress -InterfaceIndex $profile.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue if ($ipConfig.IPAddress -notmatch '^192\.168\.') { Write-Log " Corporate interface '$($profile.Name)' still Public after NLA restart" -Level "WARN" -ForegroundColor Yellow } # Set to Private as fallback (can't manually set Domain) Write-Log " Setting '$($profile.Name)' to Private..." -Level "INFO" -ForegroundColor Gray Set-NetConnectionProfile -InterfaceIndex $profile.InterfaceIndex -NetworkCategory Private -ErrorAction SilentlyContinue Write-Log " [OK] '$($profile.Name)' set to Private" -Level "SUCCESS" -ForegroundColor Green } } else { Write-Log " [OK] All network profiles are Private/Domain" -Level "SUCCESS" -ForegroundColor Green } # Stop WinRM service first Write-Log " Stopping WinRM service..." -Level "INFO" -ForegroundColor Gray Stop-Service WinRM -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 Write-Log " WinRM service stopped" -Level "INFO" -ForegroundColor Gray # Delete all existing listeners Write-Log " Removing existing WinRM listeners..." -Level "INFO" -ForegroundColor Gray Remove-Item -Path WSMan:\localhost\Listener\* -Recurse -Force -ErrorAction SilentlyContinue Write-Log " Existing listeners removed" -Level "INFO" -ForegroundColor Gray # Start WinRM service Write-Log " Starting WinRM service..." -Level "INFO" -ForegroundColor Gray Start-Service WinRM -ErrorAction Stop Set-Service WinRM -StartupType Automatic Write-Log " WinRM service started and set to Automatic" -Level "INFO" -ForegroundColor Gray # Run quick config to set defaults Write-Log " Running WinRM quickconfig..." -Level "INFO" -ForegroundColor Gray $quickConfigResult = winrm quickconfig -quiet 2>&1 Write-Log " WinRM quickconfig completed" -Level "INFO" -ForegroundColor Gray # Create HTTP listener on all addresses Write-Log " Creating HTTP listener on port 5985..." -Level "INFO" -ForegroundColor Gray $existingHttp = Get-ChildItem WSMan:\localhost\Listener -ErrorAction SilentlyContinue | Where-Object { $_.Keys -contains "Transport=HTTP" } if (-not $existingHttp) { New-Item -Path WSMan:\localhost\Listener -Transport HTTP -Address * -Force | Out-Null Write-Log " HTTP listener created" -Level "INFO" -ForegroundColor Gray } else { Write-Log " HTTP listener already exists" -Level "INFO" -ForegroundColor Gray } # Configure WinRM settings Write-Log " Configuring WinRM authentication settings..." -Level "INFO" -ForegroundColor Gray Set-Item WSMan:\localhost\Service\Auth\Basic -Value $false Set-Item WSMan:\localhost\Service\Auth\Negotiate -Value $true Set-Item WSMan:\localhost\Service\Auth\Kerberos -Value $true Set-Item WSMan:\localhost\Service\Auth\CredSSP -Value $false Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $false Write-Log " Auth: Basic=false, Negotiate=true, Kerberos=true, CredSSP=false" -Level "INFO" -ForegroundColor Gray # Set MaxMemoryPerShellMB for better performance Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB -Value 1024 -ErrorAction SilentlyContinue Write-Log " MaxMemoryPerShellMB set to 1024" -Level "INFO" -ForegroundColor Gray # Enable LocalAccountTokenFilterPolicy for remote admin access Write-Log " Enabling LocalAccountTokenFilterPolicy..." -Level "INFO" -ForegroundColor Gray $regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" Set-ItemProperty -Path $regPath -Name "LocalAccountTokenFilterPolicy" -Value 1 -Type DWord -Force -ErrorAction SilentlyContinue Write-Log " LocalAccountTokenFilterPolicy enabled" -Level "INFO" -ForegroundColor Gray # Grant WinRM access to Remote Management Users and Administrators Write-Log " Configuring WinRM security descriptor..." -Level "INFO" -ForegroundColor Gray try { # Get current SDDL $sddl = (Get-Item WSMan:\localhost\Service\RootSDDL).Value Write-Log " Current SDDL: $sddl" -Level "INFO" -ForegroundColor Gray # Enable PSRemoting to set proper permissions Enable-PSRemoting -Force -SkipNetworkProfileCheck 2>&1 | Out-Null Write-Log " PSRemoting enabled" -Level "INFO" -ForegroundColor Gray } catch { Write-Log " Note: Could not update SDDL directly, using Enable-PSRemoting" -Level "INFO" -ForegroundColor Gray } # Restart WinRM to apply all changes Write-Log " Restarting WinRM service to apply changes..." -Level "INFO" -ForegroundColor Gray Restart-Service WinRM -Force Start-Sleep -Seconds 2 Write-Log " WinRM service restarted" -Level "INFO" -ForegroundColor Gray # Configure firewall rule Write-Log " Configuring firewall rule..." -Level "INFO" -ForegroundColor Gray $ruleName = "Windows Remote Management (HTTP-In)" $existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue if (-not $existingRule) { New-NetFirewallRule -DisplayName $ruleName ` -Name "WINRM-HTTP-In-TCP" ` -Profile Domain,Private ` -LocalPort 5985 ` -Protocol TCP ` -Direction Inbound ` -Action Allow ` -Enabled True | Out-Null Write-Log " Firewall rule '$ruleName' created" -Level "INFO" -ForegroundColor Gray } else { Enable-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue Write-Log " Firewall rule '$ruleName' enabled" -Level "INFO" -ForegroundColor Gray } # Verify listener is working Write-Log " Verifying WinRM listener..." -Level "INFO" -ForegroundColor Gray $listeners = winrm enumerate winrm/config/listener 2>&1 if ($listeners -match "Transport = HTTP" -and $listeners -match "Port = 5985") { Write-Log " [OK] WinRM HTTP listener configured on port 5985" -Level "SUCCESS" -ForegroundColor Green # Show listener details $portCheck = netstat -an | Select-String ":5985.*LISTENING" if ($portCheck) { Write-Log " [OK] Port 5985 is listening" -Level "SUCCESS" -ForegroundColor Green } return $true } else { Write-Log " [WARN] WinRM listener may not be configured correctly" -Level "WARN" -ForegroundColor Yellow return $false } } catch { Write-Log " [FAIL] Error configuring WinRM: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red return $false } } function Add-WinRMAdminGroup { <# .SYNOPSIS Adds the WinRM management security group to local Administrators and Remote Management Users .DESCRIPTION Adds logon\g03078610 to both local Administrators and Remote Management Users groups to enable WinRM remote management #> param( [string]$GroupToAdd = "logon\g03078610" ) Write-Log " Configuring WinRM access groups..." -Level "INFO" -ForegroundColor Yellow Write-Log " Target group: $GroupToAdd" -Level "INFO" -ForegroundColor Gray $overallSuccess = $true # Add to Administrators group try { Write-Log " Checking local Administrators group..." -Level "INFO" -ForegroundColor Gray $adminGroup = [ADSI]"WinNT://./Administrators,group" $members = @($adminGroup.Invoke("Members")) | ForEach-Object { $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) } Write-Log " Current Administrators members: $($members -join ', ')" -Level "INFO" -ForegroundColor Gray $groupName = $GroupToAdd.Split('\')[-1] if ($members -contains $groupName) { Write-Log " [OK] $GroupToAdd is already in Administrators" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log " Adding $GroupToAdd to Administrators..." -Level "INFO" -ForegroundColor Yellow $result = net localgroup Administrators $GroupToAdd /add 2>&1 if ($LASTEXITCODE -eq 0 -or $result -match "already a member") { Write-Log " [OK] Added $GroupToAdd to Administrators" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log " [FAIL] Failed to add to Administrators: $result" -Level "ERROR" -ForegroundColor Red $overallSuccess = $false } } } catch { Write-Log " [FAIL] Error with Administrators group: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red $overallSuccess = $false } # Add to Remote Management Users group try { Write-Log " Checking Remote Management Users group..." -Level "INFO" -ForegroundColor Gray $rmGroup = [ADSI]"WinNT://./Remote Management Users,group" $rmMembers = @($rmGroup.Invoke("Members")) | ForEach-Object { $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) } Write-Log " Current Remote Management Users members: $($rmMembers -join ', ')" -Level "INFO" -ForegroundColor Gray $groupName = $GroupToAdd.Split('\')[-1] if ($rmMembers -contains $groupName) { Write-Log " [OK] $GroupToAdd is already in Remote Management Users" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log " Adding $GroupToAdd to Remote Management Users..." -Level "INFO" -ForegroundColor Yellow $result = net localgroup "Remote Management Users" $GroupToAdd /add 2>&1 if ($LASTEXITCODE -eq 0 -or $result -match "already a member") { Write-Log " [OK] Added $GroupToAdd to Remote Management Users" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log " [FAIL] Failed to add to Remote Management Users: $result" -Level "ERROR" -ForegroundColor Red $overallSuccess = $false } } } catch { Write-Log " [FAIL] Error with Remote Management Users group: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red $overallSuccess = $false } return $overallSuccess } function Get-DashboardURL { param([string]$ProvidedURL) if (-not [string]::IsNullOrEmpty($ProvidedURL)) { Write-Host " Using provided URL: $ProvidedURL" -ForegroundColor Gray return $ProvidedURL } # Check environment variable $envURL = [Environment]::GetEnvironmentVariable("ASSET_DASHBOARD_URL", "User") if (-not [string]::IsNullOrEmpty($envURL)) { Write-Host " Using environment variable URL: $envURL" -ForegroundColor Gray return $envURL } # Check for config file $configPath = "$PSScriptRoot\dashboard-config.json" if (Test-Path $configPath) { try { $config = Get-Content $configPath | ConvertFrom-Json if ($config.DashboardURL) { return $config.DashboardURL } } catch { Write-Verbose "Could not read dashboard config file: $_" } } # Auto-discovery with verbose logging Write-Host " Starting dashboard URL auto-discovery..." -ForegroundColor Yellow $candidates = @( "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp", "http://192.168.122.151:8080/api.asp", "http://localhost:8080/api.asp", "http://10.48.130.197/dashboard-v2/api.php", "http://localhost/test/dashboard/api.php", "http://127.0.0.1/test/dashboard/api.php", "http://10.48.130.197/api.php", "http://localhost/api.php", "http://127.0.0.1/api.php" ) foreach ($url in $candidates) { try { Write-Host " Testing: $url" -ForegroundColor Gray $testResponse = Invoke-RestMethod -Uri "$url?action=getDashboardData" -Method Get -TimeoutSec 5 -ErrorAction Stop if ($testResponse.success) { Write-Host " [SUCCESS] Dashboard found at: $url" -ForegroundColor Green return $url } else { Write-Host " [FAIL] Dashboard responded but not successful" -ForegroundColor Yellow } } catch { Write-Host " [FAIL] Cannot reach: $($_.Exception.Message)" -ForegroundColor Red } } # If auto-discovery fails, default to the expected URL $defaultUrl = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp" Write-Host " [FALLBACK] Using default URL: $defaultUrl" -ForegroundColor Yellow return $defaultUrl } function Test-ProxyConnection { param([string]$ProxyURL) try { Write-Host "Testing proxy connection..." -ForegroundColor Yellow $uri = "$ProxyURL" + "?vendor=dell&action=test-config" $response = Invoke-RestMethod -Uri $uri -Method Get -TimeoutSec 15 if ($response.success) { Write-Host "[OK] Proxy server accessible and Dell API configured" -ForegroundColor Green return $true } else { Write-Host "[FAIL] Proxy issue: $($response.error)" -ForegroundColor Red return $false } } catch { Write-Host "[FAIL] Cannot reach proxy: $($_.Exception.Message)" -ForegroundColor Red return $false } } function Test-DashboardConnection { param([string]$DashboardURL) try { Write-Host "Testing dashboard connection..." -ForegroundColor Yellow $response = Invoke-RestMethod -Uri "$DashboardURL?action=getDashboardData" -Method Get -TimeoutSec 10 if ($response.success) { Write-Host "[OK] Dashboard API accessible" -ForegroundColor Green return $true } else { Write-Host "[FAIL] Dashboard not responding properly" -ForegroundColor Red return $false } } catch { Write-Host "[FAIL] Cannot reach dashboard: $($_.Exception.Message)" -ForegroundColor Red return $false } } function Test-AppsFolder { return Test-Path "C:\Apps" } function Test-VDriveAccess { try { $vDrive = Get-PSDrive -Name V -ErrorAction SilentlyContinue if ($vDrive) { $null = Get-ChildItem V:\ -ErrorAction Stop | Select-Object -First 1 return $true } return $false } catch { return $false } } function Test-WindowsLTSC { try { $os = Get-CimInstance -ClassName CIM_OperatingSystem $osCaption = $os.Caption if ($osCaption -match "LTSC|LTSB") { return $true } $productName = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName -ErrorAction SilentlyContinue).ProductName if ($productName -match "LTSC|LTSB") { return $true } return $false } catch { return $false } } function Get-TrackedApplications { try { $csvPath = "$PSScriptRoot\applications.csv" if (-not (Test-Path $csvPath)) { Write-Warning "Applications CSV not found at $csvPath - using fallback patterns" return @( @{ app_name = "UDC"; search_patterns = "Universal Data Collection"; enabled = $true } @{ app_name = "PPDCS"; search_patterns = "PPDCS"; enabled = $true } @{ app_name = "Oracle"; search_patterns = "Oracle.*Database"; enabled = $true } @{ app_name = "Tanium"; search_patterns = "Tanium"; enabled = $true } @{ app_name = "eDNC"; search_patterns = "eDNC"; enabled = $true } ) } $trackedApps = @() $csvContent = Import-Csv -Path $csvPath foreach ($row in $csvContent) { if ($row.enabled -eq "1") { $trackedApps += @{ app_name = $row.app_name app_id = $row.app_id search_patterns = $row.search_patterns description = $row.description } } } Write-Host " Loaded $($trackedApps.Count) enabled applications from CSV" -ForegroundColor Cyan return $trackedApps } catch { Write-Warning "Failed to read applications CSV: $($_.Exception.Message) - using fallback patterns" return @( @{ app_name = "UDC"; app_id = "2"; search_patterns = "Universal Data Collection"; enabled = $true } @{ app_name = "PPDCS"; app_id = "4"; search_patterns = "PPDCS"; enabled = $true } @{ app_name = "Oracle"; app_id = "7"; search_patterns = "Oracle.*Database"; enabled = $true } @{ app_name = "Tanium"; app_id = "30"; search_patterns = "Tanium"; enabled = $true } @{ app_name = "eDNC"; app_id = ""; search_patterns = "eDNC"; enabled = $true } ) } } function Get-GEMachineNumber { param([string]$Hostname) # Check GE Aircraft Engines registry paths for MachineNo $gePaths = @( "HKLM:\Software\GE Aircraft Engines\DNC\General", "HKLM:\Software\WOW6432Node\GE Aircraft Engines\DNC\General" ) foreach ($gePath in $gePaths) { if (Test-Path $gePath) { try { $machineNo = Get-ItemProperty -Path $gePath -Name "MachineNo" -ErrorAction Stop if ($machineNo.MachineNo) { return $machineNo.MachineNo } } catch { # Continue to next path } } } # Check if hostname indicates a specific GE machine if ($Hostname -match '[HG](\d{3})') { $machineNum = $Matches[1] return "M$machineNum" } return $null } function Get-PCType { param( [bool]$HasAppsFolder, [bool]$HasVDriveAccess ) # Check if on logon.ds.ge.com domain (Shopfloor PCs) try { $domain = (Get-WmiObject Win32_ComputerSystem).Domain Write-Host " Domain detected: $domain" -ForegroundColor Gray if ($domain -eq "logon.ds.ge.com") { Write-Host " [OK] Shopfloor domain detected" -ForegroundColor Green # Check for specific machine type applications $installedApps = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName } | Select-Object -ExpandProperty DisplayName # ================================================================ # PC Type Detection based on installed software # Priority: Dashboard > Lobby Display > CMM > Wax Trace > Keyence > EAS1000 > Genspect > Heat Treat > Shopfloor # ================================================================ # Dashboard Detection (GE Aerospace Dashboard kiosk installer) - HIGHEST PRIORITY $hasDashboard = $installedApps -match "^GE Aerospace Dashboard" if ($hasDashboard) { Write-Host " [OK] Dashboard software detected - Dashboard PC" -ForegroundColor Cyan return "Dashboard" } # Lobby Display Detection (GE Aerospace Lobby Display kiosk installer) $hasLobbyDisplay = $installedApps -match "^GE Aerospace Lobby Display" if ($hasLobbyDisplay) { Write-Host " [OK] Lobby Display software detected - Lobby Display PC" -ForegroundColor Cyan return "Lobby Display" } # CMM Detection: PC-DMIS, goCMM, DODA $hasPcDmis = $installedApps -match "PC-DMIS|PCDMIS" $hasGoCMM = $installedApps -match "^goCMM" $hasDODA = $installedApps -match "Dovetail Digital Analysis|DODA" # 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*", "C:\ProgramData\Hexagon\PC-DMIS*" ) foreach ($dmisPath in $pcDmisPaths) { if (Test-Path $dmisPath) { $hasPcDmis = $true; break } } } if ($hasPcDmis -or $hasGoCMM -or $hasDODA) { $detected = @() if ($hasPcDmis) { $detected += "PC-DMIS" } if ($hasGoCMM) { $detected += "goCMM" } if ($hasDODA) { $detected += "DODA" } Write-Host " [OK] CMM software detected ($($detected -join ', ')) - CMM PC" -ForegroundColor Cyan return "CMM" } # Wax Trace Detection: FormTracePak, FormStatusMonitor $hasFormTracePak = $installedApps -match "FormTracePak|Formtracepak|Form Trace|FormTrace" $hasFormStatusMonitor = $installedApps -match "FormStatusMonitor" # Check file path fallback for FormTracePak if (-not $hasFormTracePak) { $ftPaths = @("C:\Program Files\MitutoyoApp*", "C:\Program Files (x86)\MitutoyoApp*") foreach ($ftPath in $ftPaths) { if (Test-Path $ftPath) { $hasFormTracePak = $true; break } } } if ($hasFormTracePak -or $hasFormStatusMonitor) { $detected = @() if ($hasFormTracePak) { $detected += "FormTracePak" } if ($hasFormStatusMonitor) { $detected += "FormStatusMonitor" } Write-Host " [OK] Wax Trace software detected ($($detected -join ', ')) - Wax Trace PC" -ForegroundColor Cyan return "Wax Trace" } # Keyence Detection: VR Series, Keyence VR USB Driver $hasKeyence = $installedApps -match "VR-3000|VR-5000|VR-6000|KEYENCE VR" if ($hasKeyence) { Write-Host " [OK] Keyence VR Series detected - Keyence PC" -ForegroundColor Cyan return "Keyence" } # EAS1000 Detection: GageCal, NI Software (National Instruments) $hasGageCal = $installedApps -match "^GageCal" $hasNISoftware = $installedApps -match "^NI-|National Instruments|NI System|NI Measurement|NI LabVIEW" if ($hasGageCal -or $hasNISoftware) { $detected = @() if ($hasGageCal) { $detected += "GageCal" } if ($hasNISoftware) { $detected += "NI Software" } Write-Host " [OK] EAS1000 software detected ($($detected -join ', ')) - EAS1000 PC" -ForegroundColor Cyan return "EAS1000" } # Genspect detection (could be Keyence or EAS1000 - default to Measuring) $hasGenspect = $installedApps -match "^Genspect" if ($hasGenspect) { Write-Host " [OK] Genspect detected - Measuring Tool PC" -ForegroundColor Cyan return "Genspect" } # Heat Treat Detection: HeatTreat application $hasHeatTreat = $installedApps -match "^HeatTreat" if ($hasHeatTreat) { Write-Host " [OK] HeatTreat software detected - Heat Treat PC" -ForegroundColor Cyan return "Heat Treat" } # Inspection Detection: By machine number (0612, 0613, 0615, 8003) or software $machineNo = Get-GEMachineNumber -Hostname $env:COMPUTERNAME $isPartMarkerMachine = $false if ($machineNo) { # Check if machine number matches Inspection machines (0612, 0613, 0615, 8003) if ($machineNo -match "^0?(612|613|615|8003)$" -or $machineNo -match "^M?(612|613|615|8003)$") { $isPartMarkerMachine = $true Write-Host " [OK] Inspection machine detected (Machine #$machineNo) - Inspection PC" -ForegroundColor Cyan return "Inspection" } } # Also check for Inspection software $hasPartMarker = $installedApps -match "Part\s*Mark|PartMark|Telesis|MECCO|Pryor|Gravotech|SIC Marking" if ($hasPartMarker) { Write-Host " [OK] Inspection software detected - Inspection PC" -ForegroundColor Cyan return "Inspection" } return "Shopfloor" } } catch { Write-Host " [WARN] Could not detect domain: $($_.Exception.Message)" -ForegroundColor Yellow } if ($HasAppsFolder -and $HasVDriveAccess) { return "Engineer" } else { return "Standard" } } function Collect-SystemInfo { Write-Host "Collecting comprehensive system information..." -ForegroundColor Green $systemInfo = @{} # Basic system info $systemInfo.Hostname = $env:COMPUTERNAME try { $computerSystem = Get-CimInstance -Class CIM_ComputerSystem -ErrorAction Stop $bios = Get-CimInstance -Class CIM_BIOSElement -ErrorAction Stop $os = Get-CimInstance -Class CIM_OperatingSystem -ErrorAction Stop $systemInfo.Manufacturer = $computerSystem.Manufacturer $systemInfo.Model = $computerSystem.Model $systemInfo.SerialNumber = $bios.SerialNumber $systemInfo.ServiceTag = $bios.SerialNumber # Often same as serial for Dell $systemInfo.TotalPhysicalMemory = [Math]::Round($computerSystem.TotalPhysicalMemory / 1GB, 2) $systemInfo.DomainRole = $computerSystem.DomainRole # OS Information $systemInfo.OSVersion = $os.Caption $systemInfo.LastBootUpTime = $os.LastBootUpTime $systemInfo.CurrentTimeZone = (Get-TimeZone).Id # Logged-in user if ($computerSystem.UserName) { $systemInfo.LoggedInUser = $computerSystem.UserName.Split('\')[-1] } else { $systemInfo.LoggedInUser = "No user logged in" } } catch { Write-Warning "Failed to retrieve WMI information: $_" $systemInfo.Manufacturer = "Unknown" $systemInfo.Model = "Unknown" $systemInfo.SerialNumber = "Unknown" $systemInfo.ServiceTag = "Unknown" $systemInfo.LoggedInUser = "Unknown" $systemInfo.OSVersion = "Unknown" } # PC Type determination $hasApps = Test-AppsFolder $hasVDrive = Test-VDriveAccess $systemInfo.PCType = Get-PCType -HasAppsFolder $hasApps -HasVDriveAccess $hasVDrive # Add application detection for shopfloor PCs if ($systemInfo.PCType -eq "Shopfloor") { Write-Host " Detecting shopfloor applications (UDC/CLM)..." -ForegroundColor Yellow $systemInfo.InstalledApplications = Get-InstalledApplications } else { Write-Host " Skipping application detection (PC Type: $($systemInfo.PCType))" -ForegroundColor Gray $systemInfo.InstalledApplications = @() } # GE Machine Number $systemInfo.MachineNo = Get-GEMachineNumber -Hostname $systemInfo.Hostname # Collect installed applications for logging and database tracking Write-Host " Collecting installed applications..." -ForegroundColor Yellow try { $installedApps = @() $installedApps += Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object {$_.DisplayName} $installedApps += Get-ItemProperty HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object {$_.DisplayName} $filteredApps = $installedApps | Select-Object DisplayName, DisplayVersion | Sort-Object DisplayName -Unique Write-Host " Found $($filteredApps.Count) installed applications:" -ForegroundColor Gray foreach ($app in $filteredApps) { $version = if ($app.DisplayVersion) { " (v$($app.DisplayVersion))" } else { "" } Write-Host " - $($app.DisplayName)$version" -ForegroundColor Gray } # Prepare application data for database tracking (specific apps only) $trackedApps = @() $csvTrackedApps = Get-TrackedApplications # Track which appids we've already added to avoid duplicates $seenAppIds = @{} foreach ($app in $filteredApps) { $appName = $app.DisplayName $appVersion = $app.DisplayVersion # Check against CSV-defined applications foreach ($csvApp in $csvTrackedApps) { # Skip if no app_id defined in CSV if (-not $csvApp.app_id -or $csvApp.app_id -eq "") { continue } if ($appName -match $csvApp.search_patterns) { $appId = [int]$csvApp.app_id # Skip if we've already tracked this appid if ($seenAppIds.ContainsKey($appId)) { Write-Host " Skipping duplicate: $($csvApp.app_name) (ID:$appId) = $appName" -ForegroundColor DarkGray break } $seenAppIds[$appId] = $true $trackedApps += @{ appid = $appId # Database appid appname = $csvApp.app_name # Short name from CSV (e.g., "Tanium") version = $appVersion # Detected version displayname = $appName # Full registry name for reference } Write-Host " Matched: $($csvApp.app_name) (ID:$appId) = $appName v$appVersion" -ForegroundColor Cyan break # Found a match, no need to check other patterns } } } # Detect running processes for UDC and CLM to set isactive $udcRunning = $false $clmRunning = $false $udcProcess = Get-Process -Name "UDC" -ErrorAction SilentlyContinue if ($udcProcess) { $udcRunning = $true } $clmProcess = Get-Process -Name "PPMon" -ErrorAction SilentlyContinue if ($clmProcess) { $clmRunning = $true } # Update tracked apps with isactive status foreach ($app in $trackedApps) { if ($app.appid -eq 2) { # UDC $app.isactive = if ($udcRunning) { 1 } else { 0 } Write-Host " UDC process running: $udcRunning" -ForegroundColor $(if ($udcRunning) { "Green" } else { "Yellow" }) } elseif ($app.appid -eq 4) { # CLM $app.isactive = if ($clmRunning) { 1 } else { 0 } Write-Host " CLM (PPMon) process running: $clmRunning" -ForegroundColor $(if ($clmRunning) { "Green" } else { "Yellow" }) } else { # Other apps - default to active if installed $app.isactive = 1 } } $systemInfo.TrackedApplications = $trackedApps Write-Host " Found $($trackedApps.Count) tracked applications for database" -ForegroundColor Cyan } catch { Write-Host " [WARN] Failed to collect installed applications: $($_.Exception.Message)" -ForegroundColor Yellow $systemInfo.TrackedApplications = @() } # ================================================================ # Collect serial port configuration (for parity with remote script) # ================================================================ Write-Host " Collecting serial port configuration..." -ForegroundColor Yellow try { $comPorts = @() $serialPorts = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue foreach ($port in $serialPorts) { $comPorts += @{ PortName = $port.DeviceID Description = $port.Description } Write-Host " Found COM port: $($port.DeviceID) - $($port.Description)" -ForegroundColor Gray } $systemInfo.SerialPorts = $comPorts if ($comPorts.Count -eq 0) { Write-Host " No serial ports found" -ForegroundColor Gray } else { Write-Host " [OK] Found $($comPorts.Count) serial port(s)" -ForegroundColor Cyan } } catch { Write-Host " [WARN] Failed to collect serial ports: $($_.Exception.Message)" -ForegroundColor Yellow $systemInfo.SerialPorts = @() } # ================================================================ # Check for VNC installation (for parity with remote script) # ================================================================ Write-Host " Checking for VNC installation..." -ForegroundColor Yellow try { $hasVnc = $false $regPaths = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKCU:\SOFTWARE\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 Write-Host " [OK] VNC software detected in registry" -ForegroundColor Cyan break } } } if (-not $hasVnc) { $vncService = Get-Service -Name "vncserver*" -ErrorAction SilentlyContinue if ($vncService) { $hasVnc = $true Write-Host " [OK] VNC service detected" -ForegroundColor Cyan } } if (-not $hasVnc) { Write-Host " No VNC installation found" -ForegroundColor Gray } $systemInfo.HasVnc = $hasVnc } catch { Write-Host " [WARN] Failed to check for VNC: $($_.Exception.Message)" -ForegroundColor Yellow $systemInfo.HasVnc = $false } # ================================================================ # Collect ALL installed applications including HKU (per-user) registry # This provides complete app list for parity with remote script # ================================================================ Write-Host " Collecting complete application list (including per-user)..." -ForegroundColor Yellow try { $allApps = @() # Get from HKLM (machine-wide) $hklmPaths = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" ) foreach ($path in $hklmPaths) { if (Test-Path $path) { $apps = Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -and $_.DisplayName.Trim() -ne "" } foreach ($app in $apps) { if ($app.DisplayName -notin $allApps) { $allApps += $app.DisplayName } } } } # Get from HKCU (current user) $hkcuPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" if (Test-Path $hkcuPath) { $apps = Get-ItemProperty $hkcuPath -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -and $_.DisplayName.Trim() -ne "" } foreach ($app in $apps) { if ($app.DisplayName -notin $allApps) { $allApps += $app.DisplayName } } } # Check per-user installed apps by enumerating loaded hives in HKU $hkuKeys = Get-ChildItem "Registry::HKU" -ErrorAction SilentlyContinue foreach ($key in $hkuKeys) { $sid = $key.PSChildName # Only check real user SIDs (S-1-5-21-*), skip _Classes entries if ($sid -match "^S-1-5-21-" -and $sid -notmatch "_Classes$") { $userUninstallPath = "Registry::HKU\$sid\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" if (Test-Path $userUninstallPath -ErrorAction SilentlyContinue) { $apps = Get-ItemProperty $userUninstallPath -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -and $_.DisplayName.Trim() -ne "" } foreach ($app in $apps) { if ($app.DisplayName -notin $allApps) { $allApps += $app.DisplayName } } } } } $systemInfo.AllInstalledApps = ($allApps | Sort-Object) -join "|" $systemInfo.AllInstalledAppsCount = $allApps.Count Write-Host " [OK] Collected $($allApps.Count) total applications (including per-user)" -ForegroundColor Cyan } catch { Write-Host " [WARN] Failed to collect complete application list: $($_.Exception.Message)" -ForegroundColor Yellow $systemInfo.AllInstalledApps = "" $systemInfo.AllInstalledAppsCount = 0 } # Collect running processes (for log analysis) Write-Host " Running processes:" -ForegroundColor Yellow try { $processes = Get-Process | Select-Object -ExpandProperty Name | Sort-Object -Unique Write-Host " $($processes -join ', ')" -ForegroundColor Gray } catch { Write-Host " [WARN] Failed to collect running processes: $($_.Exception.Message)" -ForegroundColor Yellow } # Display collected info Write-Host " System Details:" -ForegroundColor Cyan Write-Host " Hostname: $($systemInfo.Hostname)" Write-Host " Manufacturer: $($systemInfo.Manufacturer)" Write-Host " Model: $($systemInfo.Model)" Write-Host " Serial: $($systemInfo.SerialNumber)" Write-Host " PC Type: $($systemInfo.PCType)" Write-Host " User: $($systemInfo.LoggedInUser)" if ($systemInfo.MachineNo) { Write-Host " Machine No: $($systemInfo.MachineNo)" } Write-Host " Memory: $($systemInfo.TotalPhysicalMemory) GB" Write-Host " OS: $($systemInfo.OSVersion)" return $systemInfo } function Collect-ShopfloorInfo { param([hashtable]$SystemInfo) if ($SystemInfo.PCType -ne "Shopfloor") { return $null } Write-Host "" Write-Host "Collecting shopfloor-specific configurations..." -ForegroundColor Yellow try { $shopfloorConfigs = Get-ShopfloorConfigurations Write-Host " Shopfloor Configuration Summary:" -ForegroundColor Cyan Write-Host " Network Interfaces: $($shopfloorConfigs.NetworkInterfaces.Count)" Write-Host " Communication Configs: $($shopfloorConfigs.CommConfigs.Count)" Write-Host " DNC Config: $(if ($shopfloorConfigs.DNCConfig) { 'Yes' } else { 'No' })" return $shopfloorConfigs } catch { Write-Warning "Failed to collect shopfloor configurations: $_" return $null } } function Get-DefaultPrinterFQDN { try { Write-Host " Collecting default printer information..." -ForegroundColor Yellow $defaultPrinter = Get-WmiObject -Query "SELECT * FROM Win32_Printer WHERE Default=$true" -ErrorAction Stop if ($defaultPrinter -and $defaultPrinter.PortName) { $portName = $defaultPrinter.PortName Write-Host " Default Printer: $($defaultPrinter.Name)" -ForegroundColor Gray Write-Host " Port Name: $portName" -ForegroundColor Gray # Check if this is a network printer (not a local/virtual printer) $localPorts = @('USB', 'LPT', 'COM', 'PORTPROMPT:', 'FILE:', 'NUL:', 'XPS', 'PDF', 'FOXIT', 'Microsoft') $isLocalPrinter = $false foreach ($localPort in $localPorts) { if ($portName -like "$localPort*") { $isLocalPrinter = $true break } } if ($isLocalPrinter) { Write-Host " [SKIP] Local/virtual printer detected (port: $portName) - not sending to database" -ForegroundColor Yellow return $null } # Strip anything after and including underscore (e.g., 10.80.92.53_2 -> 10.80.92.53) $cleanPort = $portName -replace '_.*$', '' Write-Host " [OK] Network printer detected - will send to database" -ForegroundColor Green Write-Host " Clean port: $cleanPort" -ForegroundColor Gray return $cleanPort } else { Write-Host " No default printer found or no port available" -ForegroundColor Yellow return $null } } catch { Write-Warning "Failed to get default printer information: $_" return $null } } function Send-PrinterMappingToDashboard { param( [string]$Hostname, [string]$PrinterFQDN, [string]$DashboardURL ) if ([string]::IsNullOrEmpty($PrinterFQDN)) { Write-Host " No printer FQDN to send - skipping printer mapping" -ForegroundColor Gray return $true } try { Write-Host " Sending printer mapping to dashboard..." -ForegroundColor Yellow Write-Host " Hostname: $Hostname" -ForegroundColor Gray Write-Host " Printer FQDN: $PrinterFQDN" -ForegroundColor Gray $postData = @{ action = 'updatePrinterMapping' hostname = $Hostname printerFQDN = $PrinterFQDN } $headers = @{ 'Content-Type' = 'application/x-www-form-urlencoded' } $response = Invoke-RestMethod -Uri $DashboardURL -Method Post -Body $postData -Headers $headers -TimeoutSec 30 # Debug: Show raw response structure Write-Host " DEBUG Response: $($response | ConvertTo-Json -Compress)" -ForegroundColor Magenta if ($response.success) { Write-Host " [OK] Printer mapping updated successfully!" -ForegroundColor Green Write-Host " Printer ID: $($response.data.printerId)" -ForegroundColor Gray Write-Host " Machines Updated: $($response.data.machinesUpdated)" -ForegroundColor Gray Write-Host " Match Method: $($response.data.matchMethod)" -ForegroundColor Gray return $true } else { Write-Host " [WARN] Printer mapping failed: $($response.message)" -ForegroundColor Yellow Write-Host " DEBUG Error Response: $($response | ConvertTo-Json)" -ForegroundColor Red return $false } } catch { Write-Host " [WARN] Error sending printer mapping: $($_.Exception.Message)" -ForegroundColor Yellow return $false } } function Send-InstalledAppsToDashboard { param( [string]$Hostname, [array]$TrackedApps, [string]$DashboardURL ) if (!$TrackedApps -or $TrackedApps.Count -eq 0) { Write-Host " No tracked applications to send - skipping app mapping" -ForegroundColor Gray return $true } try { Write-Host " Sending tracked applications to dashboard..." -ForegroundColor Yellow Write-Host " Hostname: $Hostname" -ForegroundColor Gray Write-Host " Tracked Apps: $($TrackedApps.Count)" -ForegroundColor Gray # Debug: Show each app with version foreach ($app in $TrackedApps) { Write-Host " -> appid=$($app.appid), appname='$($app.appname)', version='$($app.version)'" -ForegroundColor Magenta } $appsJson = $TrackedApps | ConvertTo-Json -Compress Write-Host " DEBUG JSON: $appsJson" -ForegroundColor Magenta $postData = @{ action = 'updateInstalledApps' hostname = $Hostname installedApps = $appsJson } $headers = @{ 'Content-Type' = 'application/x-www-form-urlencoded' } $response = Invoke-RestMethod -Uri $DashboardURL -Method Post -Body $postData -Headers $headers -TimeoutSec 30 if ($response.success) { Write-Host " [OK] Installed applications updated successfully!" -ForegroundColor Green Write-Host " Apps Processed: $($response.appsProcessed)" -ForegroundColor Gray Write-Host " Machine ID: $($response.machineid)" -ForegroundColor Gray if ($response.debugError) { Write-Host " DEBUG ERROR: $($response.debugError)" -ForegroundColor Red } return $true } else { Write-Host " [WARN] Application mapping failed: $($response.message)" -ForegroundColor Yellow return $false } } catch { Write-Host " [WARN] Error sending application mapping: $($_.Exception.Message)" -ForegroundColor Yellow return $false } } function Get-WarrantyFromProxy { param( [string]$ServiceTag, [string]$ProxyURL ) if ([string]::IsNullOrEmpty($ServiceTag) -or $ServiceTag -eq "Unknown") { Write-Host " No valid service tag - skipping warranty lookup" -ForegroundColor Gray return $null } try { Write-Host "Getting warranty data from proxy server..." -ForegroundColor Yellow $uri = "$ProxyURL" + "?vendor=dell&action=warranty&servicetag=$ServiceTag" $response = Invoke-RestMethod -Uri $uri -Method Get -TimeoutSec 30 if ($response.success -and $response.warranties -and $response.warranties.Count -gt 0) { $warranty = $response.warranties[0] Write-Host " [OK] Warranty data retrieved:" -ForegroundColor Green Write-Host " Status: $($warranty.warrantyStatus)" Write-Host " End Date: $($warranty.warrantyEndDate)" Write-Host " Days Remaining: $($warranty.daysRemaining)" Write-Host " Service Level: $($warranty.serviceLevel)" return $warranty } else { Write-Host " [WARN] No warranty data found for service tag: $ServiceTag" -ForegroundColor Yellow return $null } } catch { Write-Host " [WARN] Error getting warranty from proxy: $($_.Exception.Message)" -ForegroundColor Yellow return $null } } function Send-CompleteDataToDashboard { param( [hashtable]$SystemInfo, [object]$ShopfloorInfo, [object]$WarrantyData, [string]$DashboardURL ) try { Write-Host "Sending complete asset data to dashboard..." -ForegroundColor Yellow Write-Host " Dashboard URL: $DashboardURL" -ForegroundColor Gray # Prepare comprehensive data package $postData = @{ action = 'updateCompleteAsset' # Basic system information hostname = $SystemInfo.Hostname serialNumber = $SystemInfo.SerialNumber serviceTag = $SystemInfo.ServiceTag manufacturer = $SystemInfo.Manufacturer model = $SystemInfo.Model pcType = $SystemInfo.PCType loggedInUser = $SystemInfo.LoggedInUser machineNo = $SystemInfo.MachineNo osVersion = $SystemInfo.OSVersion totalPhysicalMemory = $SystemInfo.TotalPhysicalMemory domainRole = $SystemInfo.DomainRole currentTimeZone = $SystemInfo.CurrentTimeZone # Format lastBootUpTime as MySQL datetime (YYYY-MM-DD HH:MM:SS) lastBootUpTime = if ($SystemInfo.LastBootUpTime) { $SystemInfo.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss") } else { $null } } # Add warranty data if available if ($WarrantyData) { $postData.warrantyEndDate = $WarrantyData.warrantyEndDate $postData.warrantyStatus = $WarrantyData.warrantyStatus $postData.warrantyServiceLevel = $WarrantyData.serviceLevel $postData.warrantyDaysRemaining = $WarrantyData.daysRemaining } # Add shopfloor data if available if ($ShopfloorInfo) { Write-Host " ShopfloorInfo object contains DNCConfig: $(if ($ShopfloorInfo.DNCConfig) { 'YES' } else { 'NO' })" -ForegroundColor Gray Write-Host " ShopfloorInfo object contains GERegistryInfo: $(if ($ShopfloorInfo.GERegistryInfo) { 'YES' } else { 'NO' })" -ForegroundColor Gray $postData.networkInterfaces = $ShopfloorInfo.NetworkInterfaces | ConvertTo-Json -Compress $postData.commConfigs = $ShopfloorInfo.CommConfigs | ConvertTo-Json -Compress if ($ShopfloorInfo.DNCConfig) { $postData.dncConfig = $ShopfloorInfo.DNCConfig | ConvertTo-Json -Compress Write-Host " Sending DNC Config: $(($ShopfloorInfo.DNCConfig | ConvertTo-Json -Compress).Substring(0, 100))..." -ForegroundColor Cyan } else { Write-Host " No DNC Config to send (ShopfloorInfo.DNCConfig is null or empty)" -ForegroundColor Yellow } # Add GE Aircraft Engines registry information if ($ShopfloorInfo.GERegistryInfo) { $geInfo = $ShopfloorInfo.GERegistryInfo $postData.dncDualPathEnabled = if ($geInfo.DualPathEnabled -ne $null) { $geInfo.DualPathEnabled } else { $null } $postData.dncPath1Name = $geInfo.Path1Name $postData.dncPath2Name = $geInfo.Path2Name $postData.dncGeRegistry32Bit = $geInfo.Registry32Bit $postData.dncGeRegistry64Bit = $geInfo.Registry64Bit $postData.dncGeRegistryNotes = $geInfo.RegistryNotes | ConvertTo-Json -Compress Write-Host " Sending GE Registry Info:" -ForegroundColor Cyan Write-Host " 32-bit: $($geInfo.Registry32Bit), 64-bit: $($geInfo.Registry64Bit)" -ForegroundColor Cyan Write-Host " DualPath: $(if ($geInfo.DualPathEnabled -ne $null) { $geInfo.DualPathEnabled } else { 'Not Found' })" -ForegroundColor Cyan if ($geInfo.Path1Name) { Write-Host " Path1Name: $($geInfo.Path1Name)" -ForegroundColor Cyan } if ($geInfo.Path2Name) { Write-Host " Path2Name: $($geInfo.Path2Name)" -ForegroundColor Cyan } } else { Write-Host " No GE Registry Info to send" -ForegroundColor Yellow } } else { Write-Host " No ShopfloorInfo available" -ForegroundColor Yellow } # Add installed applications data for shopfloor PCs if ($SystemInfo.InstalledApplications -and $SystemInfo.InstalledApplications.Count -gt 0) { $postData.installedApplications = $SystemInfo.InstalledApplications | ConvertTo-Json -Compress Write-Host " Sending installed applications data:" -ForegroundColor Cyan $activeApps = $SystemInfo.InstalledApplications | Where-Object { $_.IsActive -eq $true } if ($activeApps.Count -gt 0) { foreach ($app in $activeApps) { Write-Host " - $($app.AppName) (AppID: $($app.AppID)) - ACTIVE (PID: $($app.ProcessID))" -ForegroundColor Green } } else { Write-Host " - No active applications detected" -ForegroundColor Gray } } else { Write-Host " No installed applications to send" -ForegroundColor Gray } # ================================================================ # Add VNC status (for parity with remote script) # ================================================================ if ($SystemInfo.HasVnc -ne $null) { $postData.hasVnc = if ($SystemInfo.HasVnc) { "1" } else { "0" } Write-Host " VNC Status: $(if ($SystemInfo.HasVnc) { 'Installed' } else { 'Not Installed' })" -ForegroundColor $(if ($SystemInfo.HasVnc) { 'Cyan' } else { 'Gray' }) } # ================================================================ # Add serial ports (for parity with remote script) # ================================================================ if ($SystemInfo.SerialPorts -and $SystemInfo.SerialPorts.Count -gt 0) { $postData.serialPorts = $SystemInfo.SerialPorts | ConvertTo-Json -Compress Write-Host " Serial Ports: $($SystemInfo.SerialPorts.Count) port(s)" -ForegroundColor Cyan } # ================================================================ # Add complete installed apps list (for parity with remote script) # ================================================================ if ($SystemInfo.AllInstalledApps) { $postData.allInstalledApps = $SystemInfo.AllInstalledApps $postData.allInstalledAppsCount = $SystemInfo.AllInstalledAppsCount Write-Host " Complete Apps List: $($SystemInfo.AllInstalledAppsCount) total applications" -ForegroundColor Cyan } # Send to dashboard API $headers = @{ 'Content-Type' = 'application/x-www-form-urlencoded' } $response = Invoke-RestMethod -Uri $DashboardURL -Method Post -Body $postData -Headers $headers -TimeoutSec 30 if ($response.success) { Write-Host " [OK] Complete asset data stored in database!" -ForegroundColor Green Write-Host " PCID: $(if($response.machineid) { $response.machineid } else { 'Unknown' })" Write-Host " Operation: $(if($response.operation) { $response.operation } else { 'Unknown' })" Write-Host " Network Interfaces: $(if($response.networkInterfacesCount -ne $null) { $response.networkInterfacesCount } else { 'Unknown' })" Write-Host " Comm Configs: $(if($response.commConfigsCount -ne $null) { $response.commConfigsCount } else { 'Unknown' })" Write-Host " DNC Config: $(if($response.dncConfigSuccess -ne $null) { $response.dncConfigSuccess } else { 'Unknown' })" Write-Host " Installed Apps: $(if($response.installedAppsCount -ne $null) { $response.installedAppsCount } else { 'Unknown' })" if ($response.relationshipCreated) { Write-Host " Machine Relationship: Created" -ForegroundColor Green } elseif ($SystemInfo.MachineNo) { Write-Host " Machine Relationship: FAILED (machineNo=$($SystemInfo.MachineNo))" -ForegroundColor Red } else { Write-Host " Machine Relationship: Skipped (no machine number)" -ForegroundColor Gray } return $true } else { Write-Host " [FAIL] Dashboard could not store data: $($response.error)" -ForegroundColor Red if ($response.data -and $response.data.debug) { Write-Host " Debug - DNC Config Received: $($response.data.debug.dncConfigReceived)" -ForegroundColor Yellow } elseif ($response.debug) { Write-Host " Debug - DNC Config Received: $($response.debug.dncConfigReceived)" -ForegroundColor Yellow } return $false } } catch { Write-Host " [FAIL] Error sending data to dashboard: $($_.Exception.Message)" -ForegroundColor Red # Try to get more details from the response if ($_.Exception -is [System.Net.WebException]) { $response = $_.Exception.Response if ($response) { $responseStream = $response.GetResponseStream() $reader = New-Object System.IO.StreamReader($responseStream) $responseBody = $reader.ReadToEnd() $reader.Close() Write-Host " Server Response:" -ForegroundColor Yellow try { $errorData = $responseBody | ConvertFrom-Json if ($errorData.details) { Write-Host " Error: $($errorData.details.message)" -ForegroundColor Red Write-Host " File: $($errorData.details.file):$($errorData.details.line)" -ForegroundColor Gray } else { Write-Host " $responseBody" -ForegroundColor Gray } } catch { Write-Host " $responseBody" -ForegroundColor Gray } } } return $false } } # Main execution try { # Log startup Write-Log "========================================" -Level "INFO" -ForegroundColor Green Write-Log "Complete PC Asset Collection & Storage" -Level "INFO" -ForegroundColor Green Write-Log "========================================" -Level "INFO" -ForegroundColor Green Write-Log "Computer: $env:COMPUTERNAME" -Level "INFO" -ForegroundColor White Write-Log "Log file: $script:LogFile" -Level "INFO" -ForegroundColor Gray # Get dashboard URL $dashboardURL = Get-DashboardURL -ProvidedURL $DashboardURL Write-Log "Dashboard: $dashboardURL" -Level "INFO" -ForegroundColor Gray Write-Log "Note: Warranty lookups disabled (handled by dashboard)" -Level "INFO" -ForegroundColor Gray Write-Log "" -Level "INFO" -ForegroundColor White # Test connections if requested if ($TestConnections) { Write-Host "=== CONNECTION TESTS ===" -ForegroundColor Cyan # Only test dashboard connection since proxy is not accessible from PCs Write-Host "Skipping proxy test (not accessible from client PCs)" -ForegroundColor Gray $dashboardOK = Test-DashboardConnection -DashboardURL $dashboardURL Write-Host "" if ($dashboardOK) { Write-Host "[OK] Dashboard connection working!" -ForegroundColor Green exit 0 } else { Write-Host "[FAIL] Dashboard connection failed" -ForegroundColor Red exit 1 } } # Step 1: Collect system information Write-Host "=== STEP 1: COLLECT SYSTEM INFO ===" -ForegroundColor Cyan $systemInfo = Collect-SystemInfo # Step 2: Collect shopfloor-specific info (if applicable) Write-Host "" Write-Host "=== STEP 2: COLLECT SHOPFLOOR INFO ===" -ForegroundColor Cyan $shopfloorInfo = Collect-ShopfloorInfo -SystemInfo $systemInfo # Step 3: Get warranty data from proxy (if not skipped and Dell system) $warrantyData = $null $isDellSystem = $systemInfo.Manufacturer -match "Dell" Write-Host "" Write-Host "=== STEP 3: WARRANTY DATA ===" -ForegroundColor Cyan Write-Host "Warranty lookups disabled - Dashboard will handle warranty updates" -ForegroundColor Yellow Write-Host "PCs cannot reach proxy server from this network" -ForegroundColor Gray # Step 4: Send all data to dashboard for storage Write-Host "" Write-Host "=== STEP 4: STORE IN DATABASE ===" -ForegroundColor Cyan $storeSuccess = Send-CompleteDataToDashboard -SystemInfo $systemInfo -ShopfloorInfo $shopfloorInfo -WarrantyData $warrantyData -DashboardURL $dashboardURL if (-not $storeSuccess) { Write-Error "Failed to store asset data in database" exit 1 } # Step 5: Collect and send default printer mapping Write-Host "" Write-Host "=== STEP 5: PRINTER MAPPING ===" -ForegroundColor Cyan $defaultPrinterFQDN = Get-DefaultPrinterFQDN $printerMappingSuccess = Send-PrinterMappingToDashboard -Hostname $systemInfo.Hostname -PrinterFQDN $defaultPrinterFQDN -DashboardURL $dashboardURL # Step 6: Send tracked applications to database Write-Host "" Write-Host "=== STEP 6: APPLICATION MAPPING ===" -ForegroundColor Cyan $appMappingSuccess = Send-InstalledAppsToDashboard -Hostname $systemInfo.Hostname -TrackedApps $systemInfo.TrackedApplications -DashboardURL $dashboardURL # Check if running as admin for Steps 7 & 8 $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") # Step 7: Reset and configure WinRM for remote management (requires admin) Write-Log "" -Level "INFO" -ForegroundColor White Write-Log "=== STEP 7: WINRM CONFIGURATION ===" -Level "INFO" -ForegroundColor Cyan if ($isAdmin) { $winrmSuccess = Reset-WinRMConfiguration } else { Write-Log " [SKIP] Not running as admin - WinRM configuration skipped" -Level "WARN" -ForegroundColor Yellow $winrmSuccess = $false } # Step 8: Add WinRM management group to local Administrators (requires admin) Write-Log "" -Level "INFO" -ForegroundColor White Write-Log "=== STEP 8: WINRM ADMIN GROUP ===" -Level "INFO" -ForegroundColor Cyan if ($isAdmin) { $adminGroupSuccess = Add-WinRMAdminGroup } else { Write-Log " [SKIP] Not running as admin - Admin group setup skipped" -Level "WARN" -ForegroundColor Yellow $adminGroupSuccess = $false } # Final Summary Write-Log "" -Level "INFO" -ForegroundColor White Write-Log "=== COMPLETE ASSET UPDATE SUCCESS ===" -Level "INFO" -ForegroundColor Green Write-Log "Computer: $($systemInfo.Hostname)" -Level "INFO" -ForegroundColor White Write-Log "Type: $($systemInfo.PCType)" -Level "INFO" -ForegroundColor White Write-Log "Serial: $($systemInfo.SerialNumber)" -Level "INFO" -ForegroundColor White if ($systemInfo.MachineNo) { Write-Log "Machine: $($systemInfo.MachineNo)" -Level "INFO" -ForegroundColor White } Write-Log "" -Level "INFO" -ForegroundColor White Write-Log "Data Collected & Stored:" -Level "INFO" -ForegroundColor Cyan Write-Log "[OK] Basic system information" -Level "SUCCESS" -ForegroundColor Green if ($defaultPrinterFQDN) { Write-Log "[OK] Default printer mapping ($defaultPrinterFQDN)" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log "[--] Default printer mapping (no printer found)" -Level "INFO" -ForegroundColor Gray } if ($systemInfo.TrackedApplications -and $systemInfo.TrackedApplications.Count -gt 0) { Write-Log "[OK] Application mapping ($($systemInfo.TrackedApplications.Count) tracked apps)" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log "[--] Application mapping (no tracked apps found)" -Level "INFO" -ForegroundColor Gray } if ($shopfloorInfo) { Write-Log "[OK] Shopfloor configurations ($($shopfloorInfo.NetworkInterfaces.Count) network, $($shopfloorInfo.CommConfigs.Count) comm)" -Level "SUCCESS" -ForegroundColor Green } if ($warrantyData) { Write-Log "[OK] Warranty information ($($warrantyData.warrantyStatus), $($warrantyData.daysRemaining) days)" -Level "SUCCESS" -ForegroundColor Green } elseif (-not $SkipWarranty -and $isDellSystem) { Write-Log "[WARN] Warranty information (lookup failed)" -Level "WARN" -ForegroundColor Yellow } if ($winrmSuccess) { Write-Log "[OK] WinRM HTTP listener (port 5985)" -Level "SUCCESS" -ForegroundColor Green Write-Log " Note: If remote access still fails, a reboot may be required" -Level "INFO" -ForegroundColor Gray } else { Write-Log "[WARN] WinRM configuration (may need manual setup)" -Level "WARN" -ForegroundColor Yellow } if ($adminGroupSuccess) { Write-Log "[OK] WinRM admin group (logon\g03078610)" -Level "SUCCESS" -ForegroundColor Green } else { Write-Log "[WARN] WinRM admin group (failed to add)" -Level "WARN" -ForegroundColor Yellow } Write-Log "" -Level "INFO" -ForegroundColor White Write-Log "[OK] Complete PC asset collection finished!" -Level "SUCCESS" -ForegroundColor Green Write-Log "All data stored in database via dashboard API." -Level "INFO" -ForegroundColor Gray Write-Log "Log file: $script:LogFile" -Level "INFO" -ForegroundColor Gray } catch { Write-Log "Complete asset collection failed: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red Write-Error "Complete asset collection failed: $($_.Exception.Message)" exit 1 }