Created
March 13, 2026 13:28
-
-
Save figueroadavid/d203df08133ee2b78e73ced2efb2ac57 to your computer and use it in GitHub Desktop.
Checks the status of the EPS Recovery script and emails the results to the administrator
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
| #Requires -RunAsAdministrator | |
| function Test-WillowEPSStatus { | |
| <# | |
| .SYNOPSIS | |
| Sends an email showing the status of the Willow EPS servers based on the | |
| WamEPSRecovery script | |
| .DESCRIPTION | |
| The script reads the WamEpsRecoveryConfig.xml file to determine the list of EPS servers, | |
| retrieves the failover status and print-related service statuses from each server, and compiles | |
| this information into an HTML-formatted email report. | |
| The email is then sent to the specified recipient(s) using the provided SMTP server. | |
| .NOTES | |
| The script must be run with appropriate permissions to access the XML file and query the remote servers. | |
| Ensure that PowerShell remoting is enabled and properly configured on all target servers. | |
| .PARAMETER EPSXMLFilePath | |
| The file path to the WamEpsRecoveryConfig.xml file containing the EPS server configuration. | |
| .PARAMETER SMTPTo | |
| The email address(es) of the recipient(s) for the status report. | |
| .PARAMETER SMTPFrom | |
| The email address of the sender for the status report. | |
| .PARAMETER SMTPSubject | |
| The subject line for the status report email. | |
| .PARAMETER SMTPServer | |
| The SMTP server to use for sending the email. | |
| .EXAMPLE | |
| PS C:\> $WillowEPSStatusParams = @{ | |
| EPSXMLFilePath = 'C:\EPSRecovery\WamEpsRecoveryConfig.xml' | |
| SMTPTo = 'administrator@domain.tld' | |
| SMTPFrom = 'EPSRecoveryXML_{0}@domain.tld' -f $env:COMPUTERNAME | |
| SMTPSubject = 'Willow EPS Recovery XML Status - {0}' -f $env:COMPUTERNAME | |
| SMTPServer = 'smtp.domain.tld' | |
| } | |
| PS C:\> Test-WillowEPSStatus @WillowEPSStatusParams | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| [parameter()] | |
| [ValidateScript({ Test-Path -LiteralPath $_ })] | |
| [string]$EPSXMLFilePath = 'C:\EPSRecovery\WamEpsRecoveryConfig.xml', | |
| [parameter()] | |
| [string]$SMTPTo = 'administrator@domain.tld', | |
| [parameter()] | |
| [string]$SMTPFrom = ('EPSRecoveryXML_{0}@domain.tld' -f $env:COMPUTERNAME), | |
| [parameter()] | |
| [string]$SMTPSubject = ('Willow EPS Recovery XML Status - {0}' -f $env:COMPUTERNAME), | |
| [parameter()] | |
| [string]$SMTPServer = 'smtp.domain.tld' | |
| ) | |
| begin { | |
| function ConvertTo-HtmlSafeText { | |
| [CmdletBinding()] | |
| param([AllowNull()][string]$Text) | |
| if ($null -eq $Text) { return '' } | |
| return [System.Security.SecurityElement]::Escape($Text) | |
| } | |
| function ConvertTo-FailoverStatus { | |
| [CmdletBinding()] | |
| param([AllowNull()][string]$Status) | |
| $s = '' | |
| if ($null -ne $Status) { $s = $Status.Trim() } | |
| if ($s -eq 'Active' -or $s -eq 'Passive') { return $s } | |
| return 'Unknown' | |
| } | |
| function ConvertTo-ServiceStatus { | |
| [CmdletBinding()] | |
| param([AllowNull()]$Status) | |
| # ServiceControllerStatus enum or string. We normalize to: Running / Stopped / Unknown | |
| if ($null -eq $Status) { return 'Unknown' } | |
| $s = $Status.ToString().Trim() | |
| switch ($s) { | |
| 'Running' { 'Running' } | |
| 'Stopped' { 'Stopped' } | |
| default { 'Unknown' } | |
| } | |
| } | |
| $Css = @" | |
| body { | |
| font-family: Segoe UI, Arial, sans-serif; | |
| font-size: 12pt; | |
| color: #000000; | |
| } | |
| h2 { | |
| color: #0078D4; | |
| margin: 0 0 10px 0; | |
| } | |
| h3.path { | |
| font-family: Consolas, "Courier New", monospace; | |
| font-size: 11pt; | |
| font-weight: normal; | |
| margin: 0 0 15px 0; | |
| } | |
| table.status { | |
| border-collapse: collapse; | |
| border: 1px solid #CCCCCC; | |
| width: 100%; | |
| max-width: 900px; | |
| } | |
| table.status th, | |
| table.status td { | |
| border: 1px solid #CCCCCC; | |
| padding: 6px; | |
| text-align: left; | |
| vertical-align: top; | |
| white-space: nowrap; | |
| } | |
| table.status th { | |
| font-weight: bold; | |
| background-color: #E5E5E5; | |
| } | |
| /* Alternating rows */ | |
| tr.row-odd { background-color: #FFFFFF; } | |
| tr.row-even { background-color: #F2F2F2; } | |
| /* Failover status coloring */ | |
| td.failover-Active { | |
| color: #0B5A0B; | |
| background-color: #E7F3E7; | |
| font-weight: 600; | |
| } | |
| td.failover-Passive { | |
| color: #8A1212; | |
| background-color: #FDE7E9; | |
| font-weight: 600; | |
| } | |
| td.failover-Unknown { | |
| color: #7A4B00; | |
| background-color: #FFF4CE; | |
| font-weight: 600; | |
| } | |
| /* Service status coloring */ | |
| td.svc-Running { | |
| color: #0B5A0B; | |
| background-color: #E7F3E7; | |
| font-weight: 600; | |
| } | |
| td.svc-Stopped { | |
| color: #8A1212; | |
| background-color: #FDE7E9; | |
| font-weight: 600; | |
| } | |
| td.svc-Unknown { | |
| color: #7A4B00; | |
| background-color: #FFF4CE; | |
| font-weight: 600; | |
| } | |
| "@ | |
| function New-WillowEpsStatusHtml { | |
| [CmdletBinding()] | |
| param( | |
| [Parameter(Mandatory)][string]$Title, | |
| [Parameter(Mandatory)][string]$FilePath, | |
| [Parameter(Mandatory)][string[]]$ServiceNames, | |
| [Parameter(Mandatory)][object[]]$Rows, | |
| [Parameter()][string]$CssText | |
| ) | |
| $sb = [System.Text.StringBuilder]::new() | |
| [void]$sb.AppendLine('<!DOCTYPE html>') | |
| [void]$sb.AppendLine('<html>') | |
| [void]$sb.AppendLine('<head>') | |
| [void]$sb.AppendLine('<meta charset="UTF-8">') | |
| [void]$sb.AppendLine('<style type="text/css">') | |
| [void]$sb.AppendLine($CssText) | |
| [void]$sb.AppendLine('</style>') | |
| [void]$sb.AppendLine('</head>') | |
| [void]$sb.AppendLine('<body>') | |
| [void]$sb.AppendLine((' <h2>{0}</h2>' -f (ConvertTo-HtmlSafeText $Title))) | |
| [void]$sb.AppendLine((' <h3 class="path">XML Path: {0}</h3>' -f (ConvertTo-HtmlSafeText $FilePath))) | |
| [void]$sb.AppendLine(' <table class="status" cellpadding="0" cellspacing="0">') | |
| [void]$sb.AppendLine(' <tr>') | |
| [void]$sb.AppendLine(' <th>Server</th>') | |
| [void]$sb.AppendLine(' <th>Failover Status</th>') | |
| foreach ($svcName in $ServiceNames) { | |
| [void]$sb.AppendLine((' <th>{0}</th>' -f (ConvertTo-HtmlSafeText $svcName))) | |
| } | |
| [void]$sb.AppendLine(' </tr>') | |
| for ($i = 0; $i -lt $Rows.Count; $i++) { | |
| $row = $Rows[$i] | |
| $rowClass = if (($i % 2) -eq 0) { 'row-odd' } else { 'row-even' } | |
| $serverText = ConvertTo-HtmlSafeText $row.Server | |
| $failover = ConvertTo-FailoverStatus $row.FailoverStatus | |
| [void]$sb.AppendLine((' <tr class="{0}">' -f $rowClass)) | |
| [void]$sb.AppendLine((' <td>{0}</td>' -f $serverText)) | |
| [void]$sb.AppendLine((' <td class="failover-{0}">{1}</td>' -f $failover, (ConvertTo-HtmlSafeText $failover))) | |
| foreach ($svcName in $ServiceNames) { | |
| $svcRaw = $null | |
| if ($row.PSObject.Properties.Match($svcName).Count -gt 0) { | |
| $svcRaw = $row.$svcName | |
| } | |
| $svcStatus = ConvertTo-ServiceStatus $svcRaw | |
| [void]$sb.AppendLine((' <td class="svc-{0}">{1}</td>' -f $svcStatus, (ConvertTo-HtmlSafeText $svcStatus))) | |
| } | |
| [void]$sb.AppendLine(' </tr>') | |
| } | |
| [void]$sb.AppendLine(' </table>') | |
| [void]$sb.AppendLine('</body>') | |
| [void]$sb.AppendLine('</html>') | |
| $sb.ToString() | |
| } | |
| } | |
| process { | |
| # Load local XML | |
| $xml = [xml]::new() | |
| $xml.Load($EPSXMLFilePath) | |
| $EPSServerList = @( | |
| $xml.Settings.FailoverSettings.FailoverNodes.server | | |
| Where-Object { $_ -and ($_ -notmatch $env:COMPUTERNAME) } | |
| ) | |
| # Canonical service list (assumed identical across all servers) | |
| $localServices = @(Get-Service -Name 'EpicPrint*' -ErrorAction SilentlyContinue | Sort-Object -Property Name) | |
| $serviceNames = @($localServices.Name) | |
| if (-not $serviceNames -or $serviceNames.Count -eq 0) { | |
| throw "No services matched 'EpicPrint*' on $($env:COMPUTERNAME). Cannot build a consistent multi-service table." | |
| } | |
| # Local row | |
| $localFailoverRaw = $xml.settings.FailoverSettings.ServerStatus | |
| $localRow = [pscustomobject]@{ | |
| Server = $env:COMPUTERNAME | |
| FailoverStatus = (ConvertTo-FailoverStatus $localFailoverRaw) | |
| } | |
| foreach ($svcName in $serviceNames) { | |
| $svc = $localServices | Where-Object { $_.Name -eq $svcName } | Select-Object -First 1 | |
| $localRow | Add-Member -MemberType NoteProperty -Name $svcName -Value (ConvertTo-ServiceStatus $svc.Status) -Force | |
| } | |
| # Remote rows | |
| $remoteRows = foreach ($server in $EPSServerList) { | |
| # Default row = Unknown everywhere (your requirement) | |
| $row = [pscustomobject]@{ | |
| Server = $server | |
| FailoverStatus = 'Unknown' | |
| } | |
| foreach ($svcName in $serviceNames) { | |
| $row | Add-Member -MemberType NoteProperty -Name $svcName -Value 'Unknown' -Force | |
| } | |
| $session = $null | |
| try { | |
| $session = New-PSSession -ComputerName $server -ErrorAction Stop | |
| # Failover status (anything not Active/Passive => Unknown) | |
| $remoteFailoverRaw = Invoke-Command -Session $session -ErrorAction Stop -ScriptBlock { | |
| $remoteXml = [xml]::new() | |
| $remoteXml.Load($using:EPSXMLFilePath) | |
| $remoteXml.settings.FailoverSettings.ServerStatus | |
| } | |
| $row.FailoverStatus = ConvertTo-FailoverStatus $remoteFailoverRaw | |
| # Services: retrieve by canonical names; failures remain Unknown | |
| $remoteSvc = Invoke-Command -Session $session -ErrorAction Stop -ScriptBlock { | |
| Get-Service -Name $using:serviceNames -ErrorAction SilentlyContinue | | |
| Select-Object -Property Name, Status | |
| } | |
| foreach ($svcName in $serviceNames) { | |
| $match = $remoteSvc | Where-Object { $_.Name -eq $svcName } | Select-Object -First 1 | |
| if ($null -ne $match) { | |
| $row.$svcName = ConvertTo-ServiceStatus $match.Status | |
| } | |
| else { | |
| $row.$svcName = 'Unknown' | |
| } | |
| } | |
| } | |
| catch { | |
| # Keep Unknowns (already defaulted) per requirement | |
| } | |
| finally { | |
| if ($session) { | |
| Remove-PSSession -Session $session -ErrorAction SilentlyContinue | |
| } | |
| } | |
| $row | |
| } | |
| $allRows = @($localRow) + @($remoteRows) | |
| # Build HTML (manual to preserve CSS classes + alternating rows) | |
| $title = 'Willow EPS Status' | |
| $htmlBody = New-WillowEpsStatusHtml -Title $title -FilePath $EPSXMLFilePath -ServiceNames $serviceNames -Rows $allRows -CssText $Css | |
| $SMTPParams = @{ | |
| To = $SMTPTo | |
| From = $SMTPFrom | |
| Subject = $SMTPSubject | |
| Body = $htmlBody | |
| SmtpServer = $SMTPServer | |
| BodyAsHtml = $true | |
| } | |
| Send-MailMessage @SMTPParams | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment