alexsusanu@docs:LIMS Path Discovery and Generic File System Health $
alexsusanu@docs
:~$ cat LIMS Path Discovery and Generic File System Health.md

HomeLIMS → LIMS Path Discovery and Generic File System Health

LIMS Path Discovery and Generic File System Health

Functions to discover LIMS installation paths and perform health checks without assuming specific directory structures.

LIMS Path Discovery Function

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
            }
        }
    }

    # 5. Look for common LIMS-related directories
    Write-Host "Searching for common LIMS directories..." -ForegroundColor Cyan
    $searchRoots = @("C:\", "D:\", "E:\")

    foreach ($root in $searchRoots) {
        if (Test-Path $root) {
            # Look for data directories
            $dataPatterns = @("*Data*", "*Database*", "*Files*", "*Storage*")
            foreach ($pattern in $dataPatterns) {
                $found = Get-ChildItem $root -Directory -Name $pattern -ErrorAction SilentlyContinue
                foreach ($folder in $found) {
                    $fullPath = Join-Path $root $folder
                    # Check if it contains LIMS-like files
                    $hasDataFiles = Get-ChildItem $fullPath -Recurse -Directory -ErrorAction SilentlyContinue |
                                   Where-Object { $_.Name -match "LIMS|Lab|Sample|Result|Instrument|Reports" } |
                                   Select-Object -First 1
                    if ($hasDataFiles) {
                        $discoveredPaths.DataDirectories += $fullPath
                        if ($Verbose) { Write-Host "  Found data dir: $fullPath" -ForegroundColor Gray }
                    }
                }
            }
        }
    }

    # 6. Consolidate and deduplicate all paths
    $allPaths = @()
    $allPaths += $discoveredPaths.ProgramFiles
    $allPaths += $discoveredPaths.IISPaths
    $allPaths += $discoveredPaths.ServicePaths
    $allPaths += $discoveredPaths.RegistryPaths
    $allPaths += $discoveredPaths.DataDirectories

    $discoveredPaths.AllUniquePaths = $allPaths | Sort-Object -Unique | Where-Object { $_ -and (Test-Path $_) }

    # 7. Find related directories (Config, Logs, Temp) for each discovered path
    foreach ($basePath in $discoveredPaths.AllUniquePaths) {
        # Look for config files
        $configFiles = Get-ChildItem $basePath -Recurse -File -Include "*.config", "*.xml", "*.ini", "*.properties" -ErrorAction SilentlyContinue |
                      Select-Object -First 10
        $discoveredPaths.ConfigFiles += $configFiles.DirectoryName | Sort-Object -Unique

        # 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.ConfigFiles = $discoveredPaths.ConfigFiles | Sort-Object -Unique | Where-Object { $_ }
    $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

    if ($discoveredPaths.AllUniquePaths.Count -eq 0) {
        Write-Host "  No LIMS installations found. Try running with -Verbose to see search details." -ForegroundColor Yellow
        Write-Host "  You may need to specify paths manually in other functions." -ForegroundColor Yellow
    }

    return $discoveredPaths
}

Updated Generic File System Health Function

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
            $pathsToCheck += $discoveredPaths.ConfigFiles
        }
        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 (with size limits for performance)
                $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
                }
            }
        }
        else {
            $results.PathChecks += [PSCustomObject]@{
                Path = $path
                Exists = $false
                PathType = "Missing"
            }
        }
    }

    # Generate summary statistics
    $results.Summary = @{
        TotalPaths = $pathsToCheck.Count
        ExistingPaths = ($results.PathChecks | Where-Object { $_.Exists }).Count
        WritablePaths = ($results.PathChecks | Where-Object { $_.IsWritable }).Count
        LargeFolders = $results.LargeFolders.Count
        PathsWithOldFiles = $results.OldFiles.Count
        TotalSizeMB = [math]::Round(($results.PathChecks | Where-Object { $_.TotalSizeMB } | Measure-Object TotalSizeMB -Sum).Sum, 2)
        PathTypes = $results.PathChecks | Group-Object PathType | ForEach-Object { "$($_.Name): $($_.Count)" }
    }

    # Display summary
    Write-Host "`nFile System Health Summary:" -ForegroundColor Green
    Write-Host "  Total paths checked: $($results.Summary.TotalPaths)" -ForegroundColor Cyan
    Write-Host "  Existing paths: $($results.Summary.ExistingPaths)" -ForegroundColor Cyan
    Write-Host "  Writable paths: $($results.Summary.WritablePaths)" -ForegroundColor Cyan
    Write-Host "  Total size: $($results.Summary.TotalSizeMB)MB" -ForegroundColor Cyan

    Write-Host "`nPath breakdown:" -ForegroundColor Cyan
    $results.Summary.PathTypes | ForEach-Object { Write-Host "  $_" -ForegroundColor Gray }

    Write-Host "`nPath Status:" -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
    }

    if ($results.LargeFolders) {
        Write-Host "`nLarge folders (>$LargeFolderSizeMB MB):" -ForegroundColor Yellow
        $results.LargeFolders | ForEach-Object {
            Write-Host "  $($_.Path): $($_.TotalSizeMB)MB ($($_.FileCount) files)" -ForegroundColor Cyan
        }
    }

    if ($results.OldFiles) {
        Write-Host "`nDirectories with old files (>$OldFileDays days):" -ForegroundColor Yellow
        $results.OldFiles | ForEach-Object {
            Write-Host "  $($_.Path): $($_.OldFileSizeMB)MB ($($_.OldFileCount) files, oldest: $($_.OldestFile))" -ForegroundColor Cyan
        }
    }

    if ($results.Recommendations) {
        Write-Host "`nRecommendations:" -ForegroundColor Yellow
        $results.Recommendations | ForEach-Object {
            Write-Host "  • $_" -ForegroundColor Cyan
        }
    }

    return $results
}

