Complete LIMS Debugging PowerShell Script

Save this entire file as lims-complete.ps1 and load it with . .\lims-complete.ps1


# ====================================
# COMPLETE LIMS DEBUGGING FUNCTIONS
# ====================================
# Save this file as: C:\Scripts\lims-complete.ps1
# Load with: . C:\Scripts\lims-complete.ps1
# Then run any function you want

#region Path Discovery
function Find-LIMSInstallationPaths {
    param(
        [string[]]$SearchPatterns = @("*LIMS*", "*LAB*", "*Laboratory*"),
        [string[]]$CommonVendors = @("Thermo", "Waters", "Agilent", "PerkinElmer", "Abbott", "Roche", "Siemens"),
        [switch]$IncludeRegistry = $true,
        [switch]$IncludeServices = $true,
        [switch]$IncludeIIS = $true,
        [switch]$Verbose = $false
    )
    
    Write-Host "Discovering LIMS installation paths..." -ForegroundColor Yellow
    
    $discoveredPaths = @{
        ProgramFiles = @()
        IISPaths = @()
        ServicePaths = @()
        RegistryPaths = @()
        ConfigFiles = @()
        LogDirectories = @()
        DataDirectories = @()
        TempDirectories = @()
        AllUniquePaths = @()
    }
    
    # 1. Search Program Files directories
    Write-Host "Searching Program Files..." -ForegroundColor Cyan
    $programDirs = @("C:\Program Files", "C:\Program Files (x86)", "D:\Program Files")
    
    foreach ($dir in $programDirs) {
        if (Test-Path $dir) {
            # Search by common patterns
            foreach ($pattern in $SearchPatterns) {
                $found = Get-ChildItem $dir -Directory -Name $pattern -ErrorAction SilentlyContinue
                foreach ($folder in $found) {
                    $fullPath = Join-Path $dir $folder
                    $discoveredPaths.ProgramFiles += $fullPath
                    if ($Verbose) { Write-Host "  Found: $fullPath" -ForegroundColor Gray }
                }
            }
            
            # Search by vendor names
            foreach ($vendor in $CommonVendors) {
                $found = Get-ChildItem $dir -Directory -Name "*$vendor*" -ErrorAction SilentlyContinue
                foreach ($folder in $found) {
                    $fullPath = Join-Path $dir $folder
                    # Check if it contains LIMS-related files
                    $hasLIMSFiles = Get-ChildItem $fullPath -Recurse -File -Include "*.exe", "*.dll", "*.config" -ErrorAction SilentlyContinue |
                                   Where-Object { $_.Name -match "LIMS|Lab|Sample|Result|Instrument" } |
                                   Select-Object -First 1
                    if ($hasLIMSFiles) {
                        $discoveredPaths.ProgramFiles += $fullPath
                        if ($Verbose) { Write-Host "  Found vendor dir: $fullPath" -ForegroundColor Gray }
                    }
                }
            }
        }
    }
    
    # 2. Check IIS sites and applications
    if ($IncludeIIS) {
        Write-Host "Checking IIS sites..." -ForegroundColor Cyan
        try {
            Import-Module WebAdministration -ErrorAction SilentlyContinue
            $sites = Get-IISSite -ErrorAction SilentlyContinue
            
            foreach ($site in $sites) {
                if ($site.Name -match ($SearchPatterns -join "|")) {
                    $discoveredPaths.IISPaths += $site.PhysicalPath
                    if ($Verbose) { Write-Host "  Found IIS site: $($site.Name) -> $($site.PhysicalPath)" -ForegroundColor Gray }
                }
                
                # Check applications within sites
                $apps = Get-WebApplication -Site $site.Name -ErrorAction SilentlyContinue
                foreach ($app in $apps) {
                    if ($app.Path -match ($SearchPatterns -join "|")) {
                        $discoveredPaths.IISPaths += $app.PhysicalPath
                        if ($Verbose) { Write-Host "  Found IIS app: $($app.Path) -> $($app.PhysicalPath)" -ForegroundColor Gray }
                    }
                }
            }
        }
        catch {
            Write-Warning "Could not check IIS: $($_.Exception.Message)"
        }
    }
    
    # 3. Check Windows Services
    if ($IncludeServices) {
        Write-Host "Checking Windows services..." -ForegroundColor Cyan
        $services = Get-WmiObject Win32_Service | Where-Object { 
            $_.Name -match ($SearchPatterns -join "|") -or 
            $_.DisplayName -match ($SearchPatterns -join "|") -or
            $_.PathName -match ($SearchPatterns -join "|")
        }
        
        foreach ($service in $services) {
            if ($service.PathName) {
                # Extract directory from service path
                $servicePath = $service.PathName -replace '"', ''
                if (Test-Path $servicePath) {
                    $serviceDir = Split-Path $servicePath -Parent
                    $discoveredPaths.ServicePaths += $serviceDir
                    if ($Verbose) { Write-Host "  Found service: $($service.Name) -> $serviceDir" -ForegroundColor Gray }
                }
            }
        }
    }
    
    # 4. Check Registry for installation paths
    if ($IncludeRegistry) {
        Write-Host "Checking Registry..." -ForegroundColor Cyan
        $regPaths = @(
            "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
            "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
        )
        
        foreach ($regPath in $regPaths) {
            try {
                $programs = Get-ItemProperty $regPath -ErrorAction SilentlyContinue | 
                           Where-Object { 
                               $_.DisplayName -match ($SearchPatterns -join "|") -or
                               $_.Publisher -match ($CommonVendors -join "|")
                           }
                
                foreach ($program in $programs) {
                    if ($program.InstallLocation -and (Test-Path $program.InstallLocation)) {
                        $discoveredPaths.RegistryPaths += $program.InstallLocation
                        if ($Verbose) { Write-Host "  Found registry: $($program.DisplayName) -> $($program.InstallLocation)" -ForegroundColor Gray }
                    }
                }
            }
            catch {
                # Registry access might fail, continue silently
            }
        }
    }
    
    # Consolidate all paths
    $allPaths = @()
    $allPaths += $discoveredPaths.ProgramFiles
    $allPaths += $discoveredPaths.IISPaths
    $allPaths += $discoveredPaths.ServicePaths
    $allPaths += $discoveredPaths.RegistryPaths
    
    $discoveredPaths.AllUniquePaths = $allPaths | Sort-Object -Unique | Where-Object { $_ -and (Test-Path $_) }
    
    # Find related directories (Config, Logs, Temp) for each discovered path
    foreach ($basePath in $discoveredPaths.AllUniquePaths) {
        # Look for log directories
        $logDirs = Get-ChildItem $basePath -Recurse -Directory -Name "*log*", "*Log*" -ErrorAction SilentlyContinue
        foreach ($logDir in $logDirs) {
            $discoveredPaths.LogDirectories += Join-Path $basePath $logDir
        }
        
        # Look for temp directories
        $tempDirs = Get-ChildItem $basePath -Recurse -Directory -Name "*temp*", "*tmp*", "*cache*" -ErrorAction SilentlyContinue
        foreach ($tempDir in $tempDirs) {
            $discoveredPaths.TempDirectories += Join-Path $basePath $tempDir
        }
    }
    
    # Remove duplicates
    $discoveredPaths.LogDirectories = $discoveredPaths.LogDirectories | Sort-Object -Unique | Where-Object { $_ -and (Test-Path $_) }
    $discoveredPaths.TempDirectories = $discoveredPaths.TempDirectories | Sort-Object -Unique | Where-Object { $_ -and (Test-Path $_) }
    
    # Display summary
    Write-Host "`nLIMS Path Discovery Summary:" -ForegroundColor Green
    Write-Host "  Program installations: $($discoveredPaths.ProgramFiles.Count)" -ForegroundColor Cyan
    Write-Host "  IIS sites/applications: $($discoveredPaths.IISPaths.Count)" -ForegroundColor Cyan  
    Write-Host "  Service locations: $($discoveredPaths.ServicePaths.Count)" -ForegroundColor Cyan
    Write-Host "  Registry entries: $($discoveredPaths.RegistryPaths.Count)" -ForegroundColor Cyan
    Write-Host "  Log directories: $($discoveredPaths.LogDirectories.Count)" -ForegroundColor Cyan
    Write-Host "  Temp directories: $($discoveredPaths.TempDirectories.Count)" -ForegroundColor Cyan
    Write-Host "  Total unique paths: $($discoveredPaths.AllUniquePaths.Count)" -ForegroundColor Yellow
    
    return $discoveredPaths
}
#endregion

