Initial commit: Organized PowerShell scripts for ShopDB asset collection

Structure:
- asset-collection/: Local PC data collection scripts
- remote-execution/: WinRM remote execution scripts
- setup-utilities/: Configuration and testing utilities
- registry-backup/: GE registry backup scripts
- winrm-https/: WinRM HTTPS certificate setup
- docs/: Complete documentation

Each folder includes a README with detailed documentation.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-12-10 10:57:54 -05:00
commit 62c0c7bb06
102 changed files with 28017 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
@echo off
wmic product get name,version
pause

View File

@@ -0,0 +1,5 @@
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Where-Object { $_.DisplayName } |
Select-Object DisplayName, DisplayVersion, Publisher |
Sort-Object DisplayName |
Format-Table -AutoSize

View File

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

125
asset-collection/README.md Normal file
View File

@@ -0,0 +1,125 @@
# Asset Collection Scripts
Scripts for collecting PC asset data and sending it to the ShopDB database.
## Scripts
### Update-PC-CompleteAsset.ps1
**Primary asset collection script** - Comprehensive data collection for all PC types.
**What it collects:**
- System information (hostname, serial number, manufacturer, model)
- Operating system details
- Network interface configurations
- For shopfloor PCs: DNC/machine configurations from GE registry
- Dell warranty information (optional, via proxy)
**Usage:**
```powershell
# Standard execution (requires administrator)
.\Update-PC-CompleteAsset.ps1
# Test connectivity only
.\Update-PC-CompleteAsset.ps1 -TestConnections
# With warranty lookup enabled
.\Update-PC-CompleteAsset.ps1 -SkipWarranty:$false
```
**Parameters:**
| Parameter | Default | Description |
|-----------|---------|-------------|
| `-ProxyURL` | `http://10.48.130.158/vendor-api-proxy.php` | Warranty API proxy |
| `-DashboardURL` | `https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp` | ShopDB API endpoint |
| `-SkipWarranty` | `$true` | Skip warranty lookups |
| `-TestConnections` | `$false` | Test API connectivity only |
---
### Get-ShopfloorConfig.ps1
**Shopfloor configuration library** - Functions for collecting manufacturing-specific data.
**Functions provided:**
- `Get-NetworkInterfaceConfig` - Collects all network adapter information
- `Get-SerialPortConfig` - Enumerates COM port configurations
- `Get-DNCConfig` - Extracts DNC registry settings
- `Get-GERegistryConfig` - Reads GE Aircraft Engines registry keys
**Note:** This script is sourced by `Update-PC-CompleteAsset.ps1` and not run directly.
---
### Update-PC-Minimal.ps1
**Lightweight collection script** - For locked-down PCs with restricted permissions.
**What it collects:**
- Basic system info without requiring admin privileges
- Uses only non-elevated WMI/CIM queries
- Detects PC-DMIS software for measuring machine classification
**Usage:**
```powershell
.\Update-PC-Minimal.ps1
```
**When to use:**
- PCs where users cannot run as administrator
- Measuring machines with restricted permissions
- Quick data collection without full registry access
---
### Get-InstalledApps.ps1
**Application inventory script** - Collects list of installed applications.
**Usage:**
```powershell
.\Get-InstalledApps.ps1
```
---
## Batch File Launchers
| File | Purpose |
|------|---------|
| `Update-PC-CompleteAsset.bat` | Standard launcher with visible window |
| `Update-PC-CompleteAsset-Silent.bat` | Silent launcher for scheduled tasks |
| `Update-PC-Minimal.bat` | Launcher for minimal collection |
| `Get-InstalledApps.bat` | Launcher for app inventory |
| `Run-GetInstalledApps.bat` | Alternative app inventory launcher |
---
## Requirements
- PowerShell 5.1 or later
- Administrator privileges (for full collection)
- Network access to ShopDB API
- Windows 10/11 or Windows Server
## Data Flow
```
PC Local Execution
┌──────────────────┐
│ Update-PC- │
│ CompleteAsset.ps1│
│ + │
│ Get-Shopfloor- │
│ Config.ps1 │
└────────┬─────────┘
│ HTTPS POST
┌──────────────────┐
│ ShopDB API │
│ (api.asp) │
└────────┬─────────┘
┌──────────────────┐
│ MySQL Database │
└──────────────────┘
```

