LIMS IIS Troubleshooting Toolkit

Advanced PowerShell scripts for LIMS application support

#region Log Analysis Scripts

1. LIMS-specific error pattern detection


function Find-LIMSErrors {
    param(
        [string]$LogPath = "C:\inetpub\logs\LogFiles\W3SVC1",
        [int]$Hours = 24,
        [string]$OutputPath = "C:\temp\lims_analysis.txt"
    )
    
    $startTime = (Get-Date).AddHours(-$Hours)
    $results = @()
    
    # Common LIMS error patterns
    $patterns = @{
        "Database_Timeout" = "timeout|deadlock|database.*error"
        "File_Upload_Issues" = "upload.*fail|file.*error|413|Request Entity Too Large"
        "Authentication_Problems" = "401|403|authentication.*fail|login.*error"
        "Session_Issues" = "session.*timeout|session.*invalid|viewstate"
        "Integration_Errors" = "instrument.*error|integration.*fail|api.*error"
        "Memory_Issues" = "out.*memory|memory.*exception|w3wp.*crash"
        "Slow_Requests" = "time-taken"  # Will filter >30000ms separately
    }
    
    Get-ChildItem "$LogPath\*.log" | Where-Object { $_.LastWriteTime -ge $startTime } | ForEach-Object {
        $content = Get-Content $_.FullName | Where-Object { $_ -notlike "#*" }
        $fields = (Get-Content $_.FullName | Where-Object { $_ -like "#Fields:*" } | Select-Object -First 1) -replace "#Fields: ", "" -split " "
        
        foreach ($line in $content) {
            $values = $line -split " "
            if ($values.Length -ge $fields.Length) {
                $entry = @{}
                for ($i = 0; $i -lt $fields.Length; $i++) {
                    $entry[$fields[$i]] = $values[$i]
                }
                
                # Check for slow requests (>30 seconds)
                if ($entry['time-taken'] -and [int]$entry['time-taken'] -gt 30000) {
                    $results += [PSCustomObject]@{
                        Timestamp = "$($entry['date']) $($entry['time'])"
                        Type = "Slow_Request"
                        Details = "URL: $($entry['cs-uri-stem']) | Time: $($entry['time-taken'])ms | Status: $($entry['sc-status'])"
                        IP = $entry['c-ip']
                        UserAgent = $entry['cs(User-Agent)']
                    }
                }
                
                # Check for error patterns
                foreach ($pattern in $patterns.GetEnumerator()) {
                    $lineText = $line.ToLower()
                    if ($lineText -match $pattern.Value -and $entry['sc-status'] -match "^[45]") {
                        $results += [PSCustomObject]@{
                            Timestamp = "$($entry['date']) $($entry['time'])"
                            Type = $pattern.Key
                            Details = "URL: $($entry['cs-uri-stem']) | Status: $($entry['sc-status']) | Bytes: $($entry['sc-bytes'])"
                            IP = $entry['c-ip']
                            UserAgent = $entry['cs(User-Agent)']
                        }
                    }
                }
            }
        }
    }
    
    # Generate report
    $report = @"
LIMS Troubleshooting Report - Last $Hours Hours
Generated: $(Get-Date)
========================================

SUMMARY:
"@
    
    $results | Group-Object Type | Sort-Object Count -Descending | ForEach-Object {
        $report += "`n$($_.Name): $($_.Count) occurrences"
    }
    
    $report += "`n`nDETAILED ERRORS:`n"
    $results | Sort-Object Timestamp -Descending | ForEach-Object {
        $report += "$($_.Timestamp) [$($_.Type)] $($_.Details) (IP: $($_.IP))`n"
    }
    
    $report | Out-File $OutputPath
    Write-Host "Report saved to: $OutputPath" -ForegroundColor Green
    return $results
}

2. Database connection monitoring


function Test-LIMSDatabase {
    param(
        [string]$ConnectionString,
        [string]$TestQuery = "SELECT 1"
    )
    
    try {
        $connection = New-Object System.Data.SqlClient.SqlConnection($ConnectionString)
        $connection.Open()
        
        $command = New-Object System.Data.SqlClient.SqlCommand($TestQuery, $connection)
        $result = $command.ExecuteScalar()
        
        $connection.Close()
        
        return [PSCustomObject]@{
            Status = "Success"
            ResponseTime = (Measure-Command { $command.ExecuteScalar() }).TotalMilliseconds
            Timestamp = Get-Date
        }
    }
    catch {
        return [PSCustomObject]@{
            Status = "Failed"
            Error = $_.Exception.Message
            Timestamp = Get-Date
        }
    }
}

3. Application pool monitoring