#region File System Health
function Test-LIMSFileSystemHealth {
    param(
        [string[]]$CriticalPaths = @(),
        [switch]$AutoDiscoverPaths = $true,
        [string[]]$AdditionalPaths = @(),
        [int]$LargeFolderSizeMB = 1000,
        [int]$OldFileDays = 30,
        [switch]$IncludeStandardPaths = $true
    )
    
    Write-Host "Analyzing LIMS file system health..." -ForegroundColor Yellow
    
    # Build the list of paths to check
    $pathsToCheck = @()
    
    # Add manually specified paths
    $pathsToCheck += $CriticalPaths
    $pathsToCheck += $AdditionalPaths
    
    # Add standard Windows paths that are commonly used
    if ($IncludeStandardPaths) {
        $standardPaths = @(
            "C:\inetpub\wwwroot",
            "C:\Windows\Temp",
            "C:\Temp"
        )
        $pathsToCheck += $standardPaths | Where-Object { Test-Path $_ }
    }
    
    # Auto-discover LIMS paths if requested
    if ($AutoDiscoverPaths) {
        Write-Host "Auto-discovering LIMS paths..." -ForegroundColor Cyan
        try {
            $discoveredPaths = Find-LIMSInstallationPaths -Verbose:$false
            $pathsToCheck += $discoveredPaths.AllUniquePaths
            $pathsToCheck += $discoveredPaths.LogDirectories
            $pathsToCheck += $discoveredPaths.TempDirectories
        }
        catch {
            Write-Warning "Auto-discovery failed: $($_.Exception.Message)"
        }
    }
    
    # Remove duplicates and empty paths
    $pathsToCheck = $pathsToCheck | Where-Object { $_ } | Sort-Object -Unique
    
    if ($pathsToCheck.Count -eq 0) {
        Write-Warning "No paths to check. Use -CriticalPaths parameter or enable -AutoDiscoverPaths"
        return $null
    }
    
    Write-Host "Checking $($pathsToCheck.Count) discovered paths..." -ForegroundColor Cyan
    
    $results = @{
        PathChecks = @()
        LargeFolders = @()
        OldFiles = @()
        Recommendations = @()
        Summary = @{}
    }
    
    foreach ($path in $pathsToCheck) {
        if (Test-Path $path) {
            try {
                # Basic path info
                $pathInfo = [PSCustomObject]@{
                    Path = $path
                    Exists = $true
                    IsWritable = $false
                    FileCount = 0
                    TotalSizeMB = 0
                    LastModified = $null
                    PathType = "Unknown"
                }
                
                # Determine path type
                if ($path -match "log|Log") { $pathInfo.PathType = "Logs" }
                elseif ($path -match "temp|tmp|cache") { $pathInfo.PathType = "Temporary" }
                elseif ($path -match "config|Config") { $pathInfo.PathType = "Configuration" }
                elseif ($path -match "data|Data|database") { $pathInfo.PathType = "Data" }
                elseif ($path -match "program|Program") { $pathInfo.PathType = "Program" }
                elseif ($path -match "inetpub|wwwroot") { $pathInfo.PathType = "Web" }
                else { $pathInfo.PathType = "Other" }
                
                # Test write access (only for certain types)
                if ($pathInfo.PathType -in @("Temporary", "Data", "Logs")) {
                    $testFile = Join-Path $path "lims_test_$(Get-Random).tmp"
                    try {
                        "test" | Out-File $testFile -Force
                        Remove-Item $testFile -Force
                        $pathInfo.IsWritable = $true
                    }
                    catch {
                        $pathInfo.IsWritable = $false
                        $results.Recommendations += "Check write permissions for $($pathInfo.PathType) directory: $path"
                    }
                }
                
                # Get folder statistics
                $files = Get-ChildItem $path -Recurse -File -ErrorAction SilentlyContinue | Select-Object -First 10000
                $pathInfo.FileCount = ($files | Measure-Object).Count
                
                if ($pathInfo.FileCount -eq 10000) {
                    $pathInfo.FileCount = "10000+"
                    $results.Recommendations += "Large directory detected (>10k files): $path - Consider cleanup"
                }
                
                if ($files) {
                    $pathInfo.TotalSizeMB = [math]::Round(($files | Measure-Object Length -Sum).Sum / 1MB, 2)
                    $pathInfo.LastModified = ($files | Sort-Object LastWriteTime -Descending | Select-Object -First 1).LastWriteTime
                }
                
                # Check for large folders
                if ($pathInfo.TotalSizeMB -gt $LargeFolderSizeMB) {
                    $results.LargeFolders += $pathInfo
                }
                
                # Find old files (especially in temp directories)
                if ($pathInfo.PathType -in @("Temporary", "Logs")) {
                    $oldFiles = $files | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$OldFileDays) }
                    if ($oldFiles) {
                        $oldFileInfo = [PSCustomObject]@{
                            Path = $path
                            PathType = $pathInfo.PathType
                            OldFileCount = ($oldFiles | Measure-Object).Count
                            OldFileSizeMB = [math]::Round(($oldFiles | Measure-Object Length -Sum).Sum / 1MB, 2)
                            OldestFile = ($oldFiles | Sort-Object LastWriteTime | Select-Object -First 1).LastWriteTime
                        }
                        $results.OldFiles += $oldFileInfo
                        
                        if ($oldFileInfo.OldFileSizeMB -gt 100) {
                            $results.Recommendations += "Consider cleanup of old files in $($pathInfo.PathType): $path ($($oldFileInfo.OldFileSizeMB)MB, $($oldFileInfo.OldFileCount) files)"
                        }
                    }
                }
                
                $results.PathChecks += $pathInfo
            }
            catch {
                $results.PathChecks += [PSCustomObject]@{
                    Path = $path
                    Exists = $true
                    Error = $_.Exception.Message
                }
            }
        }
    }
    
    # Display summary
    Write-Host "`nFile System Health Summary:" -ForegroundColor Green
    $results.PathChecks | ForEach-Object {
        $icon = switch ($_.PathType) {
            "Program" { "📁" }
            "Data" { "💾" }
            "Logs" { "📄" }
            "Temporary" { "🗂️" }
            "Configuration" { "⚙️" }
            "Web" { "🌐" }
            default { "📂" }
        }
        
        $color = if ($_.Exists -and $_.IsWritable) { "Green" } 
                elseif ($_.Exists) { "Yellow" } 
                else { "Red" }
        
        $status = if ($_.Exists -and $_.IsWritable) { "✓ OK" } 
                 elseif ($_.Exists) { "⚠ Read-Only" } 
                 else { "✗ Missing" }
        
        Write-Host "  $icon $($_.Path) [$($_.PathType)]: $status" -ForegroundColor $color
    }
    
    return $results
}
#endregion