View File

@@ -0,0 +1,3 @@
@echo off
powershell.exe -ExecutionPolicy Bypass -File "%~dp0Get-InstalledApps.ps1"
pause

View File

@@ -0,0 +1,76 @@
@echo off
REM =====================================================
REM Complete PC Asset Collection - SILENT MODE
REM Runs Update-PC-CompleteAsset.ps1 with all output to log
REM =====================================================
REM Change to script directory
cd /d "%~dp0"
REM Set network share log directory
set "logdir=S:\dt\cameron\scan\logs"
REM Create log directory if it doesn't exist (network share should already exist)
if not exist "%logdir%" (
set "logdir=Logs"
if not exist "Logs" mkdir "Logs"
)
REM Generate log filename with timestamp
for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%"
set "datestamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%"
set "logfile=%logdir%\CompleteAsset-%COMPUTERNAME%-%datestamp%.log"
REM Log start information
echo ===================================== >> "%logfile%" 2>&1
echo Complete PC Asset Collection - %date% %time% >> "%logfile%" 2>&1
echo Computer: %COMPUTERNAME% >> "%logfile%" 2>&1
echo User Context: %USERNAME% >> "%logfile%" 2>&1
echo Script Directory: %CD% >> "%logfile%" 2>&1
echo Proxy: http://10.48.130.158/vendor-api-proxy.php >> "%logfile%" 2>&1
echo Dashboard: https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp >> "%logfile%" 2>&1
echo Network Load Balancing: Disabled >> "%logfile%" 2>&1
echo ===================================== >> "%logfile%" 2>&1
echo. >> "%logfile%" 2>&1
REM Check if PowerShell script exists
if not exist "Update-PC-CompleteAsset.ps1" (
echo ERROR: Update-PC-CompleteAsset.ps1 not found! >> "%logfile%" 2>&1
exit /b 1
)
if not exist "Get-ShopfloorConfig.ps1" (
echo WARNING: Get-ShopfloorConfig.ps1 not found - shopfloor config may fail >> "%logfile%" 2>&1
)
REM Load balancing disabled - no random delay
REM powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -WindowStyle Hidden -Command "Start-Sleep -Seconds (Get-Random -Minimum 0 -Maximum 600)" >> "%logfile%" 2>&1
REM Backup GE Aircraft Engines registry if it exists (silent mode)
echo Checking for GE Aircraft Engines registry... >> "%logfile%" 2>&1
if exist "Backup-GERegistry.ps1" (
powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -WindowStyle Hidden -File "%~dp0Backup-GERegistry.ps1" -Silent >> "%logfile%" 2>&1
if %ERRORLEVEL% equ 0 (
echo GE registry backup completed successfully >> "%logfile%" 2>&1
) else (
echo GE registry not found or backup skipped >> "%logfile%" 2>&1
)
) else (
echo Backup-GERegistry.ps1 not found - skipping registry backup >> "%logfile%" 2>&1
)
echo. >> "%logfile%" 2>&1
REM Run PowerShell script directly
echo. >> "%logfile%" 2>&1
echo === Running PowerShell script === >> "%logfile%" 2>&1
echo. >> "%logfile%" 2>&1
powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -WindowStyle Hidden -File "%~dp0Update-PC-CompleteAsset.ps1" -ProxyURL "http://10.48.130.158/vendor-api-proxy.php" -DashboardURL "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp" >> "%logfile%" 2>&1
echo. >> "%logfile%" 2>&1
echo === Script completed === >> "%logfile%" 2>&1
echo Exit code: %ERRORLEVEL% >> "%logfile%" 2>&1
echo End time: %date% %time% >> "%logfile%" 2>&1
echo. >> "%logfile%" 2>&1

View File

