Skip to content

Instantly share code, notes, and snippets.

@davidlu1001
Last active May 5, 2025 06:39
Show Gist options
  • Save davidlu1001/16b77bc30a04342e2fec1350c098853b to your computer and use it in GitHub Desktop.
Save davidlu1001/16b77bc30a04342e2fec1350c098853b to your computer and use it in GitHub Desktop.
IISAppPoolMonitor
[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