#region Windows Event Log Analysis
function Get-LIMSWindowsEvents {
    param(
        [int]$Hours = 24,
        [string[]]$LogNames = @("Application", "System", "Security"),
        [string[]]$Sources = @("ASP.NET", "IIS", "W3SVC", "WAS", "Oracle*", "LIMS*"),
        [string]$OutputPath = "C:\temp\lims_events.txt"
    )
    
    $startTime = (Get-Date).AddHours(-$Hours)
    $allEvents = @()
    
    Write-Host "Analyzing Windows Event Logs for LIMS issues..." -ForegroundColor Yellow
    
    foreach ($logName in $LogNames) {
        try {
            $events = Get-WinEvent -FilterHashtable @{
                LogName = $logName
                StartTime = $startTime
                Level = @(1,2,3,4)  # Critical, Error, Warning, Information
            } -ErrorAction SilentlyContinue
            
            # Filter for LIMS-related events
            $limsEvents = $events | Where-Object {
                $_.Message -match "IIS|ASP\.NET|w3wp|application pool|SQL|Oracle|LIMS|timeout|error|fail" -or
                $_.ProviderName -in $Sources -or
                $_.Id -in @(1000, 1001, 1002, 1309, 2001, 2002, 5011, 5186, 7034, 7035, 7036)
            }
            
            $allEvents += $limsEvents
        }
        catch {
            Write-Warning "Could not read $logName log: $($_.Exception.Message)"
        }
    }
    
    # Generate report
    $report = @"
LIMS Windows Event Analysis - Last $Hours Hours
Generated: $(Get-Date)
================================================

TOTAL EVENTS FOUND: $($allEvents.Count)

EVENTS BY SEVERITY:
"@
    
    $allEvents | Group-Object LevelDisplayName | Sort-Object Name | ForEach-Object {
        $report += "`n$($_.Name): $($_.Count) events"
    }
    
    $report += "`n`nRECENT CRITICAL/ERROR EVENTS:`n"
    $allEvents | Where-Object { $_.LevelDisplayName -in @("Critical", "Error") } | 
        Sort-Object TimeCreated -Descending | Select-Object -First 20 | ForEach-Object {
        $report += "$($_.TimeCreated) [$($_.LevelDisplayName)] $($_.ProviderName) - $($_.Message.Substring(0, [Math]::Min(100, $_.Message.Length)))`n"
    }
    
    # Create output directory if it doesn't exist
    $outputDir = Split-Path $OutputPath -Parent
    if (-not (Test-Path $outputDir)) {
        New-Item -Path $outputDir -ItemType Directory -Force | Out-Null
    }
    
    $report | Out-File $OutputPath -Encoding UTF8
    Write-Host "Event analysis saved to: $OutputPath" -ForegroundColor Green
    
    return $allEvents
}
#endregion