function Monitor-LIMSAppPool {
    param(
        [string]$AppPoolName,
        [int]$IntervalSeconds = 30,
        [int]$DurationMinutes = 60
    )
    
    Import-Module WebAdministration
    $endTime = (Get-Date).AddMinutes($DurationMinutes)
    $results = @()
    
    while ((Get-Date) -lt $endTime) {
        $pool = Get-IISAppPool -Name $AppPoolName
        $process = Get-Process -Name "w3wp" | Where-Object { 
            (Get-WmiObject Win32_Process -Filter "ProcessId = $($_.Id)").CommandLine -like "*$AppPoolName*" 
        }
        
        $result = [PSCustomObject]@{
            Timestamp = Get-Date
            State = $pool.State
            ProcessId = if ($process) { $process.Id } else { "N/A" }
            WorkingSet = if ($process) { [math]::Round($process.WorkingSet64 / 1MB, 2) } else { 0 }
            CPU = if ($process) { $process.CPU } else { 0 }
            Handles = if ($process) { $process.Handles } else { 0 }
            Threads = if ($process) { $process.Threads.Count } else { 0 }
        }
        
        $results += $result
        Write-Host "$(Get-Date -Format 'HH:mm:ss') - Pool: $($result.State) | Memory: $($result.WorkingSet)MB | PID: $($result.ProcessId)"
        
        Start-Sleep $IntervalSeconds
    }
    
    return $results
}

4. File system monitoring for LIMS uploads/temp files


function Monitor-LIMSFileSystem {
    param(
        [string[]]$Paths = @("C:\inetpub\wwwroot\uploads", "C:\Windows\Temp", "C:\inetpub\temp"),
        [int]$ThresholdMB = 1000
    )
    
    $results = @()
    
    foreach ($path in $Paths) {
        if (Test-Path $path) {
            $files = Get-ChildItem $path -Recurse -File | Where-Object { $_.LastWriteTime -gt (Get-Date).AddHours(-24) }
            $totalSize = ($files | Measure-Object Length -Sum).Sum / 1MB
            $oldFiles = Get-ChildItem $path -Recurse -File | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) }
            
            $results += [PSCustomObject]@{
                Path = $path
                RecentFiles = $files.Count
                TotalSizeMB = [math]::Round($totalSize, 2)
                OldFiles = $oldFiles.Count
                Status = if ($totalSize -gt $ThresholdMB) { "WARNING" } else { "OK" }
            }
        }
    }
    
    return $results
}

5. Real-time error monitoring


function Start-LIMSMonitoring {
    param(
        [string]$LogPath = "C:\inetpub\logs\LogFiles\W3SVC1",
        [string[]]$AlertPatterns = @("500", "timeout", "error", "exception", "fail")
    )
    
    $logFile = Get-ChildItem $LogPath -Filter "*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
    
    Write-Host "Monitoring LIMS errors in real-time..." -ForegroundColor Yellow
    Write-Host "Log file: $($logFile.FullName)" -ForegroundColor Cyan
    Write-Host "Press Ctrl+C to stop monitoring" -ForegroundColor Gray
    
    Get-Content $logFile.FullName -Tail 0 -Wait | ForEach-Object {
        $line = $_
        foreach ($pattern in $AlertPatterns) {
            if ($line -match $pattern) {
                $timestamp = Get-Date -Format "HH:mm:ss"
                Write-Host "[$timestamp] ALERT: $line" -ForegroundColor Red
                
                # Log to file for later analysis
                "[$timestamp] $line" | Add-Content "C:\temp\lims_alerts.log"
                break
            }
        }
    }
}

#endregion

#region Performance Analysis

6. Response time analysis


function Analyze-LIMSPerformance {
    param(
        [string]$LogPath = "C:\inetpub\logs\LogFiles\W3SVC1",
        [int]$Hours = 24
    )
    
    $startTime = (Get-Date).AddHours(-$Hours)
    $slowRequests = @()
    
    Get-ChildItem "$LogPath\*.log" | Where-Object { $_.LastWriteTime -ge $startTime } | ForEach-Object {
        $content = Get-Content $_.FullName
        $fields = ($content | Where-Object { $_ -like "#Fields:*" } | Select-Object -First 1) -replace "#Fields: ", "" -split " "
        
        $content | Where-Object { $_ -notlike "#*" -and $_ -ne "" } | ForEach-Object {
            $values = $_ -split " "
            if ($values.Length -ge $fields.Length) {
                $entry = @{}
                for ($i = 0; $i -lt $fields.Length; $i++) {
                    $entry[$fields[$i]] = $values[$i]
                }
                
                if ($entry['time-taken'] -and [int]$entry['time-taken'] -gt 5000) {
                    $slowRequests += [PSCustomObject]@{
                        URL = $entry['cs-uri-stem']
                        TimeTaken = [int]$entry['time-taken']
                        Status = $entry['sc-status']
                        Method = $entry['cs-method']
                        IP = $entry['c-ip']
                        Timestamp = "$($entry['date']) $($entry['time'])"
                    }
                }
            }
        }
    }
    
    # Analysis
    Write-Host "`nSLOW REQUEST ANALYSIS (>5 seconds):" -ForegroundColor Yellow
    $slowRequests | Group-Object URL | Sort-Object Count -Descending | Select-Object -First 10 | ForEach-Object {
        $avg = ($_.Group | Measure-Object TimeTaken -Average).Average
        Write-Host "$($_.Name): $($_.Count) requests, avg: $([math]::Round($avg/1000,2))s" -ForegroundColor Cyan
    }
    
    return $slowRequests
}

