Last active
May 5, 2025 06:39
-
-
Save davidlu1001/16b77bc30a04342e2fec1350c098853b to your computer and use it in GitHub Desktop.
IISAppPoolMonitor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory = $false)] | |
[ValidateSet("Dev", "Prod")] | |
[string]$Env = "Prod", | |
[Parameter(Mandatory = $false)] | |
[string]$DnsServer = "ServerName1", | |
[Parameter(Mandatory = $false)] | |
[string]$LookupZone = "abc.co.nz", | |
[Parameter(Mandatory = $false)] | |
[ValidateSet("check", "restart")] | |
[string]$Ops = "check", | |
[Parameter(Mandatory = $false)] | |
[string[]]$IisAppPoolPattern = @("LoanAlterations*", "ERA*"), | |
[Parameter(Mandatory = $false)] | |
[string]$LogFilePath = "C:\temp\scripts\DNSFailover.log", | |
[Parameter(Mandatory = $false)] | |
[int]$IisRetryAttempts = 3, | |
[Parameter(Mandatory = $false)] | |
[int]$IisRetryWaitTime = 60, | |
[Parameter(Mandatory=$false)] | |
[switch]$Help | |
) | |
# Function to show help information | |
function Show-Help { | |
$helpText = @" | |
DNS Failover Script Help | |
Description: | |
This script manages restart operations between environments with IIS application pool management. | |
Syntax: | |
.\DNSFailover.ps1 [-Env <String>] [-DnsServer <String>] [-LookupZone <String>] | |
[-Ops <String>] [-IisAppPoolPattern <String[]>] [-LogFilePath <String>] | |
[-IisRetryAttempts <Int>] [-IisRetryWaitTime <Int>] [-Help] | |
Parameters: | |
-Env <String> | |
Specifies the environment to operate in (Dev or Prod). | |
Valid values: Dev, Prod | |
Default: Prod | |
-DnsServer <String> | |
Specifies the DNS server to use for operations. | |
Default: WHATCNAME1 | |
-LookupZone <String> | |
Specifies the DNS lookup zone. | |
Default: abcbank.co.nz | |
-Ops <String> | |
Specifies the operation to perform. | |
Valid values: check, restart | |
Default: check | |
-IisAppPoolPattern <String[]> | |
Pattern(s) to match IIS application pools. Can be a single string or an array of strings. | |
Default: "LoanAlterations*" | |
Example: -IisAppPoolPattern "LoanAlterations*","ERA*" | |
-LogFilePath <String> | |
Path to the log file. | |
Default: C:\temp\scripts\DNSFailover.log | |
-IisRetryAttempts <Int> | |
Number of retry attempts for IIS operations. | |
Default: 3 | |
-IisRetryWaitTime <Int> | |
Wait time between retry attempts in seconds. | |
Default: 60 | |
-Help [Switch] | |
Shows this help message. | |
Examples: | |
# Show help | |
.\DNSFailover.ps1 -Help | |
# Check in Dev environment | |
.\DNSFailover.ps1 -Env Dev -Ops check | |
# Restart IIS App Pools in Prod environment | |
.\DNSFailover.ps1 -Env Prod -Ops restart | |
# Restart multiple IIS app pool patterns | |
.\DNSFailover.ps1 -Env Prod -Ops restart -IisAppPoolPattern "WebApp*","API*" -IisRetryAttempts 5 -IisRetryWaitTime 30 | |
"@ | |
Write-Host $helpText | |
exit 0 | |
} | |
# Show help if requested | |
if ($Help) { | |
Show-Help | |
} | |
# Environment-specific settings | |
$envConfig = @{} | |
# Dev environment settings | |
$devConfig = @{ | |
dnsName = "LendingComplusServerDev2Exp" | |
waitTimeInit = 30 | |
waitTime = 10 | |
maxWaitTime = 600 | |
} | |
$envConfig.Add("Dev", $devConfig) | |
# Prod environment settings | |
$prodConfig = @{ | |
dnsName = "LendingWebServer" | |
waitTimeInit = 30 | |
waitTime = 60 | |
maxWaitTime = 600 | |
} | |
$envConfig.Add("Prod", $prodConfig) | |
# Validate environment selection | |
if (-not $envConfig.ContainsKey($Env)) { | |
Write-Error "Invalid environment specified: $Env. Valid values are: Dev, Prod" | |
exit 1 | |
} | |
# Apply environment-specific configurations | |
$config = $envConfig[$Env] | |
$dnsName = $config.dnsName | |
$waitTimeInit = $config.waitTimeInit | |
$waitTime = $config.waitTime | |
$maxWaitTime = $config.maxWaitTime | |
# Enable strict mode for better error handling | |
Set-StrictMode -Version Latest | |
# Set error action preference to stop script execution on error | |
$ErrorActionPreference = 'Stop' | |
# Function to safely access object properties | |
function Get-SafeProperty { | |
param($object, $propertyName) | |
if (($null -ne $object) -and ($object.PSObject.Properties.Match($propertyName).Count)) { | |
return $object.$propertyName | |
} | |
return $null | |
} | |
# Function to write log messages | |
function Write-Log { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[string]$Message, | |
[Parameter(Mandatory = $false)] | |
[ValidateSet("INFO", "WARNING", "ERROR")] | |
[string]$Level = "INFO" | |
) | |
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" | |
$logMessage = "[$timestamp] [$Level] $Message" | |
# Write to console with appropriate color | |
switch ($Level) { | |
"INFO" { Write-Host $logMessage -ForegroundColor Green } | |
"WARNING" { Write-Host $logMessage -ForegroundColor Yellow } | |
"ERROR" { Write-Host $logMessage -ForegroundColor Red } | |
} | |
# Append to log file | |
try { | |
Add-Content -Path $LogFilePath -Value $logMessage -ErrorAction Stop | |
} catch { | |
Write-Warning "Failed to write to log file: $_" | |
} | |
} | |
# Function to get the active host | |
function Get-ActiveHost { | |
Write-Log "Attempting to get active host for $dnsName" | |
try { | |
$ping = Test-Connection -ComputerName $dnsName -Count 1 -ErrorAction SilentlyContinue | |
if ($ping) { | |
$hostEntry = [System.Net.Dns]::GetHostEntry($ping.Address) | |
$activeHost = $hostEntry.HostName | |
Write-Log "Active host is reachable: $activeHost" | |
return $activeHost | |
} | |
else { | |
# Even if ping fails, we might still get the hostname | |
$pingOutput = Test-Connection -ComputerName $dnsName -Count 1 -ErrorAction SilentlyContinue | Out-String | |
if ($pingOutput -match "Ping.*(\S+).*") { | |
$activeHost = $Matches[1] | |
Write-Log "Host is unreachable, but hostname obtained: $activeHost" -Level "WARNING" | |
return $activeHost | |
} | |
else { | |
throw "Unable to obtain hostname for $dnsName" | |
} | |
} | |
} | |
catch { | |
Write-Log "Failed to resolve active host for $dnsName. Error: $_" -Level "ERROR" | |
return $null | |
} | |
} | |
# Function to manage IIS application pools remotely | |
# Function to manage IIS application pools remotely | |
function Invoke-RemoteIISManagement { | |
param ( | |
[Parameter(Mandatory=$true)] | |
[string]$ComputerName, | |
[Parameter(Mandatory=$true)] | |
[hashtable]$params | |
) | |
$remoteScript = { | |
param($p) | |
# Ensure WebAdministration module is available and loaded | |
if (-not (Get-Module -ListAvailable -Name WebAdministration)) { | |
return @{ | |
Success = $false | |
ErrorMessage = "WebAdministration module is not available on the remote server. Please ensure IIS is installed." | |
Results = $null | |
} | |
} | |
# Import module | |
try { | |
Import-Module WebAdministration -ErrorAction Stop | |
} catch { | |
return @{ | |
Success = $false | |
ErrorMessage = "Failed to import WebAdministration module: $_" | |
Results = $null | |
} | |
} | |
# Get matching application pools based on pattern(s) | |
$appPools = @() | |
foreach ($pattern in $p.patterns) { | |
$matchingPools = Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -like $pattern } | |
if ($matchingPools) { | |
if ($matchingPools -is [array]) { | |
$appPools += $matchingPools | |
} else { | |
$appPools += @($matchingPools) | |
} | |
} | |
} | |
if (-not $appPools -or $appPools.Count -eq 0) { | |
return @{ | |
Success = $false | |
ErrorMessage = "No application pools matching the pattern(s) '$($p.patterns -join "', '")' were found." | |
Results = $null | |
} | |
} | |
# Initialize results | |
$results = @() | |
# Process each application pool | |
foreach ($appPool in $appPools) { | |
$result = @{ | |
AppPoolName = $appPool.Name | |
InitialState = $appPool.State | |
RestartStatus = "Not Attempted" | |
RecycleStatus = "Not Attempted" | |
FinalState = $appPool.State | |
} | |
# Check if app pool is stopped and needs to be started | |
$needsStart = $false | |
if ($appPool.State -eq "Stopped") { | |
$needsStart = $true | |
Write-Output "Application pool '$($appPool.Name)' is currently stopped, will attempt to start it" | |
} | |
if ($p.isRestart -or $needsStart) { | |
$restartSuccess = $false | |
for ($attempt = 1; $attempt -le $p.retryAttempts; $attempt++) { | |
try { | |
Write-Output "Attempt $attempt: Restarting Application Pool: $($appPool.Name)" | |
if ($appPool.State -eq "Started") { | |
Stop-WebAppPool -Name $appPool.Name -ErrorAction Stop | |
} | |
Start-WebAppPool -Name $appPool.Name -ErrorAction Stop | |
# Wait for the app pool to start | |
$waited = 0 | |
do { | |
Start-Sleep -Seconds 1 | |
$waited++ | |
$appPool = Get-Item "IIS:\AppPools\$($appPool.Name)" | |
} while (($appPool.State -ne "Started") -and ($waited -lt $p.maxWait)) | |
# Check start result | |
if ($appPool.State -eq "Started") { | |
$result.RestartStatus = "Success" | |
$restartSuccess = $true | |
Write-Output "Application Pool restarted successfully: $($appPool.Name)" | |
break | |
} else { | |
throw "Application Pool failed to start within $($p.maxWait) seconds" | |
} | |
} catch { | |
# Handle restart errors | |
$result.RestartStatus = "Failed: $_" | |
Write-Warning "Failed to restart Application Pool: $($appPool.Name). Error: $_" | |
if ($attempt -lt $p.retryAttempts) { | |
Write-Output "Waiting $($p.retryWaitTime) seconds before next attempt..." | |
Start-Sleep -Seconds $p.retryWaitTime | |
} | |
} | |
} | |
# Check overall restart success | |
if (-not $restartSuccess) { | |
Write-Error "Failed to restart Application Pool after $($p.retryAttempts) attempts: $($appPool.Name)" | |
# Skip recycling if restart failed | |
$p.isRecycle = $false | |
} | |
} | |
# Recycling (only if pool is in Started state) | |
if ($p.isRecycle -and $appPool.State -eq "Started") { | |
try { | |
Write-Output "Recycling Application Pool: $($appPool.Name)" | |
$appPool.Recycle() | |
# Wait for recycle to complete | |
Start-Sleep -Seconds 5 | |
$appPool = Get-Item "IIS:\AppPools\$($appPool.Name)" | |
$result.RecycleStatus = "Success" | |
Write-Output "Application Pool recycled successfully: $($appPool.Name)" | |
} catch { | |
$result.RecycleStatus = "Failed: $_" | |
Write-Error "Failed to recycle Application Pool: $($appPool.Name). Error: $_" | |
} | |
} elseif ($p.isRecycle -and $appPool.State -ne "Started") { | |
$result.RecycleStatus = "Skipped (Pool not in Started state)" | |
Write-Warning "Skipping recycle for Application Pool: $($appPool.Name) because it is not in Started state" | |
} | |
# Get final state | |
$appPool = Get-Item "IIS:\AppPools\$($appPool.Name)" | |
$result.FinalState = $appPool.State | |
$results += $result | |
} | |
# Return results | |
return @{ | |
Success = $true | |
ErrorMessage = $null | |
Results = $results | |
} | |
} | |
# Execute remote script | |
try { | |
$remoteResult = Invoke-Command -ComputerName $ComputerName -ScriptBlock $remoteScript -ArgumentList $params | |
# Simply return the result directly without processing - the key fix! | |
return $remoteResult | |
} catch { | |
# Handle exception | |
Write-Log "Failed to execute IIS management on remote server $ComputerName. Error: $_" -Level "ERROR" | |
return @{ | |
Success = $false | |
ErrorMessage = "Failed to execute remote command: $_" | |
Results = $null | |
} | |
} | |
} | |
# Main Execution Block | |
try { | |
# Script initialization log | |
Write-Log "Script started with operation: $Ops in $Env environment" | |
Write-Log ("Using configuration: " + ` | |
"DNS Name: $dnsName, " + ` | |
"Initial Wait: $waitTimeInit seconds, " + ` | |
"Check Interval: $waitTime seconds") | |
switch ($Ops) { | |
"check" { | |
$activeHost = Get-ActiveHost | |
if ($activeHost) { | |
Write-Log "Active host check completed: $activeHost" | |
# Check application pool status without restarting | |
$iisParams = @{ | |
patterns = $IisAppPoolPattern | |
isRestart = $false | |
isRecycle = $false | |
maxWait = 120 | |
retryAttempts = $IisRetryAttempts | |
retryWaitTime = $IisRetryWaitTime | |
} | |
# Create and execute a custom remote script just for checking status | |
$checkScript = { | |
param($p) | |
try { | |
Import-Module WebAdministration -ErrorAction Stop | |
$results = @() | |
foreach ($pattern in $p.patterns) { | |
$matchingPools = Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -like $pattern } | |
if ($matchingPools) { | |
if ($matchingPools -is [array]) { | |
foreach ($pool in $matchingPools) { | |
$results += [PSCustomObject]@{ | |
AppPoolName = $pool.Name | |
State = $pool.State | |
AutoStart = $pool.autoStart | |
ProcessModel = @{ | |
IdentityType = $pool.processModel.identityType | |
IdleTimeout = $pool.processModel.idleTimeout | |
} | |
} | |
} | |
} else { | |
$results += [PSCustomObject]@{ | |
AppPoolName = $matchingPools.Name | |
State = $matchingPools.State | |
AutoStart = $matchingPools.autoStart | |
ProcessModel = @{ | |
IdentityType = $matchingPools.processModel.identityType | |
IdleTimeout = $matchingPools.processModel.idleTimeout | |
} | |
} | |
} | |
} | |
} | |
return @{ | |
Success = $true | |
ErrorMessage = $null | |
Results = $results | |
} | |
} catch { | |
return @{ | |
Success = $false | |
ErrorMessage = "Failed to check application pools: $_" | |
Results = $null | |
} | |
} | |
} | |
try { | |
$checkResults = Invoke-Command -ComputerName $activeHost -ScriptBlock $checkScript -ArgumentList $iisParams | |
if ($null -ne $checkResults -and $checkResults.Success -eq $true -and $null -ne $checkResults.Results) { | |
Write-Log "Application Pool Status on $activeHost:" | |
foreach ($result in $checkResults.Results) { | |
Write-Log "App Pool: $($result.AppPoolName)`nState: $($result.State)`nAutoStart: $($result.AutoStart)`nIdentity Type: $($result.ProcessModel.IdentityType)`nIdle Timeout: $($result.ProcessModel.IdleTimeout)" | |
} | |
# Summary of the states | |
$stateCount = $checkResults.Results | Group-Object -Property State | Select-Object Name, Count | |
foreach ($state in $stateCount) { | |
Write-Log "Number of App Pools in '$($state.Name)' state: $($state.Count)" | |
} | |
} else { | |
Write-Log "Failed to retrieve application pool status" -Level "ERROR" | |
if ($checkResults.ErrorMessage) { | |
Write-Log "Error: $($checkResults.ErrorMessage)" -Level "ERROR" | |
} | |
} | |
} catch { | |
Write-Log "Failed to check application pools: $_" -Level "ERROR" | |
} | |
} else { | |
Write-Log "No active host found" -Level "WARNING" | |
} | |
} | |
"restart" { | |
# Restart process | |
Write-Log "Starting application pool restart process in $Env environment" | |
$activeHost = Get-ActiveHost | |
if ($activeHost) { | |
Write-Log "Active host found: $activeHost" | |
Write-Log "Proceeding with IIS Application Pool management on $activeHost" | |
# IIS parameters | |
$iisParams = @{ | |
patterns = $IisAppPoolPattern # Pass the array directly | |
isRestart = $true | |
isRecycle = $true | |
maxWait = 120 | |
retryAttempts = $IisRetryAttempts | |
retryWaitTime = $IisRetryWaitTime | |
} | |
# Execute IIS management | |
$iisResults = Invoke-RemoteIISManagement -ComputerName $activeHost -params $iisParams | |
# Process IIS results | |
if ($null -ne $iisResults -and $iisResults.Success -eq $true -and $null -ne $iisResults.Results) { | |
$successCount = 0 | |
$failCount = 0 | |
foreach ($result in $iisResults.Results) { | |
$restartStatus = $result.RestartStatus | |
$recycleStatus = $result.RecycleStatus | |
$appPoolName = $result.AppPoolName | |
$initialState = $result.InitialState | |
$finalState = $result.FinalState | |
Write-Log "App Pool: $appPoolName`nInitial State: $initialState`nRestart: $restartStatus`nRecycle: $recycleStatus`nFinal State: $finalState" | |
if (($restartStatus -eq "Success" -or $restartStatus -eq "Not Attempted") -and | |
($recycleStatus -eq "Success" -or $recycleStatus -eq "Not Attempted" -or $recycleStatus -eq "Skipped (Pool not in Started state)")) { | |
$successCount++ | |
} else { | |
$failCount++ | |
} | |
} | |
# Final report | |
Write-Log "IIS Application Pool management completed. Success: $successCount, Failed: $failCount" | |
if ($failCount -eq 0 -and $successCount -gt 0) { | |
Write-Log "All IIS Application Pool operations completed successfully." | |
} elseif ($failCount -gt 0) { | |
Write-Log "Some IIS Application Pool operations failed. Please check the logs for details." -Level "WARNING" | |
} else { | |
Write-Log "No successful IIS Application Pool operations were recorded." -Level "WARNING" | |
} | |
} else { | |
# Handle invalid results | |
Write-Log "No valid results returned from IIS Application Pool management." -Level "WARNING" | |
if ($iisResults.ErrorMessage) { | |
Write-Log "Error message: $($iisResults.ErrorMessage)" -Level "ERROR" | |
} | |
} | |
} else { | |
# Get active host failed | |
Write-Log "Failed to get active host, aborting operation" -Level "ERROR" | |
} | |
} | |
default { | |
Write-Log "Invalid operation specified: $Ops" -Level "ERROR" | |
} | |
} | |
} catch { | |
# Global error handling | |
Write-Log "An unexpected error occurred: $_" -Level "ERROR" | |
Write-Log "Use -Help parameter for usage information" -Level "WARNING" | |
exit 1 | |
} finally { | |
# Cleanup | |
Write-Log "Script execution completed for $Env environment." | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment