Day 20 我們終於把 PowerShell Script 放進:
Task Scheduler
現在已經可以做到:
06:00
↓
自動執行
↓
Server Health Check
↓
AD Audit
↓
產生 CSV
↓
寫入 Log
↓
回傳 Exit Code
這已經算是真正的:
Unattended Automation
但接下來又會遇到新的問題。
假設每天早上產生:
ServerHealth_20260929.csv
Inactive_AD_Users_20260929.csv
Inactive_AD_Computers_20260929.csv
AD_Group_Summary_20260929.csv
工程師還是要:
打開 CSV
↓
找 CPU
↓
找 Memory
↓
找 Critical
↓
換下一個 CSV
↓
再找異常
如果今天只有 5 台 Server 沒問題。
但如果:
50 台
100 台
200 台
CSV 很快就變成:
有資料,但是不好閱讀。
所以 Day 21 我們要開始處理另一個問題:
如何把 Automation 的結果變成人可以快速理解的資訊?
今天主要會使用:
ConvertTo-Html
把我們前面收集的資料做成:
Daily Infrastructure Health Report
最後產生:
Daily_Health_Report_20260929.html
打開之後可以直接看到:
Daily Infrastructure Report
Summary
Total Servers : 20
Healthy : 16
Warning : 3
Critical : 1
Server Health
SERVER01 Healthy
SERVER02 Warning
SERVER03 Critical
AD Audit
Inactive Users : 15
Inactive Computers : 28
Group Issues : 3
而不是每天先打開四五份 CSV。
HTML Report 有什麼好處?
CSV 非常適合:
資料交換
Excel 分析
後續 PowerShell 處理
保存原始結果
所以 Day 21 並不是:
有 HTML 之後就不要 CSV。
而是變成:
Automation
│
├── CSV
│ ↓
│ Raw Data
│
└── HTML
↓
Human View
CSV 留給:
工程師
稽核
資料處理
後續 Script
HTML 則讓:
工程師
主管
值班人員
可以快速看懂今天的狀態。
第一個 ConvertTo-Html
先做最簡單的。
假設:
$Servers = @(
[PSCustomObject]@{
ComputerName = "SERVER01"
CPUUsage = 25
MemoryUsage = 65
OverallStatus = "Healthy"
}
[PSCustomObject]@{
ComputerName = "SERVER02"
CPUUsage = 85
MemoryUsage = 73
OverallStatus = "Warning"
}
[PSCustomObject]@{
ComputerName = "SERVER03"
CPUUsage = 93
MemoryUsage = 88
OverallStatus = "Critical"
}
)
直接:
$Servers |
ConvertTo-Html
PowerShell 會產生類似:
...
也就是:
PowerShell Object → HTML Table
輸出成 HTML 檔案
例如:
$ReportFolder =
"C:\Automation\Reports"
$Date =
Get-Date -Format "yyyyMMdd"
$HtmlFile =
"$ReportFolder\Daily_Health_Report_$Date.html"
然後:
$Servers |
ConvertTo-Html -Title "Daily Infrastructure Report" | Out-File
-FilePath $HtmlFile `
-Encoding UTF8
最後:
C:\Automation\Reports
└── Daily_Health_Report_20260929.html
直接用 Browser 打開即可。
不要把所有 Property 都塞進 HTML
跟 Day 3、Day 4 的概念一樣。
假設 $Servers 裡有:
ComputerName
Connection
CPUUsage
CPUStatus
MemoryUsage
MemoryStatus
DiskStatus
ServiceStatus
UptimeDays
EventStatus
ErrorMessage
CheckTime
PSComputerName
RunspaceId
...
直接:
$Servers |
ConvertTo-Html
報表可能超級寬。
所以先:
$ServerView =
$Servers |
Select-Object `
ComputerName,
Connection,
CPUUsage,
MemoryUsage,
DiskStatus,
ServiceStatus,
UptimeDays,
OverallStatus
再:
$ServerView |
ConvertTo-Html
也就是:
HTML Report 不是把所有資料倒出去,而是選擇人真正需要看的資訊。
使用 PreContent 加標題
例如:
$ServerView |
ConvertTo-Html -Title "Daily Infrastructure Report"
-PreContent "Daily Infrastructure Report"
打開就會看到:
Daily Infrastructure Report
然後下面才是 Server Table。
使用 PostContent
也可以加入:
$GeneratedTime =
Get-Date `
-Format "yyyy-MM-dd HH:mm:ss"
然後:
ConvertTo-Html -PostContent
"Generated: $GeneratedTime"
讓報表知道:
這份資料是什麼時候產生的?
這對每日巡檢很重要。
但原始 HTML 很醜
ConvertTo-Html 可以產生表格。
但預設外觀通常比較像:
1990 年代內部系統
功能有了,但不好閱讀。
所以接下來要加入:
CSS
這裡不需要學前端框架。
我們只需要少量 CSS,就可以讓報表好看非常多。
建立 CSS
例如:
$Css = @"
"@
這一段:
@"
"@
在 PowerShell 裡叫:
Here-String
很適合放多行:
HTML
CSS
SQL
長文字
把 CSS 放進 ConvertTo-Html
例如:
$ServerView |
ConvertTo-Html -Title "Daily Infrastructure Report"
-Head $Css -PreContent "<h1>Daily Infrastructure Report</h1>" | Out-File
$HtmlFile `
-Encoding UTF8
現在 Browser 打開,整份報表會整齊很多。
先做 Summary
真正巡檢報表第一眼不應該看到:
SERVER001
SERVER002
SERVER003
...
SERVER100
應該先看到:
今天整體狀況怎麼樣?
例如:
$TotalServers =
@($Servers).Count
Healthy:
$HealthyServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Healthy"
}
).Count
Warning:
$WarningServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Warning"
}
).Count
Critical:
$CriticalServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Critical"
}
).Count
Unknown:
$UnknownServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Unknown"
}
).Count
建立 Summary Object
$Summary = [PSCustomObject]@{
Total =
$TotalServers
Healthy =
$HealthyServers
Warning =
$WarningServers
Critical =
$CriticalServers
Unknown =
$UnknownServers
}
顯示:
Total Healthy Warning Critical Unknown
20 16 2 1 1
這就是:
Management / Operator View
Summary 也轉成 HTML
$SummaryHtml =
$Summary |
ConvertTo-Html -Fragment
-PreContent "Summary"
注意這次使用:
-Fragment
-Fragment 是什麼?
如果不加:
-Fragment
PowerShell 會產生:
完整 HTML。
但是今天我們需要:
Summary Table
+
Server Table
+
AD Table
如果每一段都自己產生:
最後會很難組合。
所以:
-Fragment
意思就是:
只產生這一小段 HTML。
例如:
最後我們再自己組成一個完整頁面。
Server Health 也做 Fragment
$ServerHtml =
$Servers |
Select-Object ComputerName, Connection, CPUUsage, CPUStatus, MemoryUsage, MemoryStatus, DiskStatus, ServiceStatus, UptimeDays, OverallStatus | ConvertTo-Html
-Fragment `
-PreContent "Server Health"
現在:
$SummaryHtml
是一段 HTML。
$ServerHtml
也是一段 HTML。
最後可以拼在一起。
建立完整 HTML
例如:
$GeneratedTime =
Get-Date `
-Format "yyyy-MM-dd HH:mm:ss"
然後:
$Html = @"
$Css
$SummaryHtml
$ServerHtml
輸出:
$Html |
Out-File -FilePath $HtmlFile
-Encoding UTF8
這就是第一份完整的:
Daily Health Dashboard
接下來最重要的是 Status 顏色
目前:
Healthy
Warning
Critical
Unknown
全部都是一般文字。
工程師還是要自己找。
我們希望:
Healthy → 容易辨識
Warning → 醒目
Critical → 最醒目
Unknown → 需要確認
這裡 CSS 就可以幫忙。
加入 Status Class
在 CSS 加:
.status-healthy {
font-weight: bold;
}
.status-warning {
font-weight: bold;
}
.status-critical {
font-weight: bold;
}
.status-unknown {
font-weight: bold;
}
如果是實際文章 / Lab,你也可以依自己公司的 Dashboard 規範設定背景或文字色。
這裡重點不是顏色本身。
而是:
不同 Severity 應該有視覺區分。
ConvertTo-Html 不會自動幫我們判斷 Status
這是一個很重要的地方。
PowerShell 不會看到:
Critical
就自動產生:
class="status-critical"
最簡單的做法之一,是先產生 HTML,再做字串替換。
例如:
$ServerHtml =
$ServerHtml -replace
'Healthy',
'Healthy'
Warning:
$ServerHtml =
$ServerHtml -replace
'Warning',
'Warning'
Critical:
$ServerHtml =
$ServerHtml -replace
'Critical',
'Critical'
Unknown:
$ServerHtml =
$ServerHtml -replace
'Unknown',
'Unknown'
這樣 HTML 裡就會變成:
CSS 才能針對它做不同顯示。
這種 -replace 寫法很適合目前這個 Lab;如果報表未來變成複雜的正式 Web UI,就應該考慮更完整的 HTML Template,而不是一直用字串替換。
Summary 也可以做成 Cards
Table 可以用,但 Dashboard 第一眼如果能看到:
┌─────────────┐
│ Total 20 │
└─────────────┘
┌─────────────┐
│ Healthy 16 │
└─────────────┘
┌─────────────┐
│ Warning 2 │
└─────────────┘
┌─────────────┐
│ Critical 1 │
└─────────────┘
會更容易看。
我們可以直接建立 HTML:
$SummaryCards = @"
<div class="summary-card">
<div class="summary-title">
Total Servers
</div>
<div class="summary-value">
$TotalServers
</div>
</div>
<div class="summary-card">
<div class="summary-title">
Healthy
</div>
<div class="summary-value">
$HealthyServers
</div>
</div>
<div class="summary-card">
<div class="summary-title">
Warning
</div>
<div class="summary-value">
$WarningServers
</div>
</div>
<div class="summary-card">
<div class="summary-title">
Critical
</div>
<div class="summary-value">
$CriticalServers
</div>
</div>
"@
CSS:
.summary-container {
display: flex;
gap: 15px;
margin: 20px 0;
flex-wrap: wrap;
}
.summary-card {
background: white;
padding: 20px;
min-width: 140px;
border-radius: 6px;
}
.summary-title {
font-size: 14px;
}
.summary-value {
font-size: 28px;
font-weight: bold;
margin-top: 5px;
}
這時就開始真的比較像:
Dashboard
而不是單純把 CSV 搬進 Browser。
把 AD Audit 也放進同一份報表
目前前幾天已經有:
Inactive AD Users
Inactive AD Computers
AD Group Audit
如果今天 Script 直接取得:
$InactiveUsers
$InactiveComputers
$GroupIssues
可以計算:
$InactiveUserCount =
@($InactiveUsers).Count
$InactiveComputerCount =
@($InactiveComputers).Count
$GroupIssueCount =
@($GroupIssues).Count
建立:
$ADSummary = [PSCustomObject]@{
InactiveUsers =
$InactiveUserCount
InactiveComputers =
$InactiveComputerCount
GroupIssues =
$GroupIssueCount
}
再:
$ADSummaryHtml =
$ADSummary |
ConvertTo-Html -Fragment
-PreContent "Active Directory Audit"
最後插入主 HTML:
Server Summary
↓
Server Health
↓
AD Audit
如果不同 Script 已經各自輸出 CSV 呢?
Day 20 我們可能已經讓不同排程各自產生:
ServerHealth_20260929.csv
Inactive_AD_Users_20260929.csv
Inactive_AD_Computers_20260929.csv
那 Day 21 不一定需要把所有 Query 重跑一次。
可以直接:
$ServerData =
Import-Csv `
".\Reports\ServerHealth_20260929.csv"
Inactive User:
$InactiveUsers =
Import-Csv `
".\Reports\Inactive_AD_Users_20260929.csv"
Inactive Computer:
$InactiveComputers =
Import-Csv `
".\Reports\Inactive_AD_Computers_20260929.csv"
然後:
Existing CSV
↓
Report Generator
↓
HTML Dashboard
這種設計其實更漂亮。
把「資料收集」跟「報表產生」分開
以前:
HealthCheck.ps1
什麼都做。
現在可以拆成:
Collect-ServerHealth.ps1
↓
ServerHealth.csv
Collect-ADAudit.ps1
↓
ADAudit.csv
New-DailyHtmlReport.ps1
↓
讀 CSV
↓
產生 HTML
這就是:
Separation of Concerns
每個 Script 有自己的主要責任。
Collector
→ 收資料
Report Generator
→ 整理資料
HTML
→ 顯示
這比:
一支 1500 行 PowerShell
什麼都做
容易維護很多。
HTML Report 不代表原始 CSV 可以刪
這點我會保留兩種格式:
Raw
│
└── CSV
Presentation
│
└── HTML
因為假設主管問:
為什麼 SERVER02 被判定 Warning?
HTML 可能只顯示:
SERVER02
CPU 87%
Warning
但 CSV 可以保留更多細節。
所以:
HTML 是 View,不是原始資料的替代品。
先把異常 Server 放前面
如果有:
100 Servers
Healthy 95
Warning 4
Critical 1
使用者不應該先看:
95 台 Healthy
然後滑到最下面才找到 Critical。
可以排序:
$ServerView =
$Servers |
Sort-Object `
@{Expression={
switch ($_.OverallStatus) {
"Critical" { 1 }
"Warning" { 2 }
"Unknown" { 3 }
"Healthy" { 4 }
default { 5 }
}
}},
ComputerName
這樣:
Critical
Warning
Unknown
Healthy
會依序排。
這對 Dashboard 很實用。
甚至只顯示異常
可以另外建立:
$ProblemServers =
$Servers |
Where-Object {
$_.OverallStatus -ne "Healthy"
}
再:
$ProblemHtml =
$ProblemServers |
Select-Object ComputerName, CPUStatus, MemoryStatus, DiskStatus, ServiceStatus, OverallStatus | ConvertTo-Html
-Fragment `
-PreContent "Servers Requiring Attention"
這樣首頁先看到:
需要注意
下面再放:
All Servers
這就很符合實際巡檢:
先看異常,再看全部。
如果沒有異常呢?
假設:
$ProblemServers.Count
是:
0
不應該產生一個空 Table。
可以:
if (
@($ProblemServers).Count -eq 0
) {
$ProblemHtml =
"<p>No server issues detected.</p>"
}
else {
$ProblemHtml =
$ProblemServers |
ConvertTo-Html `
-Fragment
}
這樣使用者看到:
No server issues detected.
比空白表格直覺很多。
Empty Report 也不能直接代表 Healthy
但是這裡要小心。
假設:
ServerHealth.csv
根本沒有產生。
結果:
$Servers.Count
變成 0。
不能直接顯示:
No server issues detected.
因為真正情況可能是:
Collector 根本沒跑成功。
所以 Dashboard 至少要分:
No Issues
跟:
No Data
這是 Monitoring / Reporting 很重要的差別。
加入 Data Status
例如:
if (
-not (Test-Path $ServerCsv)
) {
$ServerDataStatus =
"NoData"
}
else {
$ServerDataStatus =
"Available"
}
HTML 可以顯示:
Server Data Status : Available
或者:
Server Data Status : No Data
不能把沒有資料錯當:
Everything Healthy
這跟前面:
Unknown
的觀念完全一樣。
Report 本身也需要 Timestamp
例如:
Report Generated
2026-09-29 06:30
Server Data
2026-09-29 06:00
AD Data
2026-09-29 06:10
因為:
報表現在是 06:30 產生,不代表所有資料都是 06:30 收集。
成熟一點的 Dashboard 應該知道:
Data Freshness
也就是:
我現在看到的到底是不是最新資料?
使用 CheckTime
前面很多 Report 已經有:
CheckTime
所以可以:
$LatestServerCheck =
$Servers |
Measure-Object -Property CheckTime
-Maximum
不過要注意:
Import-Csv 之後:
CheckTime
是 String。
如果要真的做日期計算,最好轉成:
[datetime]
例如:
$ServerCheckTimes =
$Servers |
ForEach-Object {
[datetime]$_.CheckTime
}
再找最大值:
$LatestServerCheck =
$ServerCheckTimes |
Measure-Object -Maximum |
Select-Object `
-ExpandProperty Maximum
今天完整 Dashboard 範例
下面整合:
Summary
Problem Servers
All Servers
AD Audit
Generated Time
$ReportFolder =
Join-Path $PSScriptRoot
"Reports"
$Date =
Get-Date `
-Format "yyyyMMdd"
$HtmlFile =
Join-Path $ReportFolder
"Daily_Health_Report_$Date.html"
$Servers = @(
[PSCustomObject]@{
ComputerName = "SERVER01"
Connection = "Success"
CPUUsage = 25
CPUStatus = "Normal"
MemoryUsage = 65
MemoryStatus = "Normal"
DiskStatus = "Normal"
ServiceStatus = "Normal"
UptimeDays = 15
OverallStatus = "Healthy"
}
[PSCustomObject]@{
ComputerName = "SERVER02"
Connection = "Success"
CPUUsage = 85
CPUStatus = "Warning"
MemoryUsage = 72
MemoryStatus = "Normal"
DiskStatus = "Normal"
ServiceStatus = "Normal"
UptimeDays = 32
OverallStatus = "Warning"
}
[PSCustomObject]@{
ComputerName = "SERVER03"
Connection = "Success"
CPUUsage = 93
CPUStatus = "Critical"
MemoryUsage = 88
MemoryStatus = "Warning"
DiskStatus = "Critical"
ServiceStatus = "Normal"
UptimeDays = 102
OverallStatus = "Critical"
}
)
$TotalServers =
@($Servers).Count
$HealthyServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Healthy"
}
).Count
$WarningServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Warning"
}
).Count
$CriticalServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Critical"
}
).Count
$UnknownServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -eq "Unknown"
}
).Count
$SummaryCards = @"
"@
$ProblemServers =
@(
$Servers |
Where-Object {
$_.OverallStatus -ne "Healthy"
}
)
if ($ProblemServers.Count -eq 0) {
$ProblemHtml =
@"
"@
}
else {
$ProblemHtml =
$ProblemServers |
Select-Object `
ComputerName,
CPUUsage,
CPUStatus,
MemoryUsage,
MemoryStatus,
DiskStatus,
ServiceStatus,
OverallStatus |
ConvertTo-Html `
-Fragment `
-PreContent `
"<h2>Servers Requiring Attention</h2>"
}
$SortedServers =
$Servers |
Sort-Object `
@{
Expression = {
switch (
$_.OverallStatus
) {
"Critical" {
1
}
"Warning" {
2
}
"Unknown" {
3
}
"Healthy" {
4
}
default {
5
}
}
}
},
ComputerName
$ServerHtml =
$SortedServers |
Select-Object ComputerName, Connection, CPUUsage, CPUStatus, MemoryUsage, MemoryStatus, DiskStatus, ServiceStatus, UptimeDays, OverallStatus | ConvertTo-Html
-Fragment -PreContent
"All Servers"
foreach ($VariableName in @(
"ProblemHtml",
"ServerHtml"
)) {
$Value =
Get-Variable `
-Name $VariableName `
-ValueOnly
$Value =
$Value `
-replace `
'<td>Healthy</td>',
'<td class="status-healthy">Healthy</td>'
$Value =
$Value `
-replace `
'<td>Warning</td>',
'<td class="status-warning">Warning</td>'
$Value =
$Value `
-replace `
'<td>Critical</td>',
'<td class="status-critical">Critical</td>'
$Value =
$Value `
-replace `
'<td>Unknown</td>',
'<td class="status-unknown">Unknown</td>'
Set-Variable `
-Name $VariableName `
-Value $Value
}
$InactiveUserCount = 15
$InactiveComputerCount = 28
$GroupIssueCount = 3
$ADSummary =
[PSCustomObject]@{
InactiveUsers =
$InactiveUserCount
InactiveComputers =
$InactiveComputerCount
GroupIssues =
$GroupIssueCount
}
$ADHtml =
$ADSummary |
ConvertTo-Html -Fragment
-PreContent `
"Active Directory Audit"
$Css = @"
"@
$GeneratedTime =
Get-Date `
-Format "yyyy-MM-dd HH:mm:ss"
$Html = @"
$Css
Generated:
$GeneratedTime
$SummaryCards
$ProblemHtml
$ServerHtml
$ADHtml
Generated automatically by
PowerShell Infrastructure Automation.
"@
$Html |
Out-File -FilePath $HtmlFile
-Encoding UTF8
Write-Host ""
Write-Host `
"HTML Report Generated:"
Write-Host `
$HtmlFile
最後打開會看到什麼?
整體架構會像:
Daily Infrastructure Report
Generated: 2026-09-29 06:30
┌────────┐
│ Total │
│ 20 │
└────────┘
┌────────┐
│Healthy │
│ 16 │
└────────┘
┌────────┐
│Warning │
│ 3 │
└────────┘
┌────────┐
│Critical│
│ 1 │
└────────┘
Servers Requiring Attention
SERVER03 CPU Critical Disk Critical
SERVER05 Memory Warning
SERVER12 Connection Unknown
All Servers
SERVER03 ...
SERVER05 ...
SERVER12 ...
SERVER01 ...
SERVER02 ...
Active Directory Audit
InactiveUsers 15
InactiveComputers 28
GroupIssues 3
一打開就知道:
今天有沒有事情要處理?
Healthy 不需要跟 Critical 一樣搶眼
這也是 Dashboard 設計的一個重要概念。
假設:
100 Servers
Healthy 98
Warning 1
Critical 1
工程師真正需要注意的是:
1 Warning
1 Critical
所以報表不是:
每一個欄位都做得很華麗。
而是:
讓異常比正常更容易被看到。
這也是 Monitoring Dashboard 很重要的一個設計方向。
Report 產生失敗也要寫 Log
Day 20 已經有:
Write-Log
所以 Day 21 Report Generator 也可以:
try {
# Generate HTML
Write-Log `
"HTML report generated: $HtmlFile"
}
catch {
Write-Log `
-Message "HTML report failed: $($_.Exception.Message)" `
-Level "ERROR"
exit 1
}
不要因為:
Health Check 成功
就假設:
HTML Report 一定成功
Collector 與 Reporter 是兩個不同的工作。
Task Scheduler 也可以串起來
現在可以有:
Task 1
06:00
Collect Server Health
Task 2
06:10
Collect AD Audit
Task 3
06:20
Generate HTML Report
但這裡會出現另一個問題。
如果:
Task 1
06:00 開始
結果今天跑到 06:25
可是:
Task 3
06:20
已經開始產 Report。
那 Report 可能讀到:
舊資料或不完整資料。
這就是後面會開始面對的:
Job Dependency
問題。
一個簡單的方式:Master Script
也可以做:
DailyAutomation.ps1
裡面:
Collect Server Health
↓
成功 / Partial
↓
Collect AD Audit
↓
成功 / Partial
↓
Generate HTML
↓
Exit Code
也就是:
& "$PSScriptRoot\Collect-ServerHealth.ps1"
& "$PSScriptRoot\Collect-ADAudit.ps1"
& "$PSScriptRoot\New-DailyHtmlReport.ps1"
不過這裡還要處理:
子 Script Exit Code
一個 Job 失敗要不要繼續?
Report 可以接受 Partial Data 嗎?
這就開始有一點:
Orchestration
的味道了。
PowerShell HTML Report 不是 Monitoring System
這裡也要把邊界分清楚。
我們今天做:
每天 06:00
↓
產一份 HTML
適合:
Daily Check
Management Report
Audit
Morning Review
但如果需求是:
CPU 超過 95%
10 秒內通知
就不適合依賴:
每天一份 HTML
那是:
Real-time Monitoring
應該交給:
Nagios
Zabbix
PRTG
SCOM
Prometheus
這些監控系統。
所以我們現在做的是:
Daily Operational Reporting
不是要重新發明完整 Monitoring Platform。
Day 21 小結
今天我們第一次把前面收集的:
Server Health
AD User Audit
AD Computer Audit
AD Group Audit
從:
CSV
轉成:
HTML Dashboard
主要使用:
ConvertTo-Html
-Fragment
-PreContent
-Head
Out-File
並加入:
CSS
Summary
Status
Problem Servers
AD Summary
Generated Time
讓整套流程從:
有資料
開始往:
資料容易被理解
前進。
目前我們的 Automation 已經變成:
Windows Server / AD
↓
PowerShell Collector
↓
CSV Raw Data
↓
PowerShell Report Generator
↓
HTML Dashboard
↓
Engineer Review
今天最重要的幾個觀念是:
CSV 是 Raw Data,HTML 是 Presentation。
兩個都可以保留。
另外:
No Data 不等於 Healthy。
如果 Collector 沒跑成功,Dashboard 應該顯示:
Unknown
No Data
Collection Failed
而不是:
Everything OK
最後則是:
Dashboard 的目的不是把所有資料塞進去,而是讓真正需要處理的異常更容易被看到。
Day 22 預告
Day 22|PowerShell 自動通知:巡檢出現 Warning / Critical 時主動發送 Email
現在我們已經可以做到:
06:00
↓
自動巡檢
06:20
↓
產生 HTML Dashboard
但還是有一個問題:
如果工程師今天根本忘記打開 HTML 呢?
所以 Day 22 會開始做:
Health Check
↓
判斷 Status
↓
全部 Healthy
│
└── 只產 Report
有 Warning / Critical
│
└── 主動通知
會開始碰到:
Send-MailMessage
但也會特別說明為什麼它在現代環境裡不一定是最理想的長期方案,以及實務上可以怎麼接:
SMTP Relay
Microsoft 365
Teams Webhook / Workflow
其他 Notification Channel
最後我們希望做到:
SERVER03
CPU = Critical
SERVER05
Disk = Warning
↓
PowerShell 自動整理摘要
↓
附上 / 連結 HTML Report
↓
通知維運人員
Day 22 會讓整套工具從「自己會執行、自己會做報表」,再往前一步變成「發現異常後會主動叫人來處理」。