IIS Monitoring & Utility Functions

Table of Contents

1. [Quick Setup](#quick-setup)

2. [Discovery & Info Functions](#discovery--info-functions)

3. [Website Monitoring Functions](#website-monitoring-functions)

4. [Restart & Recovery Functions](#restart--recovery-functions)

5. [Performance Monitoring](#performance-monitoring)

6. [Log Monitoring Functions](#log-monitoring-functions)

7. [Health Check Functions](#health-check-functions)

8. [Alerting & Notification Functions](#alerting--notification-functions)

9. [Automated Recovery Functions](#automated-recovery-functions)

10. [Bulk Management Functions](#bulk-management-functions)

11. [Usage Examples](#usage-examples)

---

Quick Setup

Add Functions to Your Profile


# Check profile location
$PROFILE

# Edit profile (creates if doesn't exist)
notepad $PROFILE

# Add all functions below to your profile, then reload:
. $PROFILE

Required Modules


# Add to top of profile
Import-Module WebAdministration,IISAdministration -ErrorAction SilentlyContinue

---

Discovery & Info Functions

Complete Site Information


function Get-IISSiteInfo {
    <#
    .SYNOPSIS
    Get comprehensive information about IIS sites
    .PARAMETER SiteName
    Site name or wildcard pattern. Default is all sites.
    .EXAMPLE
    Get-IISSiteInfo "MyWebsite"
    Get-IISSiteInfo "*test*"
    #>
    param([string]$SiteName = "*")
    
    Get-IISSite | Where-Object {$_.Name -like $SiteName} | ForEach-Object {
        $site = $_
        $appPool = Get-IISAppPool $site.Applications[0].ApplicationPoolName -ea 0
        $bindings = Get-IISSiteBinding $site.Name
        $physicalPath = $site.Applications[0].VirtualDirectories[0].PhysicalPath
        
        # Test if site responds
        $httpBinding = $bindings | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
        $siteUrl = if($httpBinding) {
            "http://localhost$($httpBinding.BindingInformation.Split(':')[1])"
        } else { "N/A" }
        
        $isResponding = $false
        if($siteUrl -ne "N/A") {
            try {
                $response = Invoke-WebRequest $siteUrl -UseBasicParsing -TimeoutSec 5 -ea 0
                $isResponding = $response.StatusCode -eq 200
            } catch { }
        }
        
        [PSCustomObject]@{
            SiteName = $site.Name
            SiteID = $site.ID
            SiteState = $site.State
            PhysicalPath = $physicalPath
            PathExists = (Test-Path $physicalPath)
            AppPoolName = $appPool.Name
            AppPoolState = if($appPool){$appPool.State}else{"Unknown"}
            DotNetVersion = if($appPool){$appPool.ManagedRuntimeVersion}else{"Unknown"}
            Bindings = ($bindings | ForEach-Object {"$($_.Protocol):$($_.BindingInformation)"}) -join ", "
            SSL = ($bindings | Where-Object {$_.Protocol -eq "https"}).Count -gt 0
            IsResponding = $isResponding
            TestUrl = $siteUrl
        }
    } | Format-Table -AutoSize
}

Quick Site Status


function Get-SiteStatus {
    <#
    .SYNOPSIS
    Quick status check for specific site
    .PARAMETER SiteName
    Name of the site to check
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName
    )
    
    try {
        $site = Get-IISSite $SiteName -ea Stop
        $appPool = Get-IISAppPool $site.Applications[0].ApplicationPoolName
        $physicalPath = $site.Applications[0].VirtualDirectories[0].PhysicalPath
        
        # Get worker process info
        $workerProcess = Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='w3wp.exe' AND CommandLine LIKE '%$($appPool.Name)%'" -ea 0
        
        Write-Host "=== Site Status: $SiteName ===" -ForegroundColor Cyan
        Write-Host "Site State: $($site.State)" -ForegroundColor $(if($site.State -eq "Started"){"Green"}else{"Red"})
        Write-Host "App Pool: $($appPool.Name) - $($appPool.State)" -ForegroundColor $(if($appPool.State -eq "Started"){"Green"}else{"Red"})
        Write-Host "Physical Path: $physicalPath" -ForegroundColor White
        Write-Host "Path Exists: $(Test-Path $physicalPath)" -ForegroundColor $(if(Test-Path $physicalPath){"Green"}else{"Red"})
        
        if($workerProcess) {
            Write-Host "Worker Process: PID $($workerProcess.ProcessId) - Memory: $([math]::Round($workerProcess.WorkingSetSize/1MB,2))MB" -ForegroundColor Green
        } else {
            Write-Host "Worker Process: Not Running" -ForegroundColor Yellow
        }
        
        # Test HTTP response
        $binding = Get-IISSiteBinding $SiteName | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
        if($binding) {
            $testUrl = "http://localhost$($binding.BindingInformation.Split(':')[1])"
            try {
                $response = Invoke-WebRequest $testUrl -UseBasicParsing -TimeoutSec 5
                Write-Host "HTTP Test: $($response.StatusCode) - $($response.StatusDescription)" -ForegroundColor Green
            } catch {
                Write-Host "HTTP Test: Failed - $($_.Exception.Message)" -ForegroundColor Red
            }
        }
        
    } catch {
        Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
    }
}

---

Website Monitoring Functions

Continuous Site Monitor


function Watch-IISSite {
    <#
    .SYNOPSIS
    Continuously monitor a website with real-time status updates
    .PARAMETER SiteName
    Name of the site to monitor
    .PARAMETER IntervalSeconds
    Check interval in seconds (default: 30)
    .PARAMETER AlertThreshold
    Number of consecutive failures before alert (default: 3)
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [int]$IntervalSeconds = 30,
        [int]$AlertThreshold = 3,
        [switch]$AutoRestart
    )
    
    $failureCount = 0
    $startTime = Get-Date
    $checkCount = 0
    
    Write-Host "Starting monitor for: $SiteName" -ForegroundColor Green
    Write-Host "Check interval: $IntervalSeconds seconds" -ForegroundColor White
    Write-Host "Alert threshold: $AlertThreshold failures" -ForegroundColor White
    Write-Host "Auto-restart: $($AutoRestart.IsPresent)" -ForegroundColor White
    Write-Host "Press Ctrl+C to stop monitoring" -ForegroundColor Yellow
    Write-Host ""
    
    try {
        while($true) {
            $checkCount++
            $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            
            # Check site status
            $site = Get-IISSite $SiteName -ea 0
            $appPool = if($site) { Get-IISAppPool $site.Applications[0].ApplicationPoolName -ea 0 } else { $null }
            
            $siteOk = $site -and $site.State -eq "Started"
            $poolOk = $appPool -and $appPool.State -eq "Started"
            
            # Test HTTP response
            $httpOk = $false
            $responseTime = 0
            if($siteOk) {
                $binding = Get-IISSiteBinding $SiteName | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
                if($binding) {
                    $testUrl = "http://localhost$($binding.BindingInformation.Split(':')[1])"
                    try {
                        $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
                        $response = Invoke-WebRequest $testUrl -UseBasicParsing -TimeoutSec 10
                        $stopwatch.Stop()
                        $responseTime = $stopwatch.ElapsedMilliseconds
                        $httpOk = $response.StatusCode -eq 200
                    } catch {
                        $httpOk = $false
                    }
                }
            }
            
            $allOk = $siteOk -and $poolOk -and $httpOk
            
            if($allOk) {
                $failureCount = 0
                $status = "✓ OK"
                $color = "Green"
            } else {
                $failureCount++
                $status = "✗ FAILED"
                $color = "Red"
            }
            
            # Display status
            $uptimeSpan = (Get-Date) - $startTime
            $uptime = "{0:hh\:mm\:ss}" -f $uptimeSpan
            
            Write-Host "$timestamp | Check #$checkCount | $status | Site:$siteOk Pool:$poolOk HTTP:$httpOk | Response:${responseTime}ms | Failures:$failureCount | Uptime:$uptime" -ForegroundColor $color
            
            # Alert and auto-restart logic
            if($failureCount -ge $AlertThreshold) {
                Write-Host "ALERT: $AlertThreshold consecutive failures detected!" -ForegroundColor Red -BackgroundColor White
                
                if($AutoRestart.IsPresent) {
                    Write-Host "Attempting auto-restart..." -ForegroundColor Yellow
                    try {
                        Restart-IISSiteComplete $SiteName -Force
                        Write-Host "Auto-restart completed" -ForegroundColor Green
                        $failureCount = 0
                    } catch {
                        Write-Host "Auto-restart failed: $($_.Exception.Message)" -ForegroundColor Red
                    }
                }
            }
            
            Start-Sleep $IntervalSeconds
        }
    } catch [System.Management.Automation.PipelineStoppedException] {
        Write-Host "`nMonitoring stopped by user" -ForegroundColor Yellow
    }
}

Site Performance Monitor


function Get-SitePerformance {
    <#
    .SYNOPSIS
    Get real-time performance metrics for a site
    .PARAMETER SiteName
    Name of the site to monitor
    .PARAMETER Samples
    Number of samples to collect (default: 5)
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [int]$Samples = 5
    )
    
    $site = Get-IISSite $SiteName -ea Stop
    $appPoolName = $site.Applications[0].ApplicationPoolName
    
    Write-Host "Collecting $Samples performance samples for: $SiteName" -ForegroundColor Green
    
    $results = @()
    
    for($i = 1; $i -le $Samples; $i++) {
        Write-Progress -Activity "Collecting Performance Data" -Status "Sample $i of $Samples" -PercentComplete (($i / $Samples) * 100)
        
        $timestamp = Get-Date
        
        # Get performance counters
        try {
            $currentConnections = (Get-Counter "\Web Service($SiteName)\Current Connections" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
        } catch { $currentConnections = "N/A" }
        
        try {
            $requestsPerSec = (Get-Counter "\Web Service($SiteName)\Requests/Sec" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
        } catch { $requestsPerSec = "N/A" }
        
        try {
            $queueLength = (Get-Counter "\HTTP Service Request Queues($appPoolName)\CurrentQueueSize" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
        } catch { $queueLength = "N/A" }
        
        # Get worker process info
        $workerProcess = Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='w3wp.exe' AND CommandLine LIKE '%$appPoolName%'" -ea 0
        $cpuPercent = if($workerProcess) { 
            try { (Get-Counter "\Process(w3wp*)\% Processor Time" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue } catch { "N/A" }
        } else { 0 }
        $memoryMB = if($workerProcess) { [math]::Round($workerProcess.WorkingSetSize/1MB,2) } else { 0 }
        
        $results += [PSCustomObject]@{
            Time = $timestamp.ToString("HH:mm:ss")
            CurrentConnections = $currentConnections
            RequestsPerSec = $requestsPerSec
            QueueLength = $queueLength
            CPUPercent = $cpuPercent
            MemoryMB = $memoryMB
        }
        
        if($i -lt $Samples) { Start-Sleep 2 }
    }
    
    Write-Progress -Activity "Collecting Performance Data" -Completed
    
    Write-Host "`nPerformance Summary for: $SiteName" -ForegroundColor Cyan
    $results | Format-Table -AutoSize
    
    # Calculate averages
    $avgConnections = ($results | Where-Object {$_.CurrentConnections -ne "N/A"} | Measure-Object -Property CurrentConnections -Average).Average
    $avgRequests = ($results | Where-Object {$_.RequestsPerSec -ne "N/A"} | Measure-Object -Property RequestsPerSec -Average).Average
    $avgMemory = ($results | Measure-Object -Property MemoryMB -Average).Average
    
    Write-Host "Averages:" -ForegroundColor Yellow
    Write-Host "  Connections: $([math]::Round($avgConnections,2))" -ForegroundColor White
    Write-Host "  Requests/Sec: $([math]::Round($avgRequests,2))" -ForegroundColor White
    Write-Host "  Memory (MB): $([math]::Round($avgMemory,2))" -ForegroundColor White
}

---

Restart & Recovery Functions

Complete Site Restart


function Restart-IISSiteComplete {
    <#
    .SYNOPSIS
    Restart IIS site and its application pool with validation
    .PARAMETER SiteName
    Name of the site to restart
    .PARAMETER RestartAppPool
    Whether to restart the application pool (default: true)
    .PARAMETER WaitSeconds
    Seconds to wait between stop/start (default: 3)
    .PARAMETER Force
    Skip confirmation prompts
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [bool]$RestartAppPool = $true,
        [int]$WaitSeconds = 3,
        [switch]$Force
    )
    
    if(!$Force) {
        $confirm = Read-Host "Restart site '$SiteName'$(if($RestartAppPool){' and app pool'})? (y/N)"
        if($confirm -ne 'y') {
            Write-Host "Operation cancelled" -ForegroundColor Yellow
            return
        }
    }
    
    try {
        $site = Get-IISSite $SiteName -ErrorAction Stop
        $appPoolName = $site.Applications[0].ApplicationPoolName
        
        Write-Host "Starting restart sequence for: $SiteName" -ForegroundColor Cyan
        
        # Stop site
        Write-Host "  Stopping site..." -ForegroundColor Yellow
        Stop-IISSite $SiteName
        Write-Host "  Site stopped" -ForegroundColor Green
        
        if($RestartAppPool) {
            # Restart app pool
            Write-Host "  Restarting application pool: $appPoolName..." -ForegroundColor Yellow
            Restart-WebAppPool $appPoolName
            Write-Host "  App pool restarted" -ForegroundColor Green
        }
        
        # Wait
        if($WaitSeconds -gt 0) {
            Write-Host "  Waiting $WaitSeconds seconds..." -ForegroundColor White
            Start-Sleep $WaitSeconds
        }
        
        # Start site
        Write-Host "  Starting site..." -ForegroundColor Yellow
        Start-IISSite $SiteName
        Write-Host "  Site started" -ForegroundColor Green
        
        # Verify restart
        Start-Sleep 2
        $siteStatus = (Get-IISSite $SiteName).State
        $poolStatus = (Get-IISAppPool $appPoolName).State
        
        if($siteStatus -eq "Started" -and $poolStatus -eq "Started") {
            Write-Host "✓ Restart completed successfully" -ForegroundColor Green
            
            # Test HTTP response
            $binding = Get-IISSiteBinding $SiteName | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
            if($binding) {
                $testUrl = "http://localhost$($binding.BindingInformation.Split(':')[1])"
                try {
                    Start-Sleep 3  # Give site time to initialize
                    $response = Invoke-WebRequest $testUrl -UseBasicParsing -TimeoutSec 10
                    Write-Host "✓ HTTP test successful: $($response.StatusCode)" -ForegroundColor Green
                } catch {
                    Write-Host "⚠ HTTP test failed: $($_.Exception.Message)" -ForegroundColor Yellow
                }
            }
        } else {
            Write-Host "✗ Restart verification failed - Site: $siteStatus, Pool: $poolStatus" -ForegroundColor Red
        }
        
    } catch {
        Write-Host "✗ Restart failed: $($_.Exception.Message)" -ForegroundColor Red
        throw
    }
}

App Pool Only Restart


function Restart-AppPoolOnly {
    <#
    .SYNOPSIS
    Restart only the application pool without touching the site
    .PARAMETER SiteName
    Site name to identify the app pool
    .PARAMETER AppPoolName  
    Or specify app pool name directly
    #>
    param(
        [string]$SiteName,
        [string]$AppPoolName
    )
    
    if($SiteName) {
        $site = Get-IISSite $SiteName -ErrorAction Stop
        $AppPoolName = $site.Applications[0].ApplicationPoolName
    }
    
    if(!$AppPoolName) {
        throw "Must specify either SiteName or AppPoolName"
    }
    
    Write-Host "Restarting application pool: $AppPoolName" -ForegroundColor Cyan
    
    try {
        $oldState = (Get-IISAppPool $AppPoolName).State
        Write-Host "  Current state: $oldState" -ForegroundColor White
        
        Restart-WebAppPool $AppPoolName
        Start-Sleep 2
        
        $newState = (Get-IISAppPool $AppPoolName).State
        Write-Host "  New state: $newState" -ForegroundColor Green
        
        # Check for worker process
        Start-Sleep 3
        $workerProcess = Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='w3wp.exe' AND CommandLine LIKE '%$AppPoolName%'" -ea 0
        if($workerProcess) {
            Write-Host "✓ Worker process started: PID $($workerProcess.ProcessId)" -ForegroundColor Green
        } else {
            Write-Host "⚠ No worker process detected yet" -ForegroundColor Yellow
        }
        
    } catch {
        Write-Host "✗ App pool restart failed: $($_.Exception.Message)" -ForegroundColor Red
        throw
    }
}

Smart Recovery Function


function Invoke-SiteRecovery {
    <#
    .SYNOPSIS
    Intelligent site recovery with progressive escalation
    .PARAMETER SiteName
    Name of the site to recover
    .PARAMETER TestUrl
    Optional custom URL to test (default: auto-detect)
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [string]$TestUrl
    )
    
    Write-Host "Starting recovery process for: $SiteName" -ForegroundColor Cyan
    
    # Get site info
    $site = Get-IISSite $SiteName -ErrorAction Stop
    $appPoolName = $site.Applications[0].ApplicationPoolName
    
    # Auto-detect test URL if not provided
    if(!$TestUrl) {
        $binding = Get-IISSiteBinding $SiteName | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
        if($binding) {
            $TestUrl = "http://localhost$($binding.BindingInformation.Split(':')[1])"
        }
    }
    
    # Test function
    function Test-SiteResponse {
        if(!$TestUrl) { return $true }  # Skip if no URL
        try {
            $response = Invoke-WebRequest $TestUrl -UseBasicParsing -TimeoutSec 10
            return $response.StatusCode -eq 200
        } catch {
            return $false
        }
    }
    
    # Recovery steps with progressive escalation
    $steps = @(
        @{ Name = "Reset App Pool Failures"; Action = { Reset-WebAppPool $appPoolName } },
        @{ Name = "Start App Pool"; Action = { Start-WebAppPool $appPoolName } },
        @{ Name = "Start Site"; Action = { Start-IISSite $SiteName } },
        @{ Name = "Recycle App Pool"; Action = { Restart-WebAppPool $appPoolName } },
        @{ Name = "Full Restart"; Action = { Restart-IISSiteComplete $SiteName -Force } }
    )
    
    foreach($step in $steps) {
        Write-Host "  Attempting: $($step.Name)..." -ForegroundColor Yellow
        
        try {
            & $step.Action
            Start-Sleep 5  # Give time for changes to take effect
            
            if(Test-SiteResponse) {
                Write-Host "✓ Recovery successful with: $($step.Name)" -ForegroundColor Green
                
                # Final verification
                $siteState = (Get-IISSite $SiteName).State
                $poolState = (Get-IISAppPool $appPoolName).State
                Write-Host "  Final status - Site: $siteState, Pool: $poolState" -ForegroundColor White
                
                return $true
            }
        } catch {
            Write-Host "    Failed: $($_.Exception.Message)" -ForegroundColor Red
        }
    }
    
    Write-Host "✗ All recovery attempts failed" -ForegroundColor Red
    return $false
}

---

Performance Monitoring

Live Performance Dashboard


function Show-SitePerformanceDashboard {
    <#
    .SYNOPSIS
    Real-time performance dashboard for a site
    .PARAMETER SiteName
    Name of the site to monitor
    .PARAMETER RefreshSeconds
    Dashboard refresh interval (default: 5)
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [int]$RefreshSeconds = 5
    )
    
    $site = Get-IISSite $SiteName -ErrorAction Stop
    $appPoolName = $site.Applications[0].ApplicationPoolName
    
    Write-Host "Starting performance dashboard for: $SiteName" -ForegroundColor Green
    Write-Host "Press Ctrl+C to stop" -ForegroundColor Yellow
    
    try {
        while($true) {
            Clear-Host
            $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            
            Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
            Write-Host "║                   IIS PERFORMANCE DASHBOARD                  ║" -ForegroundColor Cyan
            Write-Host "╠══════════════════════════════════════════════════════════════╣" -ForegroundColor Cyan
            Write-Host "║ Site: $($SiteName.PadRight(50)) ║" -ForegroundColor Cyan
            Write-Host "║ Time: $($timestamp.PadRight(50)) ║" -ForegroundColor Cyan
            Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
            
            # Site & App Pool Status
            $siteState = (Get-IISSite $SiteName).State
            $poolState = (Get-IISAppPool $appPoolName).State
            
            Write-Host "`nSTATUS:" -ForegroundColor Yellow
            Write-Host "  Site State: $siteState" -ForegroundColor $(if($siteState -eq "Started"){"Green"}else{"Red"})
            Write-Host "  Pool State: $poolState" -ForegroundColor $(if($poolState -eq "Started"){"Green"}else{"Red"})
            
            # Performance Counters
            Write-Host "`nPERFORMANCE:" -ForegroundColor Yellow
            
            try {
                $connections = (Get-Counter "\Web Service($SiteName)\Current Connections" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
                Write-Host "  Current Connections: $connections" -ForegroundColor White
            } catch {
                Write-Host "  Current Connections: N/A" -ForegroundColor Gray
            }
            
            try {
                $requests = (Get-Counter "\Web Service($SiteName)\Requests/Sec" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
                Write-Host "  Requests/Second: $([math]::Round($requests,2))" -ForegroundColor White
            } catch {
                Write-Host "  Requests/Second: N/A" -ForegroundColor Gray
            }
            
            try {
                $queue = (Get-Counter "\HTTP Service Request Queues($appPoolName)\CurrentQueueSize" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
                Write-Host "  Request Queue: $queue" -ForegroundColor $(if($queue -gt 0){"Yellow"}else{"Green"})
            } catch {
                Write-Host "  Request Queue: N/A" -ForegroundColor Gray
            }
            
            # Worker Process Info
            $workerProcess = Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='w3wp.exe' AND CommandLine LIKE '%$appPoolName%'" -ea 0
            
            Write-Host "`nWORKER PROCESS:" -ForegroundColor Yellow
            if($workerProcess) {
                $memoryMB = [math]::Round($workerProcess.WorkingSetSize/1MB,2)
                $startTime = $workerProcess.ConvertToDateTime($workerProcess.CreationDate)
                $uptime = (Get-Date) - $startTime
                
                Write-Host "  Process ID: $($workerProcess.ProcessId)" -ForegroundColor White
                Write-Host "  Memory Usage: ${memoryMB} MB" -ForegroundColor $(if($memoryMB -gt 500){"Yellow"}elseif($memoryMB -gt 1000){"Red"}else{"Green"})
                Write-Host "  Uptime: $($uptime.Days)d $($uptime.Hours)h $($uptime.Minutes)m" -ForegroundColor White
                
                try {
                    $cpu = (Get-Counter "\Process(w3wp*)\% Processor Time" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
                    Write-Host "  CPU Usage: $([math]::Round($cpu,1))%" -ForegroundColor $(if($cpu -gt 80){"Red"}elseif($cpu -gt 50){"Yellow"}else{"Green"})
                } catch {
                    Write-Host "  CPU Usage: N/A" -ForegroundColor Gray
                }
            } else {
                Write-Host "  No worker process running" -ForegroundColor Red
            }
            
            # HTTP Test
            Write-Host "`nHTTP TEST:" -ForegroundColor Yellow
            $binding = Get-IISSiteBinding $SiteName | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
            if($binding) {
                $testUrl = "http://localhost$($binding.BindingInformation.Split(':')[1])"
                try {
                    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
                    $response = Invoke-WebRequest $testUrl -UseBasicParsing -TimeoutSec 5
                    $stopwatch.Stop()
                    $responseTime = $stopwatch.ElapsedMilliseconds
                    Write-Host "  Status: $($response.StatusCode) - $($response.StatusDescription)" -ForegroundColor Green
                    Write-Host "  Response Time: ${responseTime}ms" -ForegroundColor $(if($responseTime -gt 5000){"Red"}elseif($responseTime -gt 1000){"Yellow"}else{"Green"})
                } catch {
                    Write-Host "  Status: Failed - $($_.Exception.Message)" -ForegroundColor Red
                }
            } else {
                Write-Host "  No HTTP binding found" -ForegroundColor Gray
            }
            
            Write-Host "`nRefreshing in $RefreshSeconds seconds..." -ForegroundColor Gray
            Start-Sleep $RefreshSeconds
        }
    } catch [System.Management.Automation.PipelineStoppedException] {
        Write-Host "`nDashboard stopped" -ForegroundColor Yellow
    }
}

---

Log Monitoring Functions

Real-time Log Monitor


function Watch-IISLogs {
    <#
    .SYNOPSIS
    Monitor IIS logs in real-time for a specific site
    .PARAMETER SiteName
    Name of the site to monitor
    .PARAMETER ErrorsOnly
    Show only error entries (4xx, 5xx)
    .PARAMETER Follow
    Continuously follow the log file
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [switch]$ErrorsOnly,
        [switch]$Follow
    )
    
    $site = Get-IISSite $SiteName -ErrorAction Stop
    $siteId = $site.ID
    $logPath = "C:\inetpub\logs\LogFiles\W3SVC$siteId"
    
    if(!(Test-Path $logPath)) {
        throw "Log directory not found: $logPath"
    }
    
    Write-Host "Monitoring logs for: $SiteName (Site ID: $siteId)" -ForegroundColor Green
    Write-Host "Log path: $logPath" -ForegroundColor White
    Write-Host "Errors only: $($ErrorsOnly.IsPresent)" -ForegroundColor White
    Write-Host "Follow mode: $($Follow.IsPresent)" -ForegroundColor White
    Write-Host ""
    
    if($Follow) {
        $latestLog = Get-ChildItem $logPath -Filter "*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
        $initialPosition = $latestLog.Length
        
        Write-Host "Following: $($latestLog.Name)" -ForegroundColor Cyan
        Write-Host "Press Ctrl+C to stop" -ForegroundColor Yellow
        Write-Host ""
        
        try {
            while($true) {
                $currentLog = Get-ChildItem $logPath -Filter "*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
                
                # Check if log file changed
                if($currentLog.Name -ne $latestLog.Name) {
                    Write-Host "New log file: $($currentLog.Name)" -ForegroundColor Yellow
                    $latestLog = $currentLog
                    $initialPosition = 0
                }
                
                # Read new content
                if($currentLog.Length -gt $initialPosition) {
                    $stream = [System.IO.FileStream]::new($currentLog.FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
                    $stream.Seek($initialPosition, [System.IO.SeekOrigin]::Begin) | Out-Null
                    $reader = [System.IO.StreamReader]::new($stream)
                    
                    while(($line = $reader.ReadLine()) -ne $null) {
                        if($line -notlike "#*") {  # Skip comments
                            $fields = $line -split " "
                            if($fields.Count -ge 9) {
                                $timestamp = "$($fields[0]) $($fields[1])"
                                $clientIP = $fields[2]
                                $method = $fields[3]
                                $uriStem = $fields[4]
                                $statusCode = $fields[8]
                                
                                $isError = $statusCode -match "^[45]\d\d$"
                                
                                if(!$ErrorsOnly -or $isError) {
                                    $color = if($isError) {"Red"} elseif($statusCode -eq "200") {"Green"} else {"White"}
                                    Write-Host "$timestamp | $clientIP | $method $uriStem | $statusCode" -ForegroundColor $color
                                }
                            }
                        }
                    }
                    
                    $initialPosition = $stream.Position
                    $reader.Close()
                    $stream.Close()
                }
                
                Start-Sleep 1
            }
        } catch [System.Management.Automation.PipelineStoppedException] {
            Write-Host "`nLog monitoring stopped" -ForegroundColor Yellow
        }
    } else {
        # Show recent entries
        $latestLog = Get-ChildItem $logPath -Filter "*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
        Write-Host "Showing recent entries from: $($latestLog.Name)" -ForegroundColor Cyan
        
        $lines = Get-Content $latestLog.FullName | Where-Object {$_ -notlike "#*"} | Select-Object -Last 50
        
        foreach($line in $lines) {
            $fields = $line -split " "
            if($fields.Count -ge 9) {
                $statusCode = $fields[8]
                $isError = $statusCode -match "^[45]\d\d$"
                
                if(!$ErrorsOnly -or $isError) {
                    $color = if($isError) {"Red"} elseif($statusCode -eq "200") {"Green"} else {"White"}
                    Write-Host $line -ForegroundColor $color
                }
            }
        }
    }
}

---

Health Check Functions

Comprehensive Health Check


function Test-IISSiteHealth {
    <#
    .SYNOPSIS
    Comprehensive health check for IIS site
    .PARAMETER SiteName
    Name of the site to check
    .PARAMETER IncludePerformance
    Include performance metrics in health check
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [switch]$IncludePerformance
    )
    
    $results = [PSCustomObject]@{
        SiteName = $SiteName
        OverallHealth = "Unknown"
        Checks = @()
        Warnings = @()
        Errors = @()
        Recommendations = @()
    }
    
    function Add-HealthCheck {
        param($Name, $Status, $Details, $Type = "Info")
        $results.Checks += [PSCustomObject]@{
            Name = $Name
            Status = $Status
            Details = $Details
            Type = $Type
        }
        if($Type -eq "Error") { $results.Errors += "$Name`: $Details" }
        if($Type -eq "Warning") { $results.Warnings += "$Name`: $Details" }
    }
    
    Write-Host "Running health check for: $SiteName" -ForegroundColor Green
    
    # 1. Site Existence
    try {
        $site = Get-IISSite $SiteName -ErrorAction Stop
        Add-HealthCheck "Site Exists" "✓ Pass" "Site found with ID $($site.ID)" "Info"
    } catch {
        Add-HealthCheck "Site Exists" "✗ Fail" "Site not found" "Error"
        $results.OverallHealth = "Critical"
        return $results
    }
    
    # 2. Site State
    if($site.State -eq "Started") {
        Add-HealthCheck "Site State" "✓ Pass" "Site is started" "Info"
    } else {
        Add-HealthCheck "Site State" "✗ Fail" "Site state: $($site.State)" "Error"
    }
    
    # 3. Application Pool
    $appPoolName = $site.Applications[0].ApplicationPoolName
    $appPool = Get-IISAppPool $appPoolName -ErrorAction SilentlyContinue
    
    if($appPool) {
        if($appPool.State -eq "Started") {
            Add-HealthCheck "App Pool State" "✓ Pass" "$appPoolName is started" "Info"
        } else {
            Add-HealthCheck "App Pool State" "✗ Fail" "$appPoolName state: $($appPool.State)" "Error"
        }
        
        # Check rapid-fail protection
        if($appPool.Failure.RapidFailProtection) {
            $maxCrashes = $appPool.Failure.RapidFailProtectionMaxCrashes
            if($maxCrashes -eq 0) {
                Add-HealthCheck "Rapid-Fail Protection" "✓ Pass" "No recent failures" "Info"
            } else {
                Add-HealthCheck "Rapid-Fail Protection" "⚠ Warning" "$maxCrashes recent failures detected" "Warning"
                $results.Recommendations += "Check application logs for crash causes"
            }
        }
    } else {
        Add-HealthCheck "App Pool" "✗ Fail" "App pool $appPoolName not found" "Error"
    }
    
    # 4. Physical Path
    $physicalPath = $site.Applications[0].VirtualDirectories[0].PhysicalPath
    if(Test-Path $physicalPath) {
        Add-HealthCheck "Physical Path" "✓ Pass" "Path exists: $physicalPath" "Info"
        
        # Check for web.config
        $webConfig = Join-Path $physicalPath "web.config"
        if(Test-Path $webConfig) {
            try {
                [xml]$config = Get-Content $webConfig
                Add-HealthCheck "Web.config" "✓ Pass" "Configuration file is valid XML" "Info"
            } catch {
                Add-HealthCheck "Web.config" "✗ Fail" "Configuration file has XML errors" "Error"
            }
        } else {
            Add-HealthCheck "Web.config" "⚠ Warning" "No web.config found" "Warning"
        }
    } else {
        Add-HealthCheck "Physical Path" "✗ Fail" "Path does not exist: $physicalPath" "Error"
    }
    
    # 5. Bindings and Connectivity
    $bindings = Get-IISSiteBinding $SiteName
    if($bindings) {
        Add-HealthCheck "Site Bindings" "✓ Pass" "$($bindings.Count) binding(s) configured" "Info"
        
        # Test HTTP connectivity
        $httpBinding = $bindings | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
        if($httpBinding) {
            $testUrl = "http://localhost$($httpBinding.BindingInformation.Split(':')[1])"
            try {
                $response = Invoke-WebRequest $testUrl -UseBasicParsing -TimeoutSec 10
                Add-HealthCheck "HTTP Connectivity" "✓ Pass" "HTTP $($response.StatusCode) - $($response.StatusDescription)" "Info"
            } catch {
                Add-HealthCheck "HTTP Connectivity" "✗ Fail" $_.Exception.Message "Error"
            }
        }
        
        # Check HTTPS if configured
        $httpsBinding = $bindings | Where-Object {$_.Protocol -eq "https"} | Select-Object -First 1
        if($httpsBinding) {
            # Check SSL certificate
            try {
                $cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -eq $httpsBinding.certificateHash}
                if($cert) {
                    $daysUntilExpiry = ($cert.NotAfter - (Get-Date)).Days
                    if($daysUntilExpiry -gt 30) {
                        Add-HealthCheck "SSL Certificate" "✓ Pass" "Certificate valid for $daysUntilExpiry days" "Info"
                    } elseif($daysUntilExpiry -gt 0) {
                        Add-HealthCheck "SSL Certificate" "⚠ Warning" "Certificate expires in $daysUntilExpiry days" "Warning"
                        $results.Recommendations += "Renew SSL certificate soon"
                    } else {
                        Add-HealthCheck "SSL Certificate" "✗ Fail" "Certificate expired $([math]::Abs($daysUntilExpiry)) days ago" "Error"
                    }
                } else {
                    Add-HealthCheck "SSL Certificate" "✗ Fail" "Certificate not found in store" "Error"
                }
            } catch {
                Add-HealthCheck "SSL Certificate" "⚠ Warning" "Could not verify certificate" "Warning"
            }
        }
    } else {
        Add-HealthCheck "Site Bindings" "✗ Fail" "No bindings configured" "Error"
    }
    
    # 6. Worker Process
    $workerProcess = Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='w3wp.exe' AND CommandLine LIKE '%$appPoolName%'" -ea 0
    if($workerProcess) {
        $memoryMB = [math]::Round($workerProcess.WorkingSetSize/1MB,2)
        if($memoryMB -lt 1000) {
            Add-HealthCheck "Worker Process Memory" "✓ Pass" "${memoryMB}MB memory usage" "Info"
        } elseif($memoryMB -lt 2000) {
            Add-HealthCheck "Worker Process Memory" "⚠ Warning" "${memoryMB}MB memory usage (high)" "Warning"
            $results.Recommendations += "Monitor memory usage - consider app pool recycling"
        } else {
            Add-HealthCheck "Worker Process Memory" "✗ Fail" "${memoryMB}MB memory usage (very high)" "Error"
            $results.Recommendations += "Investigate memory leaks or increase recycling frequency"
        }
    } else {
        if($site.State -eq "Started" -and $appPool.State -eq "Started") {
            Add-HealthCheck "Worker Process" "⚠ Warning" "No worker process found but site/pool are started" "Warning"
        }
    }
    
    # 7. Performance Checks (if requested)
    if($IncludePerformance) {
        try {
            $connections = (Get-Counter "\Web Service($SiteName)\Current Connections" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
            if($connections -lt 100) {
                Add-HealthCheck "Current Connections" "✓ Pass" "$connections active connections" "Info"
            } elseif($connections -lt 500) {
                Add-HealthCheck "Current Connections" "⚠ Warning" "$connections active connections (moderate load)" "Warning"
            } else {
                Add-HealthCheck "Current Connections" "✗ Fail" "$connections active connections (high load)" "Error"
            }
        } catch {
            Add-HealthCheck "Current Connections" "⚠ Warning" "Performance counter not available" "Warning"
        }
        
        try {
            $queueLength = (Get-Counter "\HTTP Service Request Queues($appPoolName)\CurrentQueueSize" -SampleInterval 1 -MaxSamples 1).CounterSamples[0].CookedValue
            if($queueLength -eq 0) {
                Add-HealthCheck "Request Queue" "✓ Pass" "No requests queued" "Info"
            } elseif($queueLength -lt 10) {
                Add-HealthCheck "Request Queue" "⚠ Warning" "$queueLength requests queued" "Warning"
            } else {
                Add-HealthCheck "Request Queue" "✗ Fail" "$queueLength requests queued (bottleneck detected)" "Error"
                $results.Recommendations += "Investigate performance bottlenecks or increase worker processes"
            }
        } catch {
            Add-HealthCheck "Request Queue" "⚠ Warning" "Performance counter not available" "Warning"
        }
    }
    
    # Determine overall health
    if($results.Errors.Count -gt 0) {
        $results.OverallHealth = "Critical"
    } elseif($results.Warnings.Count -gt 0) {
        $results.OverallHealth = "Warning"
    } else {
        $results.OverallHealth = "Healthy"
    }
    
    # Display results
    Write-Host "`n=== HEALTH CHECK RESULTS ===" -ForegroundColor Cyan
    Write-Host "Site: $SiteName" -ForegroundColor White
    Write-Host "Overall Health: $($results.OverallHealth)" -ForegroundColor $(switch($results.OverallHealth){"Healthy"{"Green"} "Warning"{"Yellow"} "Critical"{"Red"}})
    
    Write-Host "`nCHECKS:" -ForegroundColor Yellow
    $results.Checks | ForEach-Object {
        $color = switch($_.Type) {"Info"{"White"} "Warning"{"Yellow"} "Error"{"Red"}}
        Write-Host "  $($_.Name): $($_.Status) - $($_.Details)" -ForegroundColor $color
    }
    
    if($results.Warnings.Count -gt 0) {
        Write-Host "`nWARNINGS:" -ForegroundColor Yellow
        $results.Warnings | ForEach-Object { Write-Host "  $_" -ForegroundColor Yellow }
    }
    
    if($results.Errors.Count -gt 0) {
        Write-Host "`nERRORS:" -ForegroundColor Red
        $results.Errors | ForEach-Object { Write-Host "  $_" -ForegroundColor Red }
    }
    
    if($results.Recommendations.Count -gt 0) {
        Write-Host "`nRECOMMENDATIONS:" -ForegroundColor Cyan
        $results.Recommendations | ForEach-Object { Write-Host "  $_" -ForegroundColor Cyan }
    }
    
    return $results
}

---

Alerting & Notification Functions

Email Alert Function


function Send-IISAlert {
    <#
    .SYNOPSIS
    Send email alert for IIS issues
    .PARAMETER SiteName
    Name of the affected site
    .PARAMETER AlertType
    Type of alert (Error, Warning, Info)
    .PARAMETER Message
    Alert message
    .PARAMETER SmtpServer
    SMTP server address
    .PARAMETER To
    Recipient email addresses
    .PARAMETER From
    Sender email address
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        [ValidateSet("Error","Warning","Info")]
        [string]$AlertType = "Error",
        [Parameter(Mandatory=$true)]
        [string]$Message,
        [string]$SmtpServer = "localhost",
        [string[]]$To = @("admin@company.com"),
        [string]$From = "iis-monitor@$($env:COMPUTERNAME).company.com"
    )
    
    $subject = "IIS Alert [$AlertType] - $SiteName on $env:COMPUTERNAME"
    $priority = switch($AlertType) {
        "Error" { "High" }
        "Warning" { "Normal" }
        "Info" { "Low" }
    }
    
    $body = @"
IIS Alert Report
================

Server: $env:COMPUTERNAME
Site: $SiteName
Alert Type: $AlertType
Timestamp: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")

Message:
$Message

Site Status:
$(try { Get-SiteStatus $SiteName | Out-String } catch { "Could not retrieve site status" })

-- 
This is an automated message from IIS Monitor
"@

    try {
        Send-MailMessage -SmtpServer $SmtpServer -To $To -From $From -Subject $subject -Body $body -Priority $priority
        Write-Host "Alert sent successfully" -ForegroundColor Green
    } catch {
        Write-Host "Failed to send alert: $($_.Exception.Message)" -ForegroundColor Red
    }
}

---

Automated Recovery Functions

Auto-Recovery Service


function Start-IISAutoRecovery {
    <#
    .SYNOPSIS
    Start automated recovery service for critical sites
    .PARAMETER SiteNames
    Array of site names to monitor
    .PARAMETER CheckInterval
    Check interval in seconds (default: 60)
    .PARAMETER MaxRestartAttempts
    Maximum restart attempts before giving up (default: 3)
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string[]]$SiteNames,
        [int]$CheckInterval = 60,
        [int]$MaxRestartAttempts = 3
    )
    
    $recoveryState = @{}
    foreach($site in $SiteNames) {
        $recoveryState[$site] = @{
            FailureCount = 0
            LastRestart = $null
            RestartAttempts = 0
        }
    }
    
    Write-Host "Starting auto-recovery service for sites: $($SiteNames -join ', ')" -ForegroundColor Green
    Write-Host "Check interval: $CheckInterval seconds" -ForegroundColor White
    Write-Host "Max restart attempts: $MaxRestartAttempts" -ForegroundColor White
    Write-Host "Press Ctrl+C to stop" -ForegroundColor Yellow
    
    try {
        while($true) {
            foreach($siteName in $SiteNames) {
                $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
                
                try {
                    # Health check
                    $site = Get-IISSite $siteName -ea Stop
                    $appPool = Get-IISAppPool $site.Applications[0].ApplicationPoolName -ea 0
                    
                    $siteOk = $site.State -eq "Started"
                    $poolOk = $appPool -and $appPool.State -eq "Started"
                    
                    # HTTP test
                    $httpOk = $false
                    $binding = Get-IISSiteBinding $siteName | Where-Object {$_.Protocol -eq "http"} | Select-Object -First 1
                    if($binding) {
                        $testUrl = "http://localhost$($binding.BindingInformation.Split(':')[1])"
                        try {
                            $response = Invoke-WebRequest $testUrl -UseBasicParsing -TimeoutSec 10
                            $httpOk = $response.StatusCode -eq 200
                        } catch { }
                    }
                    
                    $allOk = $siteOk -and $poolOk -and $httpOk
                    
                    if($allOk) {
                        # Reset failure count on success
                        if($recoveryState[$siteName].FailureCount -gt 0) {
                            Write-Host "$timestamp | $siteName recovered successfully" -ForegroundColor Green
                            $recoveryState[$siteName].FailureCount = 0
                            $recoveryState[$siteName].RestartAttempts = 0
                        }
                    } else {
                        $recoveryState[$siteName].FailureCount++
                        $failCount = $recoveryState[$siteName].FailureCount
                        
                        Write-Host "$timestamp | $siteName failed health check (failure #$failCount) - Site:$siteOk Pool:$poolOk HTTP:$httpOk" -ForegroundColor Red
                        
                        # Attempt recovery after 2 consecutive failures
                        if($failCount -ge 2 -and $recoveryState[$siteName].RestartAttempts -lt $MaxRestartAttempts) {
                            $recoveryState[$siteName].RestartAttempts++
                            $attemptNum = $recoveryState[$siteName].RestartAttempts
                            
                            Write-Host "$timestamp | Attempting auto-recovery for $siteName (attempt $attemptNum/$MaxRestartAttempts)" -ForegroundColor Yellow
                            
                            try {
                                $success = Invoke-SiteRecovery $siteName
                                if($success) {
                                    $recoveryState[$siteName].LastRestart = Get-Date
                                    $recoveryState[$siteName].FailureCount = 0
                                    Write-Host "$timestamp | Auto-recovery successful for $siteName" -ForegroundColor Green
                                    
                                    # Send success notification
                                    # Send-IISAlert -SiteName $siteName -AlertType "Info" -Message "Auto-recovery successful after $attemptNum attempts"
                                } else {
                                    Write-Host "$timestamp | Auto-recovery failed for $siteName" -ForegroundColor Red
                                }
                            } catch {
                                Write-Host "$timestamp | Auto-recovery error for $siteName`: $($_.Exception.Message)" -ForegroundColor Red
                            }
                        } elseif($recoveryState[$siteName].RestartAttempts -ge $MaxRestartAttempts) {
                            if($failCount -eq 2) {  # Send alert only once
                                Write-Host "$timestamp | Max restart attempts reached for $siteName - manual intervention required" -ForegroundColor Red
                                # Send-IISAlert -SiteName $siteName -AlertType "Error" -Message "Site $siteName is down and auto-recovery has failed after $MaxRestartAttempts attempts. Manual intervention required."
                            }
                        }
                    }
                    
                } catch {
                    Write-Host "$timestamp | Error checking $siteName`: $($_.Exception.Message)" -ForegroundColor Red
                }
            }
            
            Start-Sleep $CheckInterval
        }
    } catch [System.Management.Automation.PipelineStoppedException] {
        Write-Host "`nAuto-recovery service stopped" -ForegroundColor Yellow
    }
}

---

Bulk Management Functions

Bulk Site Operations


function Invoke-BulkSiteOperation {
    <#
    .SYNOPSIS
    Perform operations on multiple sites
    .PARAMETER SitePattern
    Wildcard pattern to match site names (default: all sites)
    .PARAMETER Operation
    Operation to perform (Start, Stop, Restart, HealthCheck)
    .PARAMETER Parallel
    Run operations in parallel for faster execution
    #>
    param(
        [string]$SitePattern = "*",
        [ValidateSet("Start","Stop","Restart","HealthCheck","Status")]
        [Parameter(Mandatory=$true)]
        [string]$Operation,
        [switch]$Parallel
    )
    
    $sites = Get-IISSite | Where-Object {$_.Name -like $SitePattern}
    
    if($sites.Count -eq 0) {
        Write-Host "No sites found matching pattern: $SitePattern" -ForegroundColor Yellow
        return
    }
    
    Write-Host "Found $($sites.Count) sites matching pattern: $SitePattern" -ForegroundColor Green
    $sites | ForEach-Object { Write-Host "  - $($_.Name)" -ForegroundColor White }
    
    $confirm = Read-Host "`nProceed with $Operation operation on these sites? (y/N)"
    if($confirm -ne 'y') {
        Write-Host "Operation cancelled" -ForegroundColor Yellow
        return
    }
    
    $scriptBlock = {
        param($siteName, $operation)
        
        try {
            switch($operation) {
                "Start" {
                    Start-IISSite $siteName
                    return [PSCustomObject]@{Site=$siteName; Operation=$operation; Status="Success"; Message="Site started"}
                }
                "Stop" {
                    Stop-IISSite $siteName
                    return [PSCustomObject]@{Site=$siteName; Operation=$operation; Status="Success"; Message="Site stopped"}
                }
                "Restart" {
                    Restart-IISSiteComplete $siteName -Force
                    return [PSCustomObject]@{Site=$siteName; Operation=$operation; Status="Success"; Message="Site restarted"}
                }
                "HealthCheck" {
                    $health = Test-IISSiteHealth $siteName
                    return [PSCustomObject]@{Site=$siteName; Operation=$operation; Status=$health.OverallHealth; Message="Health: $($health.OverallHealth)"}
                }
                "Status" {
                    $site = Get-IISSite $siteName
                    $appPool = Get-IISAppPool $site.Applications[0].ApplicationPoolName
                    return [PSCustomObject]@{Site=$siteName; Operation=$operation; Status="Info"; Message="Site: $($site.State), Pool: $($appPool.State)"}
                }
            }
        } catch {
            return [PSCustomObject]@{Site=$siteName; Operation=$operation; Status="Error"; Message=$_.Exception.Message}
        }
    }
    
    Write-Host "`nExecuting $Operation operation..." -ForegroundColor Cyan
    
    if($Parallel -and $PSVersionTable.PSVersion.Major -ge 7) {
        # PowerShell 7+ parallel execution
        $results = $sites | ForEach-Object -Parallel {
            Import-Module WebAdministration,IISAdministration -ErrorAction SilentlyContinue
            & $using:scriptBlock $_.Name $using:Operation
        } -ThrottleLimit 5
    } else {
        # Sequential execution
        $results = foreach($site in $sites) {
            Write-Progress -Activity "Processing Sites" -Status "Working on: $($site.Name)" -PercentComplete (([Array]::IndexOf($sites, $site) / $sites.Count) * 100)
            & $scriptBlock $site.Name $Operation
        }
        Write-Progress -Activity "Processing Sites" -Completed
    }
    
    # Display results
    Write-Host "`nRESULTS:" -ForegroundColor Yellow
    $results | Format-Table -AutoSize
    
    # Summary
    $successCount = ($results | Where-Object {$_.Status -eq "Success"}).Count
    $errorCount = ($results | Where-Object {$_.Status -eq "Error"}).Count
    
    Write-Host "Summary: $successCount successful, $errorCount failed" -ForegroundColor $(if($errorCount -eq 0){"Green"}else{"Yellow"})
    
    if($errorCount -gt 0) {
        Write-Host "`nErrors:" -ForegroundColor Red
        $results | Where-Object {$_.Status -eq "Error"} | ForEach-Object {
            Write-Host "  $($_.Site): $($_.Message)" -ForegroundColor Red
        }
    }
}

---

Usage Examples

Quick Start Examples


# Basic site information
Get-IISSiteInfo "MyWebsite"
Get-IISSiteInfo "*test*"  # All sites with "test" in the name

# Quick status check
Get-SiteStatus "MyWebsite"

# Complete restart
Restart-IISSiteComplete "MyWebsite"
Restart-IISSiteComplete "MyWebsite" -RestartAppPool $false  # Site only, not app pool

# App pool only restart
Restart-AppPoolOnly -SiteName "MyWebsite"

# Start monitoring
Watch-IISSite "MyWebsite" -IntervalSeconds 30 -AlertThreshold 3 -AutoRestart

# Performance monitoring
Get-SitePerformance "MyWebsite" -Samples 10
Show-SitePerformanceDashboard "MyWebsite" -RefreshSeconds 5

# Health check
Test-IISSiteHealth "MyWebsite" -IncludePerformance

# Log monitoring
Watch-IISLogs "MyWebsite" -ErrorsOnly -Follow

# Smart recovery
Invoke-SiteRecovery "MyWebsite"

# Bulk operations
Invoke-BulkSiteOperation -SitePattern "*prod*" -Operation "HealthCheck"
Invoke-BulkSiteOperation -SitePattern "*test*" -Operation "Restart" -Parallel

# Auto-recovery service
Start-IISAutoRecovery -SiteNames @("CriticalSite1", "CriticalSite2") -CheckInterval 30

Advanced Monitoring Setup


# Set up continuous monitoring for critical sites
$criticalSites = @("MainWebsite", "APIService", "AdminPortal")

# Health check all critical sites
foreach($site in $criticalSites) {
    Write-Host "=== $site ===" -ForegroundColor Cyan
    Test-IISSiteHealth $site -IncludePerformance
    Write-Host ""
}

# Start auto-recovery for critical sites
Start-IISAutoRecovery -SiteNames $criticalSites -CheckInterval 60 -MaxRestartAttempts 3

# Monitor specific site with real-time dashboard
Show-SitePerformanceDashboard "MainWebsite" -RefreshSeconds 10

# Watch logs for errors in real-time
Watch-IISLogs "MainWebsite" -ErrorsOnly -Follow

Scheduled Maintenance


# Create a maintenance script
function Invoke-IISMaintenance {
    Write-Host "Starting IIS maintenance routine..." -ForegroundColor Green
    
    # Health check all sites
    $sites = Get-IISSite
    foreach($site in $sites) {
        Write-Host "Checking: $($site.Name)" -ForegroundColor Yellow
        $health = Test-IISSiteHealth $site.Name
        if($health.OverallHealth -ne "Healthy") {
            Write-Host "  Issues found with $($site.Name)" -ForegroundColor Red
            # Send-IISAlert -SiteName $site.Name -AlertType "Warning" -Message "Health check found issues: $($health.Errors -join ', ')"
        }
    }
    
    # Clean up old log files (older than 30 days)
    $logPath = "C:\inetpub\logs\LogFiles"
    Get-ChildItem $logPath -Recurse -Filter "*.log" | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | Remove-Item -Force
    
    Write-Host "Maintenance completed" -ForegroundColor Green
}

# Run maintenance
Invoke-IISMaintenance

---

To use these functions:

1. Add to PowerShell Profile:


   notepad $PROFILE
   # Paste all functions, save, then:
   . $PROFILE

2. Or save as module:


   # Save all functions to IISUtilities.psm1
   Import-Module .\IISUtilities.psm1

3. Start monitoring immediately:


   Watch-IISSite "MyWebsite" -AutoRestart

These functions provide enterprise-level IIS monitoring and management capabilities with real-time dashboards, automated recovery, health checks, and comprehensive logging - all with simple PowerShell commands!