7. Memory usage tracking


function Get-LIMSMemoryUsage {
    param(
        [string]$AppPoolName,
        [int]$Samples = 10,
        [int]$IntervalSeconds = 30
    )
    
    $results = @()
    
    for ($i = 1; $i -le $Samples; $i++) {
        $process = Get-Process -Name "w3wp" | Where-Object { 
            (Get-WmiObject Win32_Process -Filter "ProcessId = $($_.Id)").CommandLine -like "*$AppPoolName*" 
        }
        
        if ($process) {
            $results += [PSCustomObject]@{
                Sample = $i
                Timestamp = Get-Date
                WorkingSetMB = [math]::Round($process.WorkingSet64 / 1MB, 2)
                PrivateMemoryMB = [math]::Round($process.PrivateMemorySize64 / 1MB, 2)
                VirtualMemoryMB = [math]::Round($process.VirtualMemorySize64 / 1MB, 2)
                PagedMemoryMB = [math]::Round($process.PagedMemorySize64 / 1MB, 2)
                Handles = $process.Handles
                Threads = $process.Threads.Count
            }
            
            Write-Host "Sample $i/$Samples - Working Set: $([math]::Round($process.WorkingSet64 / 1MB, 2))MB"
        }
        
        if ($i -lt $Samples) { Start-Sleep $IntervalSeconds }
    }
    
    return $results
}

#endregion

#region Network and Dependencies

8. Test external dependencies (common for LIMS)


function Test-LIMSDependencies {
    param(
        [hashtable]$Dependencies = @{
            "Database" = @{ Type = "SQL"; Server = "localhost"; Port = 1433 }
            "FileServer" = @{ Type = "SMB"; Server = "fileserver.domain.com"; Share = "\\fileserver\lims" }
            "EmailServer" = @{ Type = "SMTP"; Server = "mail.domain.com"; Port = 25 }
            "WebService" = @{ Type = "HTTP"; URL = "https://api.vendor.com/health" }
        }
    )
    
    $results = @()
    
    foreach ($dep in $Dependencies.GetEnumerator()) {
        $name = $dep.Key
        $config = $dep.Value
        
        try {
            switch ($config.Type) {
                "SQL" {
                    $connection = New-Object System.Net.Sockets.TcpClient
                    $connection.Connect($config.Server, $config.Port)
                    $connection.Close()
                    $status = "Available"
                }
                "SMB" {
                    $test = Test-Path $config.Share
                    $status = if ($test) { "Available" } else { "Unavailable" }
                }
                "SMTP" {
                    $connection = New-Object System.Net.Sockets.TcpClient
                    $connection.Connect($config.Server, $config.Port)
                    $connection.Close()
                    $status = "Available"
                }
                "HTTP" {
                    $response = Invoke-WebRequest $config.URL -UseBasicParsing -TimeoutSec 10
                    $status = if ($response.StatusCode -eq 200) { "Available" } else { "Error: $($response.StatusCode)" }
                }
            }
        }
        catch {
            $status = "Failed: $($_.Exception.Message)"
        }
        
        $results += [PSCustomObject]@{
            Dependency = $name
            Type = $config.Type
            Status = $status
            Timestamp = Get-Date
        }
    }
    
    return $results
}

#endregion

#region Event Log Analysis

9. Windows Event Log analysis for LIMS


function Get-LIMSEventLogs {
    param(
        [int]$Hours = 24,
        [string[]]$LogNames = @("Application", "System", "Security")
    )
    
    $startTime = (Get-Date).AddHours(-$Hours)
    $events = @()
    
    foreach ($logName in $LogNames) {
        $logEvents = Get-WinEvent -FilterHashtable @{
            LogName = $logName
            StartTime = $startTime
            Level = @(1,2,3)  # Critical, Error, Warning
        } -ErrorAction SilentlyContinue | Where-Object {
            $_.Message -match "IIS|w3wp|application pool|ASP.NET|SQL|LIMS" -or
            $_.Id -in @(1000, 1001, 1002, 1309, 2001, 2002, 5011, 5186)
        }
        
        $events += $logEvents
    }
    
    return $events | Sort-Object TimeCreated -Descending | Select-Object -First 50
}

#endregion

Usage Examples and Quick Commands:

<#

QUICK TROUBLESHOOTING WORKFLOW:

1. Check for recent errors:

Find-LIMSErrors -Hours 2

2. Monitor app pool health:

Monitor-LIMSAppPool -AppPoolName "YourLIMSAppPool" -DurationMinutes 10

3. Test dependencies:

Test-LIMSDependencies

4. Check performance:

Analyze-LIMSPerformance -Hours 4

5. Real-time monitoring:

Start-LIMSMonitoring

6. Check system events:

Get-LIMSEventLogs -Hours 4 | Format-Table TimeCreated, LevelDisplayName, Id, Message -Wrap

#>