@@ -0,0 +1,83 @@
@echo off
REM =====================================================
REM Complete PC Asset Collection - Batch Launcher
REM Runs Update-PC-CompleteAsset.ps1 with execution policy bypass
REM =====================================================
echo.
echo Complete PC Asset Collection - Starting...
echo ========================================
echo.
REM Check if running as administrator
net session >nul 2>&1
if %errorLevel% == 0 (
echo Running as Administrator: YES
) else (
echo.
echo ERROR: This script must be run as Administrator!
echo Right-click and select "Run as administrator"
echo.
pause
exit /b 1
)
echo.
echo Dashboard: http://10.48.130.197/test/dashboard/api.php
echo PC: %COMPUTERNAME%
echo User: %USERNAME%
echo Note: Warranty lookups handled by dashboard server
echo.
REM Change to script directory
cd /d "%~dp0"
REM Check if PowerShell script exists
if not exist "Update-PC-CompleteAsset.ps1" (
echo.
echo ERROR: Update-PC-CompleteAsset.ps1 not found in current directory!
echo Current directory: %CD%
echo.
pause
exit /b 1
)
REM Check if shopfloor config script exists
if not exist "Get-ShopfloorConfig.ps1" (
echo.
echo WARNING: Get-ShopfloorConfig.ps1 not found!
echo Shopfloor configuration collection may fail.
echo.
)
echo Starting complete asset collection...
echo.
REM Run PowerShell script with bypass execution policy
powershell.exe -ExecutionPolicy Bypass -NoLogo -File "%~dp0Update-PC-CompleteAsset.ps1"
REM Capture the exit code from PowerShell
set PS_EXIT_CODE=%ERRORLEVEL%
echo.
echo ========================================
if %PS_EXIT_CODE% == 0 (
echo Complete asset collection SUCCESS!
echo Exit code: %PS_EXIT_CODE%
echo.
echo All data has been collected and stored:
echo - System information ^(hostname, serial, type, user^)
echo - Shopfloor configurations ^(if applicable^)
echo - Dell warranty information ^(if Dell system^)
) else (
echo Complete asset collection FAILED!
echo Exit code: %PS_EXIT_CODE%
echo.
echo Check the output above for error details.
)
echo.
echo Press any key to close this window...
pause >nul
exit /b %PS_EXIT_CODE%

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0Update-PC-Minimal.ps1"
echo.
echo Log saved to: %TEMP%\shopdb-update.log
echo.
pause

View File