#region Process Memory Monitoring
function Monitor-LIMSProcessMemory {
    param(
        [string[]]$ProcessNames = @("w3wp", "sqlservr", "oracle", "tnslsnr"),
        [int]$DurationMinutes = 30,
        [int]$SampleIntervalSeconds = 60,
        [string]$OutputPath = "C:\temp\lims_memory_trend.csv"
    )
    
    $endTime = (Get-Date).AddMinutes($DurationMinutes)
    $results = @()
    
    Write-Host "Monitoring LIMS process memory for $DurationMinutes minutes..." -ForegroundColor Yellow
    Write-Host "Sample interval: $SampleIntervalSeconds seconds" -ForegroundColor Cyan
    
    while ((Get-Date) -lt $endTime) {
        foreach ($processName in $ProcessNames) {
            $processes = Get-Process -Name $processName -ErrorAction SilentlyContinue
            
            foreach ($process in $processes) {
                try {
                    $result = [PSCustomObject]@{
                        Timestamp = Get-Date
                        ProcessName = $process.Name
                        ProcessId = $process.Id
                        WorkingSetMB = [math]::Round($process.WorkingSet64 / 1MB, 2)
                        PrivateMemoryMB = [math]::Round($process.PrivateMemorySize64 / 1MB, 2)
                        VirtualMemoryMB = [math]::Round($process.VirtualMemorySize64 / 1MB, 2)
                        HandleCount = $process.Handles
                        ThreadCount = $process.Threads.Count
                        StartTime = $process.StartTime
                    }
                    
                    $results += $result
                    Write-Host "$(Get-Date -Format 'HH:mm:ss') - $($process.Name)($($process.Id)): $($result.WorkingSetMB)MB" -ForegroundColor Gray
                }
                catch {
                    Write-Warning "Could not get details for $($process.Name): $($_.Exception.Message)"
                }
            }
        }
        
        Start-Sleep $SampleIntervalSeconds
    }
    
    # Save results
    $outputDir = Split-Path $OutputPath -Parent
    if (-not (Test-Path $outputDir)) {
        New-Item -Path $outputDir -ItemType Directory -Force | Out-Null
    }
    
    $results | Export-Csv $OutputPath -NoTypeInformation
    Write-Host "Memory trend analysis saved to: $OutputPath" -ForegroundColor Green
    
    return $results
}
#endregion

