When managing Exchange it is always nice to keep an eye on the Exchange queues which you can do with the Management shell but this is very boring output, so I wondered what if I could make that an website that would then update every 15 minutes to reflect the live status of the queues - this then means people can check these queues without Exchange interaction.
Note : This is only available for your Exchange on-premsis environment and cannot be used for Exchange Online.
This is what the final result looks like, where you have active queues on the top with coloured health card and shadow redundancy cards still displayed but as information only (as they usually require no action)
Now we need the script to take the command from earlier and make it look amazing in HTML this is below:
Script : ExchageQueues.ps1# Function to get health status color based on message count
function Get-HealthStatus {
param (
[int]$MessageCount
)
if ($MessageCount -lt 15) {
return @{
Status = "Healthy"
Color = "#10B981" # Green
BgColor = "#DCFCE7"
}
}
elseif ($MessageCount -ge 30 -and $MessageCount -le 50) {
return @{
Status = "Warning"
Color = "#F59E0B" # Yellow
BgColor = "#FEF3C7"
}
}
elseif ($MessageCount -gt 51) {
return @{
Status = "Critical"
Color = "#EF4444" # Red
BgColor = "#FEE2E2"
}
}
else {
return @{
Status = "Notice"
Color = "#3B82F6" # Blue
BgColor = "#DBEAFE"
}
}
}
# Get current timestamp
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Initialize HTML string with styles
$html = @"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exchange Queue Monitor</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f3f4f6;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.timestamp {
color: #6b7280;
font-size: 0.875rem;
}
.server-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.server-card {
background: white;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
overflow: hidden;
}
.server-header {
background-color: #f9fafb;
padding: 15px 20px;
border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
}
.server-title {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: #111827;
}
.queue-list {
padding: 20px;
}
.queue-item {
padding: 15px;
border-radius: 6px;
margin-bottom: 10px;
border-left: 4px solid;
}
.queue-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.queue-id {
font-weight: 500;
color: #1f2937;
}
.message-count {
padding: 4px 12px;
border-radius: 9999px;
color: white;
font-size: 0.875rem;
font-weight: 500;
}
.queue-details {
font-size: 0.875rem;
color: #4b5563;
}
.section-header {
margin: 30px 0 20px;
padding-bottom: 10px;
border-bottom: 2px solid #e5e7eb;
color: #374151;
}
.shadow-queue {
background-color: #f9fafb;
border-left-color: #9ca3af !important;
}
.shadow-queue .message-count {
background-color: #6b7280 !important;
}
.queue-type-badge {
font-size: 0.75rem;
padding: 2px 8px;
border-radius: 4px;
background-color: #e5e7eb;
color: #4b5563;
}
.auto-refresh {
text-align: center;
margin-top: 20px;
color: #6b7280;
font-size: 0.875rem;
}
.no-queues {
text-align: center;
padding: 40px;
color: #6b7280;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Exchange Queue Monitor</h1>
<span class="timestamp">Last updated: $timestamp</span>
</div>
<h2 class="section-header">Primary Queues</h2>
"@
# Get all mailbox servers and their queues
$mailboxServers = Get-MailboxServer
$hasPrimaryQueues = $false
$hasShadowQueues = $false
# First section: Primary Queues
$html += '<div class="server-grid">'
foreach ($server in $mailboxServers) {
$primaryQueues = Get-Queue -Server $server.Identity | Where-Object {
$_.MessageCount -gt 5 -and $_.DeliveryType -ne "ShadowRedundancy"
}
if ($primaryQueues) {
$hasPrimaryQueues = $true
$html += @"
<div class="server-card">
<div class="server-header">
<h2 class="server-title">$($server.Name)</h2>
</div>
<div class="queue-list">
"@
foreach ($queue in $primaryQueues) {
$health = Get-HealthStatus -MessageCount $queue.MessageCount
$html += @"
<div class="queue-item" style="background-color: $($health.BgColor); border-left-color: $($health.Color);">
<div class="queue-header">
<span class="queue-id">Queue ID: $($queue.Identity.Split('\')[-1])</span>
<span class="message-count" style="background-color: $($health.Color);">$($queue.MessageCount) messages</span>
</div>
<div class="queue-details">
<p>Next Hop: $($queue.NextHopDomain)</p>
<p>Status: $($queue.Status)</p>
<p>Delivery Type: $($queue.DeliveryType)</p>
</div>
</div>
"@
}
$html += @"
</div>
</div>
"@
}
}
$html += '</div>'
if (-not $hasPrimaryQueues) {
$html += @"
<div class="no-queues">
<h2>No Active Primary Queues</h2>
<p>No primary queues with more than 5 messages found.</p>
</div>
"@
}
# Second section: Shadow Redundancy Queues
$html += '<h2 class="section-header">Shadow Redundancy Information</h2>'
$html += '<div class="server-grid">'
foreach ($server in $mailboxServers) {
$shadowQueues = Get-Queue -Server $server.Identity | Where-Object {
$_.MessageCount -gt 5 -and $_.DeliveryType -eq "ShadowRedundancy"
}
if ($shadowQueues) {
$hasShadowQueues = $true
$html += @"
<div class="server-card">
<div class="server-header">
<h2 class="server-title">$($server.Name)</h2>
<span class="queue-type-badge">Shadow</span>
</div>
<div class="queue-list">
"@
foreach ($queue in $shadowQueues) {
$html += @"
<div class="queue-item shadow-queue">
<div class="queue-header">
<span class="queue-id">Queue ID: $($queue.Identity.Split('\')[-1])</span>
<span class="message-count">$($queue.MessageCount) messages</span>
</div>
<div class="queue-details">
<p>Next Hop: $($queue.NextHopDomain)</p>
<p>Status: $($queue.Status)</p>
</div>
</div>
"@
}
$html += @"
</div>
</div>
"@
}
}
$html += '</div>'
if (-not $hasShadowQueues) {
$html += @"
<div class="no-queues">
<h2>No Shadow Queues</h2>
<p>No shadow redundancy queues with more than 5 messages found.</p>
</div>
"@
}
# Close HTML
$html += @"
<div class="auto-refresh">
This page auto-refreshes every 15 minutes
</div>
</div>
<script>
setTimeout(function() {
window.location.reload();
}, 900000); //15 minutes in milliseconds
</script>
</body>
</html>
"@
# Define the output path
$outputPath = "QueueStatus.html"
# Create directory if it doesn't exist
$outputDir = Split-Path -Parent $outputPath
if (-not (Test-Path -Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force
}
# Save the HTML file
$html | Out-File -FilePath $outputPath -Encoding UTF8
Output File
The section in the script that defines where the output file is location which is set as this:
$outputPath = "QueueStatus.html"
If you need that being delivered to a webserver to serve the page you can change that to the below, as an example:
$outputPath = "\\wwwsrv1.bear.local\www\ExchangeQueue\QueueStatus.html"
Scheduled Task
The script on it own needs to be manually run, so simple add a scheduled task with the syntax of the options below to run this every 15 minutes.
Action : Start a Program
Command : powershell.exe
Arguments : -File "c:\Queues\ExchageQueues.ps1"