@@ -0,0 +1,510 @@
# Minimal PC data collection script
# For locked-down PCs with restricted permissions
# SSL/TLS Certificate Bypass for HTTPS connections
try {
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 { }
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$apiUrl = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp"
$logFile = "$env:TEMP\shopdb-update.log"
# Start fresh log
"$(Get-Date) - Starting minimal PC update" | Out-File $logFile
$data = @{
action = 'updateCompleteAsset'
hostname = $env:COMPUTERNAME
manufacturer = 'Unknown'
model = 'Unknown'
serialNumber = 'Unknown'
pcType = 'Measuring' # Default, will be updated if PC-DMIS is found
}
"Hostname: $($data.hostname)" | Tee-Object -FilePath $logFile -Append
# Try to get serial number
try {
$bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop
$data.serialNumber = $bios.SerialNumber
"Serial: $($data.serialNumber)" | Tee-Object -FilePath $logFile -Append
} catch {
"ERROR getting serial: $_" | Tee-Object -FilePath $logFile -Append
}
# Try to get manufacturer/model
try {
$cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
$data.manufacturer = $cs.Manufacturer
$data.model = $cs.Model
"Manufacturer: $($data.manufacturer)" | Tee-Object -FilePath $logFile -Append
"Model: $($data.model)" | Tee-Object -FilePath $logFile -Append
} catch {
"ERROR getting system info: $_" | Tee-Object -FilePath $logFile -Append
}
# Get IP address using ipconfig (no elevated permissions required)
$interfaces = @()
try {
$ipconfig = ipconfig /all
$currentIP = ""
$currentMAC = ""
foreach ($line in $ipconfig) {
if ($line -match '^\S') {
# New adapter section - save previous if valid
if ($currentIP -and $currentIP -notlike '127.*' -and $currentIP -notlike '169.254.*') {
$interfaces += @{
IPAddress = $currentIP
MACAddress = $currentMAC
}
"IP: $currentIP | MAC: $currentMAC" | Tee-Object -FilePath $logFile -Append
}
$currentIP = ""
$currentMAC = ""
}
elseif ($line -match 'IPv4 Address.*:\s*(\d+\.\d+\.\d+\.\d+)') {
$currentIP = $matches[1] -replace '\(Preferred\)',''
}
elseif ($line -match 'Physical Address.*:\s*([0-9A-F-]+)') {
$currentMAC = $matches[1] -replace '-',':'
}
}
# Don't forget the last adapter
if ($currentIP -and $currentIP -notlike '127.*' -and $currentIP -notlike '169.254.*') {
$interfaces += @{
IPAddress = $currentIP
MACAddress = $currentMAC
}
"IP: $currentIP | MAC: $currentMAC" | Tee-Object -FilePath $logFile -Append
}
} catch {
"ERROR getting network info: $_" | Tee-Object -FilePath $logFile -Append
}
if ($interfaces.Count -gt 0) {
$data.networkInterfaces = ($interfaces | ConvertTo-Json -Compress)
}
# Try to get OS and last boot time
try {
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
$data.osVersion = $os.Caption
"OS: $($data.osVersion)" | Tee-Object -FilePath $logFile -Append
# Get last boot time
if ($os.LastBootUpTime) {
$data.lastBootUpTime = $os.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss")
"Last Boot: $($data.lastBootUpTime)" | Tee-Object -FilePath $logFile -Append
}
} catch {
"ERROR getting OS: $_" | Tee-Object -FilePath $logFile -Append
}
# Try to get logged in user
try {
$data.loggedInUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
"User: $($data.loggedInUser)" | Tee-Object -FilePath $logFile -Append
} catch {
"ERROR getting user: $_" | Tee-Object -FilePath $logFile -Append
}
# Try to get machine number (no admin required for reading HKLM)
try {
$machineNo = $null
# Primary location: GE Aircraft Engines DNC registry (64-bit and 32-bit)
$regPaths = @(
"HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General",
"HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General"
)
foreach ($regPath in $regPaths) {
if (Test-Path $regPath) {
$regValue = (Get-ItemProperty -Path $regPath -Name "MachineNo" -ErrorAction SilentlyContinue).MachineNo
if ($regValue) {
$machineNo = $regValue
"Machine # from registry ($regPath): $machineNo" | Tee-Object -FilePath $logFile -Append
break
}
}
}
# Fall back to DNC.ini file
if (-not $machineNo) {
$dncIniPath = "C:\DNC\DNC.ini"
if (Test-Path $dncIniPath) {
$iniContent = Get-Content $dncIniPath -Raw -ErrorAction SilentlyContinue
if ($iniContent -match 'MachineNo\s*=\s*(.+)') {
$machineNo = $matches[1].Trim()
"Machine # from DNC.ini: $machineNo" | Tee-Object -FilePath $logFile -Append
}
}
}
if ($machineNo) {
# Check if machine number is a generic/placeholder value
# These should not be sent to API - must be set manually
# Generic machine numbers - don't send to API but can help identify PC type
$genericMachineTypes = @{
"^WJPRT" = "Measuring" # Generic printer/measuring tool
"^WJCMM" = "CMM" # Generic CMM
"^WJMEAS" = "Measuring" # Generic measuring
"^0600$" = "Wax Trace" # Wax trace machines
"^0612$" = "Part Marker" # Part markers
"^0613$" = "Part Marker" # Part markers
"^0615" = "Part Marker" # Part markers
"^8003$" = "Part Marker" # Part markers
"^TEST" = $null # Test machines - no type hint
"^TEMP" = $null # Temporary - no type hint
"^DEFAULT"= $null # Default value - no type hint
"^0+$" = $null # All zeros - no type hint
}
$isGeneric = $false
$genericTypeHint = $null
foreach ($pattern in $genericMachineTypes.Keys) {
if ($machineNo -match $pattern) {
$isGeneric = $true
$genericTypeHint = $genericMachineTypes[$pattern]
break
}
}
if ($isGeneric) {
if ($genericTypeHint) {
"Machine # '$machineNo' is generic ($genericTypeHint) - NOT sending to API (requires manual assignment)" | Tee-Object -FilePath $logFile -Append
# Store the type hint for later use in PC type detection
$script:genericTypeHint = $genericTypeHint
} else {
"Machine # '$machineNo' is generic/placeholder - NOT sending to API (requires manual assignment)" | Tee-Object -FilePath $logFile -Append
}
# Don't set $data.machineNo - leave it out of API call
} else {
$data.machineNo = $machineNo
}
} else {
"No machine number found" | Tee-Object -FilePath $logFile -Append
}
} catch {
"ERROR getting machine number: $_" | Tee-Object -FilePath $logFile -Append
}
# Check for VNC installation
try {
$hasVnc = $false
# Check registry for installed programs (both 32-bit and 64-bit)
$regPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
foreach ($path in $regPaths) {
if (Test-Path $path) {
$apps = Get-ItemProperty $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like "*VNC Server*" -or $_.DisplayName -like "*VNC Connect*" -or $_.DisplayName -like "*RealVNC*" }
if ($apps) {
$hasVnc = $true
"VNC detected: $($apps.DisplayName -join ', ')" | Tee-Object -FilePath $logFile -Append
break
}
}
}
# Also check for VNC service
if (-not $hasVnc) {
$vncService = Get-Service -Name "vncserver*" -ErrorAction SilentlyContinue
if ($vncService) {
$hasVnc = $true
"VNC service detected: $($vncService.Name)" | Tee-Object -FilePath $logFile -Append
}
}
if ($hasVnc) {
$data.hasVnc = "1"
} else {
$data.hasVnc = "0"
"No VNC detected" | Tee-Object -FilePath $logFile -Append
}
} catch {
"ERROR checking VNC: $_" | Tee-Object -FilePath $logFile -Append
$data.hasVnc = "0"
}
# Check for WinRM status
try {
$hasWinRM = $false
# Check if WinRM service is running
$winrmService = Get-Service -Name "WinRM" -ErrorAction SilentlyContinue
if ($winrmService -and $winrmService.Status -eq "Running") {
# Also verify WinRM is configured to accept connections
try {
$winrmConfig = winrm get winrm/config/service 2>$null
if ($winrmConfig -match "AllowRemoteAccess\s*=\s*true") {
$hasWinRM = $true
"WinRM enabled and accepting connections" | Tee-Object -FilePath $logFile -Append
} else {
"WinRM service running but remote access not enabled" | Tee-Object -FilePath $logFile -Append
}
} catch {
# If we can't check config, assume it's enabled if service is running
$hasWinRM = $true
"WinRM service running (config check skipped)" | Tee-Object -FilePath $logFile -Append
}
} else {
"WinRM service not running" | Tee-Object -FilePath $logFile -Append
}
if ($hasWinRM) {
$data.hasWinRM = "1"
} else {
$data.hasWinRM = "0"
}
} catch {
"ERROR checking WinRM: $_" | Tee-Object -FilePath $logFile -Append
$data.hasWinRM = "0"
}
# Load applications.csv for app matching
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$csvPath = Join-Path $scriptDir "applications.csv"
$appMappings = @()
if (Test-Path $csvPath) {
try {
$appMappings = Import-Csv $csvPath | Where-Object { $_.enabled -eq "1" -and $_.app_id }
"Loaded $($appMappings.Count) app mappings from CSV" | Tee-Object -FilePath $logFile -Append
} catch {
"ERROR loading applications.csv: $_" | Tee-Object -FilePath $logFile -Append
}
} else {
"WARNING: applications.csv not found at $csvPath" | Tee-Object -FilePath $logFile -Append
}
# Get installed applications from registry and match against CSV
$matchedApps = @()
$hasPcDmis = $false
$hasFormTracePak = $false
$hasKeyence = $false
$hasEAS1000 = $false
$hasGoCMM = $false
$hasDODA = $false
$hasFormStatusMonitor = $false
$hasGageCal = $false
$hasNISoftware = $false
$hasGenspect = $false
$hasHeatTreat = $false
try {
$regPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
# Get all installed apps
$installedApps = @()
foreach ($path in $regPaths) {
if (Test-Path $path) {
$apps = Get-ItemProperty $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -and $_.DisplayName.Trim() -ne "" }
foreach ($app in $apps) {
$installedApps += @{
DisplayName = $app.DisplayName.Trim()
Version = if ($app.DisplayVersion) { $app.DisplayVersion.Trim() } else { "" }
}
}
}
}
"Found $($installedApps.Count) installed applications" | Tee-Object -FilePath $logFile -Append
# Match against CSV patterns
foreach ($mapping in $appMappings) {
$patterns = $mapping.search_patterns -split '\|'
foreach ($installedApp in $installedApps) {
$matched = $false
foreach ($pattern in $patterns) {
if ($installedApp.DisplayName -match $pattern) {
$matched = $true
break
}
}
if ($matched) {
# Avoid duplicates
if (-not ($matchedApps | Where-Object { $_.appid -eq $mapping.app_id })) {
$matchedApps += @{
appid = [int]$mapping.app_id
appname = $mapping.app_name
version = $installedApp.Version
}
"$($mapping.app_name) (ID:$($mapping.app_id)) detected: $($installedApp.DisplayName)" | Tee-Object -FilePath $logFile -Append
# Check for PC type indicators
switch ($mapping.app_name) {
"PC-DMIS" { $hasPcDmis = $true }
"goCMM" { $hasGoCMM = $true }
"DODA" { $hasDODA = $true }
"FormTracePak" { $hasFormTracePak = $true }
"FormStatusMonitor" { $hasFormStatusMonitor = $true }
"Keyence VR Series" { $hasKeyence = $true }
"GageCal" { $hasGageCal = $true }
"NI Software" { $hasNISoftware = $true }
"Genspect" { $hasGenspect = $true }
"HeatTreat" { $hasHeatTreat = $true }
}
}
break
}
}
}
"Matched $($matchedApps.Count) tracked applications" | Tee-Object -FilePath $logFile -Append
if ($matchedApps.Count -gt 0) {
$data.installedApps = ($matchedApps | ConvertTo-Json -Compress)
}
} catch {
"ERROR getting installed apps: $_" | Tee-Object -FilePath $logFile -Append
}
# File path fallbacks for apps that may not be in registry
# Check PC-DMIS
if (-not $hasPcDmis) {
try {
$pcDmisPaths = @(
"C:\ProgramData\Hexagon\PC-DMIS*",
"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
"PC-DMIS found at: $dmisPath" | Tee-Object -FilePath $logFile -Append
break
}
}
} catch {
"ERROR checking PC-DMIS paths: $_" | Tee-Object -FilePath $logFile -Append
}
}
# Check UDC if not already matched
if (-not ($matchedApps | Where-Object { $_.appid -eq 2 })) {
if (Test-Path "C:\Program Files\UDC") {
$matchedApps += @{ appid = 2; appname = "UDC"; version = "" }
"UDC found at: C:\Program Files\UDC" | Tee-Object -FilePath $logFile -Append
}
}
# Check eDNC if not already matched
if (-not ($matchedApps | Where-Object { $_.appid -eq 8 })) {
if (Test-Path "C:\Program Files (x86)\DNC") {
$matchedApps += @{ appid = 8; appname = "eDNC"; version = "" }
"eDNC found at: C:\Program Files (x86)\DNC" | Tee-Object -FilePath $logFile -Append
}
}
# Check FormTracePak if not already matched
if (-not $hasFormTracePak) {
try {
$formTracePaths = @(
"C:\Program Files\MitutoyoApp*",
"C:\Program Files (x86)\MitutoyoApp*"
)
foreach ($ftPath in $formTracePaths) {
if (Test-Path $ftPath) {
$hasFormTracePak = $true
if (-not ($matchedApps | Where-Object { $_.appid -eq 76 })) {
$matchedApps += @{ appid = 76; appname = "FormTracePak"; version = "" }
}
"FormTracePak found at: $ftPath" | Tee-Object -FilePath $logFile -Append
break
}
}
} catch {
"ERROR checking FormTracePak paths: $_" | Tee-Object -FilePath $logFile -Append
}
}
# Update installedApps data if we found more apps via file paths
if ($matchedApps.Count -gt 0) {
$data.installedApps = ($matchedApps | ConvertTo-Json -Compress)
}
# Set PC type based on application detection
# Priority: CMM > Wax Trace > Keyence > EAS1000 > Genspect > Heat Treat > Generic hint > default Shopfloor
$isCMM = ($hasPcDmis -or $hasGoCMM -or $hasDODA)
$isWaxTrace = ($hasFormTracePak -or $hasFormStatusMonitor)
$isEAS1000 = ($hasGageCal -or $hasNISoftware)
if ($isCMM) {
$data.pcType = "CMM"
$detected = @()
if ($hasPcDmis) { $detected += "PC-DMIS" }
if ($hasGoCMM) { $detected += "goCMM" }
if ($hasDODA) { $detected += "DODA" }
"PC Type set to: CMM ($($detected -join ', '))" | Tee-Object -FilePath $logFile -Append
} elseif ($isWaxTrace) {
$data.pcType = "Wax Trace"
$detected = @()
if ($hasFormTracePak) { $detected += "FormTracePak" }
if ($hasFormStatusMonitor) { $detected += "FormStatusMonitor" }
"PC Type set to: Wax Trace ($($detected -join ', '))" | Tee-Object -FilePath $logFile -Append
} elseif ($hasKeyence) {
$data.pcType = "Keyence"
"PC Type set to: Keyence (Keyence VR Series)" | Tee-Object -FilePath $logFile -Append
} elseif ($isEAS1000) {
$data.pcType = "EAS1000"
$detected = @()
if ($hasGageCal) { $detected += "GageCal" }
if ($hasNISoftware) { $detected += "NI Software" }
"PC Type set to: EAS1000 ($($detected -join ', '))" | Tee-Object -FilePath $logFile -Append
} elseif ($hasGenspect) {
$data.pcType = "Genspect"
"PC Type set to: Genspect" | Tee-Object -FilePath $logFile -Append
} elseif ($hasHeatTreat) {
$data.pcType = "Heat Treat"
"PC Type set to: Heat Treat" | Tee-Object -FilePath $logFile -Append
} elseif ($script:genericTypeHint) {
# Use generic machine number hint when no software detected
$data.pcType = $script:genericTypeHint
"PC Type set to: $($script:genericTypeHint) (from generic machine # - requires manual assignment)" | Tee-Object -FilePath $logFile -Append
} else {
$data.pcType = "Shopfloor"
"No specialized apps detected, defaulting to: Shopfloor" | Tee-Object -FilePath $logFile -Append
}
# Send to API
"Sending to API: $apiUrl" | Tee-Object -FilePath $logFile -Append
try {
$response = Invoke-RestMethod -Uri $apiUrl -Method Post -Body $data -ErrorAction Stop
"API Response: $($response | ConvertTo-Json -Compress)" | Tee-Object -FilePath $logFile -Append
if ($response.success) {
"SUCCESS - MachineID: $($response.machineid)" | Tee-Object -FilePath $logFile -Append
} else {
"FAILED - $($response.message)" | Tee-Object -FilePath $logFile -Append
}
} catch {
"ERROR calling API: $_" | Tee-Object -FilePath $logFile -Append
}
"$(Get-Date) - Done" | Tee-Object -FilePath $logFile -Append