#region Services Monitoring
function Get-LIMSServiceStatus {
    param(
        [string[]]$ServicePatterns = @("*Oracle*", "*SQL*", "*IIS*", "*W3SVC*", "*LIMS*", "*Print*"),
        [switch]$IncludeAutoStartOnly = $true
    )
    
    Write-Host "Checking LIMS-related Windows services..." -ForegroundColor Yellow
    
    $services = @()
    foreach ($pattern in $ServicePatterns) {
        $services += Get-Service -Name $pattern -ErrorAction SilentlyContinue
    }
    
    $results = $services | Sort-Object Name -Unique | ForEach-Object {
        $startType = (Get-WmiObject -Class Win32_Service -Filter "Name='$($_.Name)'").StartMode
        
        [PSCustomObject]@{
            ServiceName = $_.Name
            DisplayName = $_.DisplayName
            Status = $_.Status
            StartType = $startType
            CanStop = $_.CanStop
        }
    }
    
    if ($IncludeAutoStartOnly) {
        $results = $results | Where-Object { $_.StartType -eq "Auto" }
    }
    
    # Display summary
    $stopped = ($results | Where-Object { $_.Status -eq "Stopped" }).Count
    $running = ($results | Where-Object { $_.Status -eq "Running" }).Count
    
    Write-Host "Service Summary: $running running, $stopped stopped" -ForegroundColor Cyan
    
    # Highlight critical stopped services
    $stoppedCritical = $results | Where-Object { $_.Status -eq "Stopped" -and $_.StartType -eq "Auto" }
    if ($stoppedCritical) {
        Write-Host "⚠ Critical services stopped:" -ForegroundColor Red
        $stoppedCritical | ForEach-Object {
            Write-Host "  - $($_.ServiceName) ($($_.DisplayName))" -ForegroundColor Red
        }
    }
    
    return $results
}
#endregion

#region IIS Configuration Analysis  
function Test-LIMSIISConfiguration {
    param(
        [string]$SiteName = "Default Web Site",
        [string]$AppPoolName,
        [string[]]$CriticalPaths = @("C:\inetpub\wwwroot\uploads", "C:\inetpub\wwwroot\temp")
    )
    
    Import-Module WebAdministration -ErrorAction SilentlyContinue
    
    $issues = @()
    $recommendations = @()
    
    Write-Host "Validating IIS configuration for LIMS..." -ForegroundColor Yellow
    
    try {
        # Check application pool settings
        if ($AppPoolName) {
            $appPool = Get-IISAppPool -Name $AppPoolName -ErrorAction SilentlyContinue
            if ($appPool) {
                # Check critical app pool settings
                if ($appPool.ProcessModel.IdleTimeout.TotalMinutes -gt 0) {
                    $issues += "App pool idle timeout is set to $($appPool.ProcessModel.IdleTimeout.TotalMinutes) minutes"
                    $recommendations += "Set idle timeout to 0 for LIMS (always-on application)"
                }
                
                if ($appPool.Recycling.PeriodicRestart.Time.TotalHours -lt 24) {
                    $issues += "App pool recycles every $($appPool.Recycling.PeriodicRestart.Time.TotalHours) hours"
                    $recommendations += "Consider increasing recycle time for LIMS stability"
                }
            }
        }
        
        # Display results
        Write-Host "`nIIS Configuration Analysis Results:" -ForegroundColor Green
        if ($issues.Count -eq 0) {
            Write-Host "✓ No configuration issues found" -ForegroundColor Green
        } else {
            Write-Host "⚠ Found $($issues.Count) configuration issues:" -ForegroundColor Yellow
            $issues | ForEach-Object { Write-Host "  - $_" -ForegroundColor Red }
            
            Write-Host "`nRecommendations:" -ForegroundColor Cyan
            $recommendations | ForEach-Object { Write-Host "  + $_" -ForegroundColor Green }
        }
        
        return @{
            Issues = $issues
            Recommendations = $recommendations
        }
    }
    catch {
        Write-Error "Failed to analyze IIS configuration: $($_.Exception.Message)"
    }
}
#endregion