Usage Examples

# 1. Discover LIMS paths first
$limsPaths = Find-LIMSInstallationPaths -Verbose

# 2. Use discovered paths for health check
Test-LIMSFileSystemHealth -AutoDiscoverPaths

# 3. Manual paths if auto-discovery doesn't work
Test-LIMSFileSystemHealth -CriticalPaths @("C:\YourLIMS", "D:\Data") -AutoDiscoverPaths:$false

# 4. Combine auto-discovery with additional specific paths
Test-LIMSFileSystemHealth -AutoDiscoverPaths -AdditionalPaths @("\\NetworkShare\LIMSData")

# 5. Just check standard Windows paths (no LIMS discovery)
Test-LIMSFileSystemHealth -AutoDiscoverPaths:$false -IncludeStandardPaths

# 6. Discover paths and show what was found
$paths = Find-LIMSInstallationPaths -Verbose
$paths.AllUniquePaths | ForEach-Object { Write-Host "Found: $_" }

Key Improvements:

  1. Path Discovery First: Find-LIMSInstallationPaths searches multiple sources (Program Files, IIS, Services, Registry)
  2. No Hardcoded Paths: Discovers actual LIMS installation locations
  3. Multiple Search Methods: Program files, IIS sites, Windows services, Registry entries
  4. Path Type Classification: Identifies Logs, Temp, Data, Config, Program directories
  5. Performance Optimized: Limits file scanning to prevent hanging on huge directories
  6. Flexible Usage: Can use auto-discovery, manual paths, or combination
  7. Better Recommendations: Context-aware suggestions based on path types

This approach works for any LIMS installation without assuming specific directory structures!

Key Improvements in the Updated Version:
1. Path Discovery Function (Find-LIMSInstallationPaths)
Searches multiple sources:

Program Files - Looks for LIMS patterns and vendor names (Thermo, Waters, Agilent, etc.)
IIS Sites - Finds web applications with LIMS-related names
Windows Services - Discovers services pointing to LIMS executables
Registry - Checks uninstall entries for LIMS software
File System - Scans for directories with LIMS-like file structures

  1. Generic File System Health (Test-LIMSFileSystemHealth)
    No hardcoded assumptions:

Auto-discovery by default - Uses the discovery function automatically
Manual override - Can specify exact paths if needed
Path type classification - Identifies Logs, Temp, Data, Config, Program directories
Performance optimized - Limits scanning to prevent hanging on huge directories

  1. Flexible Usage Patterns

Auto-discover everything (recommended)

Test-LIMSFileSystemHealth -AutoDiscoverPaths

If auto-discovery fails, specify manually

Test-LIMSFileSystemHealth -CriticalPaths @("C:\YourSpecificLIMS", "D:\Data") -AutoDiscoverPaths:$false

Combine both approaches

Test-LIMSFileSystemHealth -AutoDiscoverPaths -AdditionalPaths @("\NetworkShare\LIMSData")

See what paths were discovered first

$paths = Find-LIMSInstallationPaths -Verbose
$paths.AllUniquePaths

  1. Real-World Examples
    This approach will find LIMS installations like:

C:\Program Files\Thermo Fisher\SampleManager
C:\Program Files (x86)\Waters\LIMS
D:\Applications\YourCustomLIMS
C:\inetpub\wwwroot\StarLIMS
Vendor-specific locations discovered via services/registry

The discovery function is intelligent enough to find LIMS installations regardless of vendor, version, or custom installation paths

Last updated: 2025-08-26 20:00 UTC