#region Network Connectivity
function Test-LIMSConnectivity {
    param(
        [hashtable]$Endpoints = @{
            "Database_Server" = @{ Server = "localhost"; Port = 1521; Type = "Oracle" }
            "File_Server" = @{ Server = "fileserver"; Port = 445; Type = "SMB" }
            "Email_Server" = @{ Server = "mailserver"; Port = 25; Type = "SMTP" }
        },
        [int]$TimeoutSeconds = 10
    )
    
    $results = @()
    
    Write-Host "Testing LIMS network connectivity..." -ForegroundColor Yellow
    
    foreach ($endpoint in $Endpoints.GetEnumerator()) {
        $name = $endpoint.Key
        $config = $endpoint.Value
        
        Write-Host "Testing $name ($($config.Server):$($config.Port))..." -ForegroundColor Cyan
        
        try {
            $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
            $tcpTest = Test-NetConnection -ComputerName $config.Server -Port $config.Port -WarningAction SilentlyContinue
            $stopwatch.Stop()
            
            $status = if ($tcpTest.TcpTestSucceeded) { "Connected" } else { "Failed" }
            $detail = if ($tcpTest.TcpTestSucceeded) { "$($config.Type) connection successful" } else { "$($config.Type) connection failed" }
            
            $result = [PSCustomObject]@{
                Endpoint = $name
                Server = $config.Server
                Port = $config.Port
                Type = $config.Type
                Status = $status
                ResponseTime = $stopwatch.ElapsedMilliseconds
                Detail = $detail
            }
            
            $results += $result
            
            $color = if ($status -eq "Connected") { "Green" } else { "Red" }
            Write-Host "  $status - $($stopwatch.ElapsedMilliseconds)ms - $detail" -ForegroundColor $color
        }
        catch {
            Write-Host "  Error - $($_.Exception.Message)" -ForegroundColor Red
        }
    }
    
    return $results
}
#endregion

#region System Resources
function Get-LIMSSystemResources {
    param(
        [string[]]$Drives = @("C:", "D:", "F:"),
        [int]$MemoryWarningPercent = 85,
        [int]$DiskWarningPercent = 80
    )
    
    Write-Host "Checking LIMS system resources..." -ForegroundColor Yellow
    
    $results = @{
        Memory = $null
        Drives = @()
        CPU = $null
        Timestamp = Get-Date
    }
    
    # Memory check
    $memory = Get-CimInstance -ClassName Win32_OperatingSystem
    $totalMemoryGB = [math]::Round($memory.TotalVisibleMemorySize / 1MB, 2)
    $freeMemoryGB = [math]::Round($memory.FreePhysicalMemory / 1MB, 2)
    $usedMemoryGB = $totalMemoryGB - $freeMemoryGB
    $memoryPercent = [math]::Round(($usedMemoryGB / $totalMemoryGB) * 100, 1)
    
    $results.Memory = [PSCustomObject]@{
        TotalGB = $totalMemoryGB
        UsedGB = $usedMemoryGB
        FreeGB = $freeMemoryGB
        UsedPercent = $memoryPercent
        Status = if ($memoryPercent -gt $MemoryWarningPercent) { "Warning" } else { "OK" }
    }
    
    Write-Host "Memory: $usedMemoryGB/$totalMemoryGB GB ($memoryPercent%)" -ForegroundColor $(if ($memoryPercent -gt $MemoryWarningPercent) { "Yellow" } else { "Green" })
    
    # Disk space check
    foreach ($drive in $Drives) {
        try {
            $disk = Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DeviceID -eq $drive }
            if ($disk) {
                $totalGB = [math]::Round($disk.Size / 1GB, 2)
                $freeGB = [math]::Round($disk.FreeSpace / 1GB, 2)
                $usedGB = $totalGB - $freeGB
                $usedPercent = [math]::Round(($usedGB / $totalGB) * 100, 1)
                
                $driveResult = [PSCustomObject]@{
                    Drive = $drive
                    TotalGB = $totalGB
                    UsedGB = $usedGB
                    FreeGB = $freeGB
                    UsedPercent = $usedPercent
                    Status = if ($usedPercent -gt $DiskWarningPercent) { "Warning" } else { "OK" }
                }
                
                $results.Drives += $driveResult
                
                Write-Host "Drive $drive`: $usedGB/$totalGB GB ($usedPercent%)" -ForegroundColor $(if ($usedPercent -gt $DiskWarningPercent) { "Yellow" } else { "Green" })
            }
        }
        catch {
            Write-Warning "Could not check drive $drive`: $($_.Exception.Message)"
        }
    }
    
    return $results
}
#endregion

# ====================================
# USAGE INSTRUCTIONS
# ====================================

Write-Host @"

LIMS DEBUGGING FUNCTIONS LOADED!
=================================

1. Discover LIMS Paths:
   Find-LIMSInstallationPaths -Verbose

2. File System Health Check:
   Test-LIMSFileSystemHealth -AutoDiscoverPaths

3. Windows Event Analysis:
   Get-LIMSWindowsEvents -Hours 12

4. Memory Monitoring:
   Monitor-LIMSProcessMemory -DurationMinutes 30

5. Service Status Check:
   Get-LIMSServiceStatus

6. IIS Configuration Check:
   Test-LIMSIISConfiguration -SiteName "Default Web Site"

7. Network Connectivity Test:
   Test-LIMSConnectivity

8. System Resource Check:
   Get-LIMSSystemResources

To load this file: . .\lims-complete.ps1
Then run any function above!

"@ -ForegroundColor Green

How to Use This File:

1. Save it as: C:\Scripts\lims-complete.ps1

2. Load it:


   . C:\Scripts\lims-complete.ps1

3. Run any function:


   Find-LIMSInstallationPaths -Verbose
   Test-LIMSFileSystemHealth -AutoDiscoverPaths

DONE. Everything is in one file now!

PowerShell Execution Policy (Important First Step!)

Unlike Linux, PowerShell has execution policies that may block scripts:


# Check current policy
Get-ExecutionPolicy

# If it shows "Restricted", you need to change it
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

# Or for the current session only
Set-ExecutionPolicy Bypass -Scope Process

Common policies:

---

Method 1: Interactive PowerShell (Like Bash Interactive)

Open PowerShell and load functions directly:


# 1. Open PowerShell as Administrator (recommended for LIMS functions)
# Right-click PowerShell → "Run as Administrator"

# 2. Copy and paste the entire function definition
function Test-LIMSFileSystemHealth {
    param(
        [string[]]$CriticalPaths = @(),
        # ... rest of function
    )
    # ... function body
}

# 3. Press Enter - function is now loaded in memory

# 4. Run the function
Test-LIMSFileSystemHealth -AutoDiscoverPaths

Linux equivalent: Like sourcing functions in bash: . ./functions.sh

---

Method 2: Save as .ps1 File and Execute

Create a PowerShell script file:


# 1. Create file: lims-debug.ps1
# Save all your functions in this file

# 2. Load the functions (like sourcing in Linux)
. .\lims-debug.ps1

# 3. Run individual functions
Test-LIMSFileSystemHealth -AutoDiscoverPaths
Get-LIMSServiceStatus

Linux equivalent: Like ./script.sh but you need to source (load) first

---

Method 3: All-in-One Script Execution

Create a complete script that loads and runs functions:


# File: run-lims-checks.ps1

# Define all functions first
function Find-LIMSInstallationPaths {
    # ... function definition
}

function Test-LIMSFileSystemHealth {
    # ... function definition  
}

# Then execute them at the bottom
Write-Host "Starting LIMS health checks..." -ForegroundColor Green

$paths = Find-LIMSInstallationPaths
$health = Test-LIMSFileSystemHealth -AutoDiscoverPaths

Write-Host "Checks complete!" -ForegroundColor Green

Run it:


.\run-lims-checks.ps1

---

Method 4: Module Approach (Advanced)

Create a PowerShell module (like a Python package):


# 1. Create directory: C:\Users\YourName\Documents\PowerShell\Modules\LIMSDebug

# 2. Create file: LIMSDebug.psm1 with all your functions

# 3. Create file: LIMSDebug.psd1 (manifest)
@{
    ModuleVersion = '1.0'
    FunctionsToExport = @('Find-LIMSInstallationPaths', 'Test-LIMSFileSystemHealth')
}

# 4. Import and use
Import-Module LIMSDebug
Test-LIMSFileSystemHealth

---

Common Execution Scenarios

Scenario 1: Quick One-Time Check


# Open PowerShell as Administrator
# Copy entire function, paste, press Enter
# Run immediately

Test-LIMSFileSystemHealth -AutoDiscoverPaths

Scenario 2: Save for Repeated Use


# Save functions in: C:\Scripts\lims-functions.ps1

# To use:
. C:\Scripts\lims-functions.ps1
Test-LIMSFileSystemHealth -AutoDiscoverPaths

Scenario 3: Automated Execution


# Create: C:\Scripts\daily-lims-check.ps1

# Schedule with Task Scheduler or run via:
powershell.exe -ExecutionPolicy Bypass -File "C:\Scripts\daily-lims-check.ps1"

---

PowerShell ISE vs Command Line vs VS Code

PowerShell ISE (Integrated Scripting Environment)


# Start Menu → PowerShell ISE
# Great for development and testing
# Can save/load scripts easily
# Built-in help and intellisense

Command Line PowerShell


# Start Menu → PowerShell (or Windows Terminal)
# Good for quick tasks
# Copy/paste functions directly

VS Code with PowerShell Extension


# Best for serious development
# Syntax highlighting, debugging
# Can run functions directly in integrated terminal

---

Running with Parameters (Linux vs PowerShell)

Linux Style:


./script.sh --path /var/log --verbose
./script.sh -p /var/log -v

PowerShell Style:


Test-Function -Path "C:\Logs" -Verbose
Test-Function -Path "C:\Logs" -V  # Short form if supported

# Named parameters (order doesn't matter)
Test-Function -Verbose -Path "C:\Logs"

# Positional parameters
Test-Function "C:\Logs" $true

---

Practical Example: Running LIMS Functions

Step-by-Step Process:

1. Open PowerShell as Administrator


# Right-click Start → Windows PowerShell (Admin)

2. Set execution policy if needed


Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

3. Navigate to your script location


cd C:\Scripts
# or wherever you saved the functions

4. Load the functions


# If functions are in a file:
. .\lims-functions.ps1

# Or copy/paste the function definitions directly

5. Run the functions


# Discover LIMS paths
$paths = Find-LIMSInstallationPaths -Verbose

# Check what was found
$paths.AllUniquePaths

# Run health check
$health = Test-LIMSFileSystemHealth -AutoDiscoverPaths

# View results
$health.Summary
$health.Recommendations

---

Debugging and Troubleshooting

Common Issues:

1. "Execution Policy" Error


Set-ExecutionPolicy Bypass -Scope Process

2. "Function not recognized"


# Function wasn't loaded - reload it
. .\your-script.ps1

3. "Access Denied"


# Run PowerShell as Administrator
# Many LIMS functions need elevated privileges

4. "Path not found"


# Use full paths
Get-ChildItem "C:\Full\Path\To\File"
# Not: Get-ChildItem "~/relative/path"

---

Useful PowerShell Commands for Function Development


# See all loaded functions
Get-Command -CommandType Function

# Get help for a function
Get-Help Test-LIMSFileSystemHealth -Full

# See function definition
${function:Test-LIMSFileSystemHealth}

# Test if a function exists
if (Get-Command "Test-LIMSFileSystemHealth" -ErrorAction SilentlyContinue) {
    Write-Host "Function is loaded"
}

# Clear all functions (start fresh)
Get-ChildItem function: | Remove-Item

# Profile location (like .bashrc)
$PROFILE

---

Comparison: Linux vs PowerShell

| Task | Linux | PowerShell |

|------|-------|------------|

| Make executable | chmod +x script.sh | Set-ExecutionPolicy |

| Run script | ./script.sh | .\script.ps1 |

| Source functions | . ./functions.sh | . .\functions.ps1 |

| Background job | ./script.sh & | Start-Job { .\script.ps1 } |

| Pipe to file | ./script.sh > output.txt | .\script.ps1 > output.txt |

| Run as admin | sudo ./script.sh | Right-click → "Run as Administrator" |

| Environment variables | export VAR=value | $env:VAR = "value" |

| Path separator | / | \ (or \\ in strings) |

---

Quick Start Template

Save this as quick-lims-test.ps1:


# Set execution policy for this session
Set-ExecutionPolicy Bypass -Scope Process -Force

# Your functions go here
function Test-QuickLIMS {
    Write-Host "LIMS Quick Test Running..." -ForegroundColor Green
    
    # Basic system info
    Write-Host "Computer: $env:COMPUTERNAME" -ForegroundColor Cyan
    Write-Host "User: $env:USERNAME" -ForegroundColor Cyan
    Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)" -ForegroundColor Cyan
    
    # Test basic functionality
    $drives = Get-PSDrive -PSProvider FileSystem
    Write-Host "Available drives: $($drives.Name -join ', ')" -ForegroundColor Cyan
}

# Run the test
Test-QuickLIMS

Execute it:


.\quick-lims-test.ps1

This gives you a foundation to build upon and test that PowerShell functions are working correctly!