name: winskill slug: winskill displayName: "Windows 伺服器運維工具箱" description: "Windows 伺服器運維工具箱 - 磁碟分析、臨時檔案清理、IIS 站點管理、批次檔案操作、服務狀態監控、Windows Update 診斷、即時效能監控、安全審計、登錄檔啟動項審計、磁碟健康檢測、網路埠監控、事件日誌診斷、已安裝程式管理、使用者會話監控、計劃任務審計、檔案共享審計、DNS網絡卡診斷、SSL證書過期檢測、防火牆規則審計、服務崩潰恢復狀態、系統檔案修復、儲存池管理、備份狀態檢查、Docker容器管理、K8s叢集監控、自動化修復嚮導、效能基線與趨勢分析、等保合規檢查清單、遠端多伺服器管理、日誌輪轉、模組索引。只讀分析+安全確認,絕不誤刪檔案,完全免費離線執行。" description_zh: "Windows 伺服器運維工具箱 - 磁碟分析、清理、IIS 管理、批次操作、服務監控、更新診斷、效能監控、安全審計、登錄檔審計、磁碟健康、網路監控、事件日誌、程式管理、會話監控、計劃任務、共享審計、DNS診斷、SSL證書、防火牆審計、服務崩潰記錄、系統修復、儲存池、備份檢查、Docker容器管理、K8s監控、自動化修復嚮導、效能基線與趨勢分析、等保合規檢查清單、遠端多伺服器管理、日誌輪轉、模組索引。只讀+確認模式,零依賴離線執行。" version: 3.1.0 category: system-administration platforms: - windows tags: - windows - sysadmin - disk-cleanup - iis - monitoring - file-management - devops - performance - security - audit - registry - disk-health - network - event-log - software - session - scheduled-tasks - smb-share - dns - ssl-certificate - firewall - service-recovery requires_api_key: false
直接對 AI 說就行,不用記命令。
| 你想做什麼 | 直接對 AI 說 |
|---|---|
| 看磁碟空間 | "幫我掃一下 D 盤大檔案" |
| 清理臨時檔案 | "看看我電腦的臨時檔案" |
| 檢查 IIS | "我的 IIS 站點還活著嗎?" |
| 檢測重複檔案 | "幫我找下重複檔案" |
| 檢查服務狀態 | "看下資料庫和 IIS 服務有沒有掛" |
| Windows Update 狀態 | "Windows Update 正常嗎?有積壓補丁嗎?" |
| 伺服器卡頓定位 | "伺服器變卡了,幫我看看是什麼原因" |
| 檢查有沒有被入侵 | "幫我查一下有沒有異常登入" |
| 檢查可疑啟動項 | "有沒有可疑的自啟動程式" |
| 磁碟健康狀態 | "硬碟還健康嗎?有沒有壞道" |
| 網路連線監控 | "誰在連我的伺服器" |
| 系統日誌哪裡錯了 | "系統日誌有沒有最近的錯誤" |
| 伺服器裝了什麼程式 | "看看系統安裝了哪些軟體" |
| 誰在伺服器上 | "當前有誰登入了伺服器" |
| 檢查計劃任務 | "有沒有可疑的計劃任務" |
| 檔案共享審計 | "有哪些共享資料夾,許可權安全嗎" |
| DNS / 網絡卡診斷 | "DNS 解析正常嗎" |
| 搞不定了 | "我遇到報錯了,幫我看看" |
⚠️ AI 必須遵守:凡涉及刪除、停止服務、修改系統的操作,必須先展示操作清單,等使用者明確說"確認執行"後才可執行。
| 保護項 | 方式 |
|---|---|
| 系統目錄保護 | 絕不操作 C:\Windows、C:\Program Files 等 |
| 刪除必須確認 | 先展示受影響檔案,等使用者說"確認清理"後才執行 |
| 回收站優先 | 刪除用 Shell.Application 回收站 API |
| 只讀診斷 | 所有分析命令不修改任何檔案/服務/登錄檔 |
| 排除保護 | pagefile.sys、hiberfil.sys 等系統檔案不可操作 |
| 登錄檔保護 | 登錄檔分析只讀,任何修改需雙重確認 |
| 日誌讀取保護 | 事件日誌只讀檢視,不修改任何日誌記錄 |
function Write-WinskillLog {
param([string]$Message)
$logDir = "$env:USERPROFILE\.workbuddy\output\winskill"
$logFile = Join-Path $logDir "winskill.log"
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp $Message" | Out-File $logFile -Append -Encoding utf8
if (Test-Path $logFile) {
$size = (Get-Item $logFile).Length
if ($size -gt 10MB) {
$timestamp = Get-Date -Format "yyyyMMddHHmmss"
Move-Item $logFile "winskill.log.$timestamp" -Force
}
}
Get-ChildItem $logDir -Filter "winskill.log.*" | Where-Object {
$_.LastWriteTime -lt (Get-Date).AddDays(-7)
} | Remove-Item -Force
}
Write-Host "`n📋 Winskill 3.1.0 - 模組索引(共 30 個模組)"
Write-Host ("=" * 60)
Write-Host ("{0,-25} {1,-35}" -f "模組", "用途")
Write-Host ("-" * 60)
$modules = @(
"模組 1: 磁碟空間分析 - 掃大檔案/目錄佔用",
"模組 2: 大檔案重複檢測 - 找重複檔案",
"模組 3: 臨時檔案安全清理 - 清理臨時檔案",
"模組 4: IIS 站點管理 - IIS 站點/應用池狀態",
"模組 5: 服務狀態監控 - Windows 服務狀態",
"模組 6: 批次檔案操作 - 批次重新命名/移動/複製",
"模組 7: 目錄磁碟使用報告 - 目錄大小報告",
"模組 8: Windows Update 服務狀態 - 更新服務/快取",
"模組 9: 即時效能監控 - CPU/記憶體/磁碟/網路",
"模組 10: 安全審計與日誌分析 - 暴力破解/特權操作",
"模組 11: 登錄檔與啟動項安全審計 - 啟動項/WMI",
"模組 12: 磁碟健康狀態檢測 - SMART/壞道/溫度",
"模組 13: 網路連線與埠監控 - 連線/埠/防火牆",
"模組 14: Windows 事件日誌診斷 - 系統/應用日誌",
"模組 15: 已安裝程式與補丁管理 - 程式/補丁清單",
"模組 16: 使用者會話與登入狀態監控 - 會話/登入",
"模組 17: 計劃任務審計 - 計劃任務清單",
"模組 18: 檔案共享與 SMB 審計 - 共享/許可權",
"模組 19: DNS 解析與網絡卡診斷 - DNS/網絡卡",
"模組 20: SSL 證書過期檢測 - 證書掃描",
"模組 21: 防火牆規則審計 - 規則/高危檢測",
"模組 22: 關鍵服務崩潰與自動恢復 - 服務崩潰記錄",
"模組 23: 系統檔案完整性檢查與修復 - SFC/DISM",
"模組 24: 儲存池與虛擬磁碟管理 - Storage Spaces",
"模組 25: Windows Server Backup 狀態檢查 - 備份狀態",
"模組 26: Docker / K8s 容器管理 - 容器/叢集",
"模組 27: 自動化修復嚮導 - 一鍵修復常見問題",
"模組 28: 效能基線 & 趨勢分析 - 趨勢/告警/預測",
"模組 29: 安全合規檢查清單 - 等保2.0/CIS",
"模組 30: 遠端多伺服器管理 - 多機批次管理"
)
$modules | ForEach-Object { Write-Host $_ }
Write-Host ("=" * 60)
Write-Host "直接對 AI 說模組名稱或用途即可呼叫"
🖥️ 基礎運維(1-7): 模組 1:磁碟空間分析 · 模組 2:大檔案重複檢測 · 模組 3:臨時檔案安全清理 · 模組 4:IIS 站點管理 · 模組 5:服務狀態監控 · 模組 6:批次檔案操作 · 模組 7:目錄磁碟使用報告
🔧 進階運維(8-18): 模組 8:Windows Update 服務狀態與快取管理 · 模組 9:即時效能監控與程序資源分析 · 模組 10:安全審計與日誌分析 · 模組 11:登錄檔與啟動項安全審計 · 模組 12:磁碟健康狀態檢測 · 模組 13:網路連線與埠監控 · 模組 14:Windows 事件日誌診斷 · 模組 15:已安裝程式與補丁管理 · 模組 16:使用者會話與登入狀態監控 · 模組 17:計劃任務審計 · 模組 18:檔案共享與 SMB 審計
🔒 安全專項(19-25): 模組 19:DNS 解析與網絡卡診斷 · 模組 20:SSL 證書過期檢測 · 模組 21:Windows 防火牆規則審計 · 模組 22:關鍵服務崩潰與自動恢復狀態 · 模組 23:系統檔案完整性檢查與修復(SFC / DISM) · 模組 24:儲存池與虛擬磁碟管理(Storage Spaces) · 模組 25:Windows Server Backup 狀態檢查
🚀 高階功能(26-30): 模組 26:Docker / K8s 容器管理 · 模組 27:自動化修復嚮導 · 模組 28:效能基線 & 趨勢分析 · 模組 29:安全合規檢查清單 · 模組 30:遠端多伺服器管理
$scanPath = "C:\" # ← 改成你要掃的磁碟機代號或目錄
Get-ChildItem -Path $scanPath -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Length -gt 100MB } |
Sort-Object Length -Descending |
Select-Object -First 50 @{N='路徑';E={$_.FullName}},
@{N='大小(GB)';E={[math]::Round($_.Length/1GB,2)}},
@{N='最後修改';E={$_.LastWriteTime}}
**風險等級**:🟢 無(只讀)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Access to the path 'xxx' is denied` | 許可權不足 | 管理員身份執行 PowerShell |
| `Could not find a part of the path` | 目錄不存在 | 確認路徑 |
$scanPath = "D:\" # ← 掃描目標目錄
$minSize = 1MB # ← 只檢測大於 1MB 的檔案
Get-ChildItem -Path $scanPath -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Length -gt $minSize } |
ForEach-Object {
$hash = (Get-FileHash $_.FullName -Algorithm MD5 -ErrorAction SilentlyContinue).Hash
if ($hash) {
[PSCustomObject]@{
路徑 = $_.FullName
雜湊 = $hash
大小MB = [math]::Round($_.Length/1MB,2)
}
}
} |
Group-Object 雜湊 |
Where-Object { $_.Count -gt 1 } |
ForEach-Object { $_.Group }
**風險等級**:🟢 無(只讀)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Exception getting file hash` | 檔案正在使用 | 跳過 |
| `The specified path is not valid` | 路徑格式不對 | 檢查路徑 |
$tempPath = $env:TEMP
$daysOld = 7
$cutoff = (Get-Date).AddDays(-$daysOld)
$tempFiles = Get-ChildItem -Path $tempPath -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $cutoff }
Write-Host "═══════════════════════════════════════════"
Write-Host " 📋 掃描結果: $tempPath"
Write-Host " 過期臨時檔案數: $($tempFiles.Count) 個"
Write-Host "═══════════════════════════════════════════"
$tempFiles | Sort-Object Length -Descending |
Select-Object -First 20 |
Select-Object @{N='檔案路徑';E={$_.FullName}},
@{N='大小(KB)';E={[math]::Round($_.Length/1KB,1)}},
@{N='最後修改';E={$_.LastWriteTime}} |
Format-Table -AutoSize
$totalKB = ($tempFiles | Measure-Object -Property Length -Sum).Sum
Write-Host " 預計可釋放空間: $([math]::Round($totalKB/1MB,2)) MB"
Write-Host " ⚠️ 確認刪除請說 '確認清理'"
# ⚠️ 僅當用戶明確說「確認清理」後才執行!
Write-Host " 正在移到回收站..."
$shell = New-Object -ComObject Shell.Application
$rb = $shell.NameSpace(0x0a)
$totalFiles = 0
foreach ($f in $tempFiles) {
try {
$rb.MoveHere($f.FullName)
$totalFiles++
} catch { }
}
Write-Host " ✅ 已完成!已將 $totalFiles 個檔案移到回收站"
Write-Host " 如需徹底刪除,請手動清空回收站"
**風險等級**:🟡 中(需確認後刪除)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Cannot find path ... does not exist` | temp 目錄下某些路徑不存在 | 自動跳過 |
| `Access is denied` | 系統臨時檔案正在使用中 | 跳過,正常現象 |
| `MoveHere` 失敗 | 回收站操作異常 | 手動清或用其他方式 |
if (-not (Get-Module -ListAvailable -Name WebAdministration)) {
Write-Host "⚠️ IIS 管理工具未安裝,請以管理員執行:"
Install-WindowsFeature Web-Mgmt-Tools
}
Import-Module WebAdministration
Import-Module WebAdministration
Write-Host "════════ IIS 站點列表 ════════"
Get-Website | Select-Object @{N='站點名';E={$_.Name}},
@{N='狀態';E={$_.State}},
@{N='物理路徑';E={$_.PhysicalPath}},
@{N='應用池';E={$_.ApplicationPool}},
@{N='繫結';E={$_.Bindings -join ';'}} |
Format-Table -AutoSize
Write-Host "`n════════ 應用池狀態 ════════"
Get-WebAppPoolState | Select-Object @{N='名';E={$_.Name}}, @{N='狀態';E={$_.Value}} |
Format-Table -AutoSize
# ⚠️ 僅當用戶明確確認後才執行!
# Restart-WebSite -Name "你的站點名"
**風險等級**:🟢 檢視只讀 / 🟡 重啟需確認
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Get-Website is not recognized` | IIS 模組未載入 | `Install-WindowsFeature Web-Mgmt-Tools` |
| `Cannot connect to IIS` | W3SVC 服務未啟動 | 管理服務 |
| `Could not find WebAdministration` | 未裝 IIS 管理工具 | 新增 Web 管理工具 |
$criticalServices = @(
"W3SVC", # IIS
"MSSQLSERVER", # SQL Server
"MySQL80", # MySQL
"Redis", # Redis
"Elasticsearch",# ES
"Docker", # Docker
"Spooler", # 列印
"TermService", # 遠端桌面
"Nginx", # Nginx
"Apache", # Apache
"Tomcat" # Tomcat
)
Write-Host "════════ 關鍵服務狀態 ════════"
$hasStopped = $false
foreach ($svc in $criticalServices) {
$s = Get-Service -Name $svc -ErrorAction SilentlyContinue
if ($s) {
$icon = if ($s.Status -eq 'Running') { '✅' } else { '❌'; $hasStopped = $true }
Write-Host " $icon $($s.DisplayName) [$($s.Status)]"
}
}
Write-Host "═══════════════════════════════"
if ($hasStopped) { Write-Host " ⚠️ 有服務未執行" }
else { Write-Host " ✅ 所有檢測服務正常" }
**風險等級**:🟢 無(只讀)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Cannot find any service ...` | 該服務未安裝 | 自動跳過 |
$sourceDir = "D:\logs" # ← 源目錄
$searchFilter = "*.log" # ← 檔案篩選
$renamePrefix = "backup" # ← 新檔名字首
$files = Get-ChildItem -Path $sourceDir -Filter $searchFilter -File
Write-Host "════════ 批次重新命名預覽 ════════"
Write-Host " 掃描到 $($files.Count) 個 $searchFilter 檔案"
$i = 1
foreach ($f in $files) {
Write-Host " 🔄 $($f.Name) → ${renamePrefix}_${i}${f.Extension}"
$i++
}
Write-Host "`n 共 $($files.Count) 個檔案將被重新命名"
Write-Host " ⚠️ 確認執行請說 '確認重新命名'"
# ⚠️ 僅當用戶明確說「確認重新命名」後才執行!
# $i = 1
# foreach ($f in $files) {
# $newName = "${renamePrefix}_${i}${f.Extension}"
# Rename-Item -Path $f.FullName -NewName $newName
# Write-Host " ✅ $($f.Name) → $newName"
# $i++
# }
**風險等級**:🟡 中(需確認)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Cannot rename because item already exists` | 新檔名已存在 | 加時間戳避免重名 |
| `Access to the path is denied` | 檔案鎖定 | 關相關程式再試 |
function Get-DirectorySize([string]$Path) {
(Get-ChildItem -Path $Path -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
}
$dirs = @("C:\inetpub", "C:\Windows\Temp", "C:\ProgramData",
"$env:TEMP", "C:\Users",
"C:\Windows\SoftwareDistribution", "D:\")
foreach ($d in $dirs) {
if (Test-Path $d) {
$sizeGB = [math]::Round((Get-DirectorySize $d) / 1GB, 2)
if ($sizeGB -gt 10) { $icon = '🔴' }
elseif ($sizeGB -gt 5) { $icon = '🟡' }
else { $icon = '🟢' }
Write-Host " $icon $d : ${sizeGB} GB"
}
}
**風險等級**:🟢 無(只讀)
# 更新服務狀態
Get-Service -Name wuauserv |
Select-Object @{N='服務';E={$_.Name}},
@{N='狀態';E={$_.Status}},
@{N='啟動型別';E={$_.StartType}},
@{N='賬號';E={$_.StartName}}
Write-Host ""
# 已安裝補丁(最近 10 個)
Get-HotFix | Sort-Object InstalledOn -Descending |
Select-Object -First 10 @{N='補丁ID';E={$_.HotFixID}},
@{N='安裝日期';E={$_.InstalledOn}}
### 8.2 檢查 SoftwareDistribution 更新快取大小
$sd = "C:\Windows\SoftwareDistribution"
if (Test-Path $sd) {
$total = (Get-ChildItem -Path $sd -Recurse -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
Write-Host " SoftwareDistribution 大小: $([math]::Round($total/1MB,1)) MB"
$dlDir = Join-Path $sd "Download"
if (Test-Path $dlDir) {
$dlSize = (Get-ChildItem -Path $dlDir -Recurse -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
Write-Host " └─ 下載快取(Download): $([math]::Round($dlSize/1MB,1)) MB"
}
Write-Host " 💡 若超過 500MB 可考慮清理(需先停止 wuauserv 服務)"
} else {
Write-Host " ⚠️ SoftwareDistribution 目錄不存在"
}
# ⚠️ 僅當用戶明確說「確認清理更新快取」後才執行!
# Stop-Service -Name wuauserv -Force
# Remove-Item -Path "$sd\Download\*" -Recurse -Force -ErrorAction SilentlyContinue
# Start-Service -Name wuauserv
# Write-Host " ✅ 已清理更新下載快取,Windows Update 將重新下載所需補丁"
**風險等級**:🟢 檢視只讀 / 🟡 清理快取需確認
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `The service ... cannot be stopped` | 服務正在使用 | 稍後重試或重啟後清理 |
| `Access is denied` | 部分快取檔案被鎖定 | 先停服務再清理 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| Windows Update 一直不下載 | 服務掛起,重停重啟動可解決 |
| 錯誤碼 0x80070005 | 許可權問題,清理快取可修復 |
| 補丁安裝失敗 | 清理 Download 快取後重新檢查更新 |
Get-Counter '\Processor(_Total)\% Processor Time',
'\Memory\Available MBytes',
'\Memory\% Used',
'\PhysicalDisk(_Total)\% Disk Time',
'\PhysicalDisk(_Total)\Current Disk Queue Length' |
Select-Object -ExpandProperty CounterSamples |
Select-Object @{N='指標';E={$_.Path.Split('\')[-2..-1] -join '\'}},
@{N='當前值';E={[math]::Round($_.CookedValue, 1)}} |
Format-Table -AutoSize
### 9.2 記憶體佔用 Top 10 程序
Get-Process | Sort-Object WorkingSet64 -Descending |
Select-Object -First 10 @{N='程序名';E={$_.Name}},
@{N='記憶體(MB)';E={[math]::Round($_.WorkingSet64/1MB,1)}},
@{N='CPU(s)';E={[math]::Round($_.CPU,1)}} |
Format-Table -AutoSize
### 9.3 磁碟 I/O 詳細統計(按程序)
Get-Counter '\Process(*)\Read Bytes/sec',
'\Process(*)\Write Bytes/sec' |
Select-Object -ExpandProperty CounterSamples |
Where-Object { $_.CookedValue -gt 0 } |
Sort-Object CookedValue -Descending |
Select-Object -First 15 @{N='程序';E={
$_.Path -replace '\\Process\((.*)\)\\Read Bytes/sec$','$1' |
ForEach-Object { if ($_ -eq 'idle') { 'System Idle' } else { $_ }
}}, @{N='Bytes/s';E={[math]::Round($_.CookedValue, 0)}} |
Format-Table -AutoSize
# ⚠️ 僅當用戶明確說「確認建立」後才執行!
# $collector = New-DataCollectorSet -Name "ServerPerf_$(Get-Date -Format 'yyyyMMdd')" -Path "C:\PerfLogs"
# $collector.DataCollectors.Create(0, "Counter", 0,
# '\Processor(_Total)\% Processor Time',
# '\Memory\Available MBytes',
# '\PhysicalDisk(_Total)\% Disk Time'
# )
# $collector.Commit("C:\PerfLogs\ServerPerf", 0, 0x01) | Out-Null
# Write-Host " ✅ 已建立效能採集器,資料將儲存到 C:\PerfLogs"
**風險等級**:🟢 即時檢視 / 🟡 建立採集器需確認
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `The specified counter is not found` | 計數器未註冊 | 執行 `lodctr /R` 重建計數器 |
| `Access denied` | 需要管理員許可權 | 提升後執行 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| CPU 持續 >90% | 按程序排序定位異常程序 |
| 可用記憶體 <500MB | 判斷是否記憶體洩漏, IIS 應用池是否需回收 |
| 磁碟佇列長度 >2 | I/O 瓶頸,檢查是否日誌寫入過多 |
| 計數器全為零 | `lodctr /R` 重建效能計數器 |
$since = (Get-Date).AddHours(-24)
$failedLogins = Get-WinEvent -FilterHashtable @{
LogName='Security'; ID=4625; StartTime=$since
} -ErrorAction SilentlyContinue
if ($failedLogins) {
Write-Host "⚠️ 最近 24h 失敗登入 $($failedLogins.Count) 次`n"
$failedLogins |
Select-Object TimeCreated,
@{N='賬號';E={$_.Properties[5].Value}},
@{N='來源IP';E={$_.Properties[19].Value}} |
Group-Object SourceIP |
Sort-Object Count -Descending |
Select-Object -First 10 @{N='攻擊源IP';E={$_.Name}},
@{N='嘗試次數';E={$_.Count}} |
Format-Table -AutoSize
Write-Host " 💡 嘗試次數 >10 的 IP 建議加入防火牆黑名單"
} else {
Write-Host " ✅ 最近 24 小時無失敗登入記錄"
}
### 10.2 檢視特權提升操作(管理員操作審計)
$since = (Get-Date).AddHours(-24)
Write-Host "════════ 特權操作(4672)════════"
Get-WinEvent -FilterHashtable @{
LogName='Security'; ID=4672; StartTime=$since
} -ErrorAction SilentlyContinue |
Select-Object TimeCreated,
@{N='使用者';E={$_.Properties[1].Value}},
@{N='特權';E={$_.Properties[2..($_.Properties.Count-1)].Value -join ', '}} |
Format-Table -AutoSize
### 10.3 本地管理員組成員檢查
Write-Host "════════ 本地管理員組成員 ════════"
Get-LocalGroupMember -Group "Administrators" |
Select-Object @{N='使用者名稱';E={$_.Name}},
@{N='來源';E={$_.PrincipalSource}} |
Format-Table -AutoSize
# ⚠️ 僅當用戶明確說「確認匯出」後才執行!
# $logPath = "C:\Logs\Security_$(Get-Date -Format 'yyyyMMdd').evtx"
# if (-not (Test-Path "C:\Logs")) { New-Item -ItemType Directory -Path "C:\Logs" -Force }
# wevtutil epl Security $logPath
# Write-Host " ✅ 安全日誌已匯出到: $logPath"
**風險等級**:🟢 只讀審計 / 🟡 匯出需確認
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `No events were found ...` | 沒有符合條件的日誌 | 正常,說明期間無異常 |
| `The requested operation cannot be performed on a file ...` | 日誌檔案被佔用 | 複製到另一路徑匯出 |
| `Get-LocalGroupMember` 報錯 | 需要管理員許可權 | 提升執行 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 某 IP 失敗登入 >100 次 | 暴力破解,建議防火牆封禁 |
| 4672 事件頻繁 | 有人在頻繁執行管理員操作,需排查 |
| 管理員組成員異常多了不認識賬號 | 立即排查是否被入侵後留後門,刪非法賬號 |
$runKeys = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce",
"HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run"
)
Write-Host "════════ 自啟動登錄檔項 ════════"
foreach ($key in $runKeys) {
if (Test-Path $key) {
Write-Host "`n 📁 $($key.Split('\')[-1..-1] -join '\')"
Get-ItemProperty $key -ErrorAction SilentlyContinue |
Get-Member -MemberType NoteProperty |
Where-Object { $_.Name -notlike 'PS*' } |
ForEach-Object {
$val = (Get-ItemProperty $key -Name $_.Name -ErrorAction SilentlyContinue).$($_.Name)
Write-Host " 🔹 $($_.Name) = $val"
}
}
}
Write-Host "`n═══════════════════════════════"
Write-Host " 💡 不認識的程式請搜尋確認,不要直接刪除"
### 11.2 計劃任務審計
Write-Host "════════ 計劃任務列表 ════════"
Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' } |
Select-Object @{N='任務名';E={$_.TaskName}},
@{N='狀態';E={$_.State}},
@{N='作者';E={$_.Author}},
@{N='執行方式';E={$_.Principal.UserId}} |
Format-Table -AutoSize
Write-Host "`n════════ 最近修改的任務 ════════"
Get-ScheduledTask | Sort-Object LastWriteTime -Descending |
Select-Object -First 10 @{N='任務名';E={$_.TaskName}},
@{N='修改時間';E={$_.LastWriteTime}} |
Format-Table -AutoSize
### 11.3 WMI 持久化檢測
Write-Host "════════ WMI 事件訂閱 ════════"
try {
$wmiSubs = Get-CimInstance -Namespace root\subscription -ClassName __EventFilter -ErrorAction SilentlyContinue
if ($wmiSubs) {
$wmiSubs | Select-Object @{N='名稱';E={$_.Name}},
@{N='查詢';E={$_.Query}} |
Format-Table -AutoSize
} else {
Write-Host " ✅ 未發現 WMI 事件訂閱"
}
} catch {
Write-Host " ⚠️ WMI 查詢失敗,可能需要管理員許可權"
}
### 11.4 系統服務異常檢測
Write-Host "════════ 非微軟服務列表 ════════"
Get-Service | Where-Object {
$_.DisplayName -notlike '*Microsoft*' -and
$_.DisplayName -notlike '*Windows*' -and
$_.DisplayName -notlike '*Intel*' -and
$_.DisplayName -notlike '*NVIDIA*' -and
$_.DisplayName -notlike '*AMD*' -and
$_.DisplayName -notlike '*Realtek*' -and
$_.DisplayName -notlike '*HP*' -and
$_.DisplayName -notlike '*Dell*'
} | Select-Object @{N='服務名';E={$_.Name}},
@{N='顯示名';E={$_.DisplayName}},
@{N='狀態';E={$_.Status}},
@{N='啟動型別';E={$_.StartType}} |
Format-Table -AutoSize
**風險等級**:🟢 無(只讀審計)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Access to the path is denied` | 許可權不足 | 管理員身份執行 |
| `Requested registry access is not allowed` | 登錄檔許可權限制 | 提升許可權執行 |
| `Get-CimInstance` 報錯 | WMI 服務未啟動 | 檢查 Winmgmt 服務 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| Run 鍵值超多(>20個) | 正常軟體也會註冊,重點排查不認識的 |
| WMI 訂閱存在未知項 | 可能是惡意軟體持久化,需進一步分析 |
| 服務名隨機字串 | 典型惡意軟體特徵,需立即排查 |
Write-Host "════════ 磁碟 SMART 狀態 ════════"
Get-PhysicalDisk | Select-Object @{N='磁碟';E={$_.FriendlyName}},
@{N='狀態';E={$_.HealthStatus}},
@{N='使用時長';E={[math]::Round($_.UsageHours,0)}},
@{N='溫度(°C)';E={$_.Temperature}},
@{N='剩餘壽命%';E={$_.RemainingLifetimePercent}} |
Format-Table -AutoSize
### 12.2 磁碟壞道與錯誤計數
Write-Host "════════ 磁碟錯誤統計 ════════"
Get-PhysicalDisk | ForEach-Object {
$disk = $_
$errors = Get-StorageReliabilityCounter -PhysicalDisk $disk -ErrorAction SilentlyContinue
if ($errors) {
[PSCustomObject]@{
磁碟 = $disk.FriendlyName
讀取錯誤 = $errors.ReadErrorsTotal
寫入錯誤 = $errors.WriteErrorsTotal
重對映扇區 = $errors.ReallocatedSectors
待對映扇區 = $errors.PendingSectors
不可修復錯誤 = $errors.UncorrectableErrors
}
}
} | Format-Table -AutoSize
Write-Host "`n 💡 重對映扇區 >0 或待對映扇區 >0 表示磁碟開始老化"
Write-Host " 💡 不可修復錯誤 >0 建議立即備份資料並更換磁碟"
### 12.3 磁碟溫度監控
Write-Host "════════ 磁碟溫度監控 ════════"
Get-PhysicalDisk | ForEach-Object {
$temp = $_.Temperature
if ($temp -gt 50) { $icon = '🔴' }
elseif ($temp -gt 40) { $icon = '🟡' }
else { $icon = '🟢' }
[PSCustomObject]@{
磁碟 = $_.FriendlyName
溫度 = "${temp}°C"
狀態 = $icon
}
} | Format-Table -AutoSize
Write-Host "`n 💡 溫度 >50°C 需檢查散熱,>60°C 立即停機檢查"
### 12.4 磁碟 I/O 延遲分析
Write-Host "════════ 磁碟 I/O 延遲 ════════"
Get-Counter '\PhysicalDisk(*)\Avg. Disk sec/Read',
'\PhysicalDisk(*)\Avg. Disk sec/Write' |
Select-Object -ExpandProperty CounterSamples |
Where-Object { $_.CookedValue -gt 0 } |
Select-Object @{N='磁碟';E={
$_.Path -replace '\\PhysicalDisk\((.*)\)\\.*$','$1'
}}, @{N='平均延遲(ms)';E={[math]::Round($_.CookedValue * 1000, 2)}} |
Format-Table -AutoSize
Write-Host "`n 💡 讀延遲 >20ms 或寫延遲 >20ms 表示磁碟效能下降"
**風險等級**:🟢 無(只讀檢測)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `No MSFT_PhysicalDisk ...` | 系統不支援儲存診斷 | 需 Windows Server 2016+ |
| `Temperature` 屬性不存在 | 磁碟不支援溫度感測器 | 用第三方工具檢測 |
| `Get-StorageReliabilityCounter` 報錯 | 需要管理員許可權 | 提升執行 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| SMART 狀態顯示 "Warning" | 磁碟開始老化,立即備份資料 |
| 重對映扇區持續增長 | 磁碟壽命將盡,儘快更換 |
| I/O 延遲突然飆升 | 可能磁碟故障前兆,檢查 SMART |
Write-Host "════════ 活動網路連線 ════════"
Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue |
Select-Object @{N='本地地址';E={$_.LocalAddress}},
@{N='本地埠';E={$_.LocalPort}},
@{N='遠端地址';E={$_.RemoteAddress}},
@{N='遠端埠';E={$_.RemotePort}},
@{N='狀態';E={$_.State}},
@{N='程序';E={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Name}} |
Sort-Object RemoteAddress |
Format-Table -AutoSize
### 13.2 監聽埠審計
Write-Host "════════ 監聽埠列表 ════════"
Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue |
Select-Object @{N='本地地址';E={$_.LocalAddress}},
@{N='監聽埠';E={$_.LocalPort}},
@{N='程序';E={
$proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
if ($proc) { "$($proc.Name) (PID: $($proc.Id))" } else { "未知" }
}} |
Sort-Object LocalPort |
Format-Table -AutoSize
### 13.3 可疑外連檢測(高頻連線分析)
Write-Host "════════ 高頻外連目標 ════════"
Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue |
Group-Object RemoteAddress |
Where-Object { $_.Count -gt 5 } |
Sort-Object Count -Descending |
Select-Object -First 15 @{N='遠端IP';E={$_.Name}},
@{N='連線數';E={$_.Count}},
@{N='程序';E={
$proc = Get-Process -Id ($_.Group[0].OwningProcess) -ErrorAction SilentlyContinue
if ($proc) { $proc.Name } else { "未知" }
}} |
Format-Table -AutoSize
Write-Host "`n 💡 連線數 >20 的 IP 需重點排查是否為惡意外連"
### 13.4 防火牆規則審計
Write-Host "════════ 防火牆入站規則 ════════"
Get-NetFirewallRule -Direction Inbound -Enabled True -ErrorAction SilentlyContinue |
Where-Object { $_.Action -eq 'Allow' } |
Select-Object @{N='規則名';E={$_.DisplayName}},
@{N='操作';E={$_.Action}},
@{N='本地埠';E={$_.LocalPort}},
@{N='遠端地址';E={$_.RemoteAddress}} |
Format-Table -AutoSize
**風險等級**:🟢 無(只讀檢測)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Get-NetTCPConnection` 報錯 | 需要管理員許可權 | 提升執行 |
| `Get-NetFirewallRule` 報錯 | 防火牆服務未啟動 | 檢查 mpssvc 服務 |
| `OwningProcess` 為 0 | 系統程序,正常 | 無需處理 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 未知程序監聽 80/443 埠 | 可能是 Web 服務或惡意軟體,需排查 |
| 大量外連到同一 IP | 可能是 C2 通訊,立即斷網排查 |
| 防火牆規則異常寬鬆 | 建議收緊為最小許可權原則 |
$since = (Get-Date).AddHours(-2)
Write-Host "════════ 最近 2 小時系統日誌(錯誤/警告)════════"
Get-WinEvent -FilterHashtable @{
LogName='System';Level=2,3;StartTime=$since
} -ErrorAction SilentlyContinue |
Select-Object @{N='時間';E={$_.TimeCreated}},
@{N='來源';E={$_.Message.Split("`r`n")[0] -replace '^.*?: '}},
@{N='級別';E={if ($_.Level -eq 2) { '❌錯誤' } else { '⚠️警告' }}} |
Select-Object -First 30 |
Format-Table -AutoSize -Wrap
### 14.2 應用程式錯誤日誌
$since = (Get-Date).AddHours(-24)
Write-Host "════════ 最近 24 小時應用程式錯誤 ════════"
Get-WinEvent -FilterHashtable @{
LogName='Application'; Level=2; StartTime=$since
} -ErrorAction SilentlyContinue |
Select-Object @{N='時間';E={$_.TimeCreated}},
@{N='來源';E={$_.ProviderName}},
@{N='錯誤摘要';E={
($_.Message -replace "`r`n",' ' | Select-Object -First 120)
}} |
Select-Object -First 20 |
Format-Table -AutoSize -Wrap
### 14.3 關鍵系統事件掃描(藍色畫面/重啟/故障)
Write-Host "════════ 關鍵系統事件(最近 7 天)════════"
Get-WinEvent -FilterHashtable @{
LogName='System';
ID=1074, 6005, 6006, 6008, 6009, 41, 1001, 1002;
StartTime=(Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue |
Select-Object @{N='時間';E={$_.TimeCreated}},
@{N='事件ID';E={$_.Id}},
@{N='說明';E={
switch ($_.Id) {
1074 {'系統關機/重啟 (User initiated)'}
6005 {'系統啟動'}
6006 {'系統正常關機'}
6008 {'非正常關機 (意外斷電/崩潰)'}
6009 {'系統啟動 (新會話)'}
41 {'Kernel-Power 重啟 (無干淨關機)'}
1001{'BugCheck (藍色畫面)'}
1002{'應用程式掛起/無響應'}
default {$_.Message.Split("`r`n")[0]}
}
}} |
Format-Table -AutoSize -Wrap
### 14.4 Setup 日誌審計(最近 7 天)
Write-Host "════════ Setup 最近 7 天事件 ════════"
Get-WinEvent -FilterHashtable @{
LogName='Setup';
StartTime=(Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue |
Select-Object @{N='時間';E={$_.TimeCreated}},
@{N='來源';E={$_.ProviderName}},
@{N='摘要';E={
($_.Message -replace "`r`n",' ' | Select-Object -First 120)
}} |
Select-Object -First 15 |
Format-Table -AutoSize -Wrap
**風險等級**:🟢 無(只讀閱讀)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `No events were found ...` | 沒有符合條件的日誌 | 正常,說明期間無異常 |
| `RPC server is unavailable` | 事件日誌服務未執行 | 檢查 EventLog 服務 |
| `Access denied` | 許可權不足 | 管理員身份執行 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 每天大量 Error 日誌 | 說明存在持續性故障,需逐條排查來源 |
| 非正常關機 (6008) | 可能存在硬體/電源問題 |
| 藍色畫面 (1001) | 記錄藍色畫面程式碼,排查驅動/硬體 |
Write-Host "════════ 已安裝程式清單 ════════"
Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -and $_.UninstallString -notlike '*{*' } |
Select-Object @{N='程式名';E={$_.DisplayName}},
@{N='版本';E={$_.DisplayVersion}},
@{N='安裝日期';E={$_.InstallDate}},
@{N='大小';E={if($_.EstimatedSize){[math]::Round($_.EstimatedSize/1MB,1)}else{'?'}}} |
Sort-Object DisplayName |
Format-Table -AutoSize
### 15.2 已安裝 Windows 補丁
Write-Host "════════ 已安裝 Windows 補丁 ════════"
Get-HotFix | Sort-Object InstalledOn -Descending |
Select-Object @{N='補丁ID';E={$_.HotFixID}},
@{N='安裝日期';E={$_.InstalledOn}},
@{N='描述';E={$_.Description}} |
Format-Table -AutoSize
### 15.3 安裝時間線分析
Write-Host "════════ 最近安裝的軟體 ════════"
Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
Where-Object { $_.InstallDate } |
Sort-Object InstallDate -Descending |
Select-Object -First 20 @{N='程式名';E={$_.DisplayName}},
@{N='版本';E={$_.DisplayVersion}},
@{N='安裝日期';E={$_.InstallDate}} |
Format-Table -AutoSize
### 15.4 補丁對比(檢測缺失)
$hotfixes = Get-HotFix -ErrorAction SilentlyContinue | Select-Object -ExpandProperty HotFixID
$currentBuild = [System.Environment]::OSVersion.Version
$expectedPatches = @()
# 檢測 Windows 10/11 常見缺失補丁
if ($currentBuild.Major -ge 10) {
$kbToCheck = @(
"KB5034441", # Secure Boot DBX
"KB5036897", # 最新 .NET
"KB5037771" # 最新累積
)
foreach ($kb in $kbToCheck) {
if ($hotfixes -notcontains $kb) {
$expectedPatches += "$kb - 缺失"
}
}
}
Write-Host "════════ 補丁一致性檢查 ════════"
Write-Host " 當前版本: $($currentBuild.Major).$($currentBuild.Minor).$($currentBuild.Build)"
Write-Host ""
if ($expectedPatches.Count -gt 0) {
Write-Host " ⚠️ 缺失補丁:"
foreach ($p in $expectedPatches) { Write-Host " ❌ $p" }
} else {
Write-Host " ✅ 常用補丁均已安裝"
}
**風險等級**:🟢 無(只讀閱讀)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Requested registry access is not allowed` | 部分登錄檔許可權限制 | 管理員身份執行 |
| `Get-HotFix` 報錯 | 需要管理員許可權 | 管理員身份執行 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 某臺伺服器明顯比其他機器少很多補丁 | 可能 WSUS/自動更新未執行 |
| 安裝時間線上出現未知日期的軟體 | 可能未經授權安裝,需排查 |
| KB 補丁安裝失敗 | Windows Update 服務或快取異常 |
Write-Host "════════ 當前登入使用者 ════════"
Get-CimInstance Win32_LogonSession | Where-Object { $_.LogonType -ne 0 } |
Select-Object @{N='使用者名稱';E={
$user = Get-CimInstance Win32_ComputerSystem | Select-Object UserName
$user.UserName ?? "未知"
}},
@{N='會話ID';E={$_.LogonId}},
@{N='登入型別';E={
switch ($_.LogonType) {
2 {'互動 (本地登入)'}
3 {'網路登入'}
4 {'批處理'}
5 {'服務'}
7 {'解鎖'}
8 {'網路明文'}
9 {'新憑據'}
10 {'遠端互動 (RDP)'}
11 {'快取互動'}
default {"其他 ($($_.LogonType))"}
}
}},
@{N='登入時間';E={$_.StartTime}} |
Format-Table -AutoSize
### 16.2 遠端桌面會話監控
Write-Host "════════ 遠端桌面會話 (RDP) ════════"
Get-CimInstance Win32_LogonSession | Where-Object { $_.LogonType -eq 10 } |
Select-Object @{N='使用者名稱';E={
$user = Get-CimInstance Win32_ComputerSystem | Select-Object UserName
$user.UserName ?? "未知"
}},
@{N='會話ID';E={$_.LogonId}},
@{N='登入時間';E={$_.StartTime}},
@{N='空閒時間(分鐘)';E={
if ($_.StartTime) {
[math]::Round(((Get-Date) - $_.StartTime).TotalMinutes, 0)
} else { 0 }
}} |
Format-Table -AutoSize
Write-Host "`n💡 空閒 >60 分鐘的會話可能為殭屍會話"
### 16.3 異常登入檢測(多 IP / 異地登入)
Write-Host "════════ 最近 7 天的登入來源 ════════"
Get-WinEvent -FilterHashtable @{
LogName='Security';
ID=4624;
StartTime=(Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue |
Where-Object { $_.Properties[18].Value -ne '-' } |
Select-Object @{N='登入時間';E={$_.TimeCreated}},
@{N='使用者名稱';E={$_.Properties[5].Value}},
@{N='來源IP';E={$_.Properties[18].Value}},
@{N='登入型別';E={
switch ($_.Properties[8].Value) {
2 {'互動(本地)'}
3 {'網路'}
10 {'遠端(RDP)'}
default {"其他"}
}
}} |
Group-Object SourceIP |
Sort-Object Count -Descending |
Select-Object -First 10 @{N='來源IP';E={$_.Name}},
@{N='登入次數';E={$_.Count}},
@{N='最近登入';E={$_.Group[0].TimeCreated}} |
Format-Table -AutoSize
Write-Host "`n💡 任何未知來源IP 的登入都值得警惕"
### 16.4 系統賬戶狀態檢查
Write-Host "════════ 本地使用者賬戶狀態 ════════"
Get-LocalUser | Where-Object { $_.Enabled -eq $true } |
Select-Object @{N='賬戶名';E={$_.Name}},
@{N='狀態';E={if ($_.Enabled) {'啟用'}else{'停用'}}},
@{N='最後登入';E={$_.LastLogon}},
@{N='密碼過期';E={
if ($_.PasswordExpires) { $_.PasswordExpires } else { '永不過期 ⚠️' }
}},
@{N='需要密碼';E={if ($_.PasswordRequired) {'是'}else{'否 ⚠️'}}} |
Format-Table -AutoSize
Write-Host "`n⚠️ 標記的賬戶存在安全風險:無密碼或密碼永不過期"
**風險等級**:🟢 無(只讀監控)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Get-LocalUser` 報錯 | 需要管理員許可權 | 管理員身份執行 |
| `Get-CimInstance` 報錯 | WMI 未啟動 | 檢查 Winmgmt |
| `No events found` | 沒有相關日誌 | 正常 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 多個 RDP 會話來自同一 IP | 可能正常(NAT),也可能為多人共用賬戶 |
| Guest 賬戶啟用 | 不必要的安全風險,建議停用 |
| 殭屍 RDP 會話 | 佔用資源,閒置超時後應自動斷開 |
| 密碼永不過期 | 合規風險,建議設定密碼策略 |
Write-Host "════════ 所有計劃任務 ════════"
$tasks = Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' }
$total = ($tasks | Measure-Object).Count
Write-Host " 總數: $total 個啟用的任務`n"
$tasks | Select-Object @{N='任務名';E={$_.TaskName}},
@{N='路徑';E={$_.TaskPath}},
@{N='狀態';E={$_.State}},
@{N='觸發器';E={
$info = Get-ScheduledTaskInfo -TaskName $_.TaskName -TaskPath $_.TaskPath -ErrorAction SilentlyContinue
if ($info.LastRunTime) { "上次: $($info.LastRunTime)" } else { "從未執行" }
}} |
Sort-Object TaskPath, TaskName |
Format-Table -AutoSize
### 17.2 異常計劃任務檢測(凌晨執行 / 隱藏視窗 / 臨時目錄)
Write-Host "════════ 異常計劃任務檢測 ════════"
$suspicious = @()
$tasks = Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' }
foreach ($task in $tasks) {
$score = 0
$reasons = @()
$taskInfo = Get-ScheduledTaskInfo -TaskName $task.TaskName -TaskPath $task.TaskPath -ErrorAction SilentlyContinue
$lastRun = if ($taskInfo.LastRunTime) { $taskInfo.LastRunTime } else { $null }
# 檢測1: 凌晨執行 (0:00-5:00)
# 通過檢查 Actions 中的命令列引數
$actions = $task.Actions
foreach ($action in $actions) {
$argStr = "$($action.Arguments)" + "$($action.Execute)"
if ($argStr -match '(-WindowStyle\s+Hidden|/hidden|-w\s+hidden|-hide|/background|/min|-NoLogo\s+-NonInteractive\s+-WindowStyle\s+Hidden)') {
$score += 2
$reasons += "隱藏視窗執行"
}
if ($argStr -match '(\\Temp\\|\\TMP\\|\\AppData\\Local\\Temp\\)') {
$score += 3
$reasons += "從臨時目錄執行"
}
if ($argStr -match '((?:powershell|cmd|wscript|cscript|rundll32|mshta|regsvr32).*?(?:-enc|-e\s|IEX|Invoke-Expression|DownloadString|FromBase64String|eval))') {
$score += 4
$reasons += "可疑命令特徵(編碼/下載)"
}
}
# 檢測2: 無描述資訊的任務
if (-not $task.Description -or $task.Description -eq '') {
$score += 1
$reasons += "無描述資訊"
}
if ($score -ge 3) {
$suspicious += [PSCustomObject]@{
任務名 = $task.TaskName
路徑 = $task.TaskPath
風險分 = $score
原因 = ($reasons -join ', ')
上次執行 = $lastRun
}
}
}
if ($suspicious.Count -gt 0) {
$suspicious | Sort-Object 風險分 -Descending |
Format-Table -AutoSize -Wrap
Write-Host "`n🔴 以上任務存在可疑特徵,建議逐條排查"
} else {
Write-Host "✅ 未發現明顯可疑的計劃任務"
}
### 17.3 按觸發條件分類(開機自啟 / 定時 / 使用者登入觸發)
Write-Host "════════ 計劃任務按觸發型別分類 ════════`n"
# 開機自啟
Write-Host "═══ 開機/系統啟動時觸發 ═══"
Get-ScheduledTask | Where-Object {
$_.Triggers | Where-Object { $_.CimClass.CimClassName -match 'BootTrigger|StartupTrigger' }
} | Select-Object -First 15 @{N='任務名';E={$_.TaskName}},
@{N='路徑';E={$_.TaskPath}},
@{N='觸發器';E={
($_.Triggers | ForEach-Object { $_.CimClass.CimClassName }) -join ', '
}} |
Format-Table -AutoSize
Write-Host "`n═══ 使用者登入時觸發 ═══"
Get-ScheduledTask | Where-Object {
$_.Triggers | Where-Object { $_.CimClass.CimClassName -match 'LogonTrigger' }
} | Select-Object -First 15 @{N='任務名';E={$_.TaskName}},
@{N='路徑';E={$_.TaskPath}},
@{N='觸發器';E={
($_.Triggers | ForEach-Object { $_.CimClass.CimClassName }) -join ', '
}} |
Format-Table -AutoSize
Write-Host "`n═══ 定時/週期性觸發 (數量最多) ═══"
$timeTasks = Get-ScheduledTask | Where-Object {
$_.Triggers | Where-Object { $_.CimClass.CimClassName -match 'TimeTrigger|DailyTrigger|WeeklyTrigger|MonthlyTrigger' }
}
Write-Host " 總數: $(($timeTasks | Measure-Object).Count) 個"
### 17.4 非 Microsoft 建立的任務
**風險等級**:🟢 無(只讀審計) | 報錯 | 含義 | 解決 | |-----|------|-----| | `Access denied` | 許可權不足 | 管理員身份執行 | | `The system cannot find the file specified` | 任務引用的程式已刪除 | 正常(殘留任務),值得清理 | | `Task Scheduler service is not running` | 計劃任務服務未啟動 | `Start-Service Schedule` | **常見坑 & 解決**: | 場景 | 說明 | |-----|------| | 大量凌晨執行的隱藏視窗任務 | 軟體更新常見,但需逐條確認來源 | | 從 Temp 目錄執行的任務 | 高危特徵,多數惡意軟體行為 | | 無描述的隨機名任務 | 可能是病毒/蠕蟲建立,建議隔離排查 | | 編碼命令 (Base64) 執行 | 極高風險,常見於挖礦指令碼和外連木馬 |Write-Host "════════ 非 Microsoft 建立的計劃任務 ════════" $nonMsTasks = Get-ScheduledTask | Where-Object { $_.TaskPath -notmatch '\\\\Microsoft\\\\' -and $_.State -ne 'Disabled' } if ($nonMsTasks) { $nonMsTasks | Select-Object @{N='任務名';E={$_.TaskName}}, @{N='路徑';E={$_.TaskPath}}, @{N='狀態';E={$_.State}}, @{N='描述';E={ if ($_.Description) { $_.Description.Substring(0, [Math]::Min(60, $_.Description.Length)) } else { '(無描述)' } }} | Format-Table -AutoSize Write-Host "`n💡 第三方任務值得審查,尤其是無描述且來源不明的" } else { Write-Host "✅ 無非 Microsoft 計劃任務" }本技能來自小蔥技能站7w4.net。
Write-Host "════════ 已共享資料夾 ════════"
Get-SmbShare | Where-Object { $_.Name -notin @('IPC$','ADMIN$') -or $_.Special -eq $false } |
Select-Object @{N='共享名';E={$_.Name}},
@{N='路徑';E={$_.Path}},
@{N='描述';E={if($_.Description){$_.Description}else{'(無)'}}},
@{N='最大使用者數';E={$_.ConcurrentUserLimit}},
@{N='快取模式';E={$_.CachingMode}},
@{N='共享狀態';E={$_.ShareState}} |
Format-Table -AutoSize
Write-Host "`n💡 ADMIN$ 和 IPC$ 為系統預設管理共享,屬正常"
### 18.2 共享許可權審計
Write-Host "════════ 共享許可權審計 ════════"
$shares = Get-SmbShare | Where-Object { $_.Name -notin @('IPC$','ADMIN$') -or $_.Special -eq $false }
foreach ($share in $shares) {
Write-Host "`n═══ 共享: $($share.Name) → $($share.Path) ═══"
# 共享級別許可權
try {
$sharePerm = Get-SmbShareAccess -Name $share.Name -ErrorAction SilentlyContinue
foreach ($perm in $sharePerm) {
$flag = if ($perm.AccessRight -eq 'Full' -and $perm.AccountName -eq 'Everyone') {
"🔴 高風險: Everyone 擁有完全控制"
} elseif ($perm.AccountName -eq 'Everyone') {
"⚠️ Everyone 可訪問"
} else {
""
}
Write-Host " 共享許可權: $($perm.AccountName) -> $($perm.AccessRight) $flag"
}
} catch {
Write-Host " 共享許可權: 無法獲取"
}
# NTFS 級別許可權(對物理路徑)
if (Test-Path $share.Path) {
$ntfsPerm = Get-Acl $share.Path -ErrorAction SilentlyContinue
if ($ntfsPerm) {
$ntfsPerm.Access | Where-Object { $_.IdentityReference -match 'Everyone|BUILTIN|Guest|ANONYMOUS' } |
ForEach-Object {
Write-Host " NTFS許可權: $($_.IdentityReference) -> $($_.FileSystemRights) ⚠️ 寬鬆許可權"
}
}
}
}
### 18.3 當前 SMB 連線會話
Write-Host "════════ 當前 SMB 連線會話 ════════"
$sessions = Get-SmbSession -ErrorAction SilentlyContinue |
Select-Object @{N='客戶端';E={$_.ClientComputerName}},
@{N='使用者名稱';E={$_.UserName}},
@{N='空閒時間(分鐘)';E={[math]::Round($_.IdleTime.TotalMinutes, 0)}},
@{N='會話時長(分鐘)';E={[math]::Round((New-TimeSpan -Start $_.SessionStartTime).TotalMinutes, 0)}} |
Sort-Object @{E='空閒時間(分鐘)';Descending=$true}
if ($sessions) {
$sessions | Format-Table -AutoSize
Write-Host "`n💡 空閒 >60 分鐘的會話可能為殭屍連線"
} else {
Write-Host " 當前無活躍 SMB 會話"
}
### 18.4 開放共享中的風險項彙總
Write-Host "════════ 共享風險彙總 ════════"
$risks = @()
$shares = Get-SmbShare | Where-Object { $_.Name -notin @('IPC$','ADMIN$') -or $_.Special -eq $false }
foreach ($share in $shares) {
$sharePerm = Get-SmbShareAccess -Name $share.Name -ErrorAction SilentlyContinue
foreach ($perm in $sharePerm) {
if ($perm.AccountName -eq 'Everyone') {
$risks += "🔴 [$($share.Name)] Everyone -> $($perm.AccessRight) 控制權"
}
if ($perm.AccountName -eq 'ANONYMOUS LOGON' -or $perm.AccountName -eq 'Guest') {
$risks += "🔴 [$($share.Name)] $($perm.AccountName) -> $($perm.AccessRight)"
}
}
# 檢查共享指向路徑不存在
if (-not (Test-Path $share.Path)) {
$risks += "⚠️ [$($share.Name)] 共享路徑不存在: $($share.Path)"
}
}
if ($risks.Count -gt 0) {
$risks | ForEach-Object { Write-Host $_ }
Write-Host "`n建議: 收緊共享許可權為最小訪問原則,停用 Guest/匿名訪問"
} else {
Write-Host "✅ 未發現高風險的共享配置"
}
# 同時檢查 SMBv1 是否啟用
$smb1 = Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -ErrorAction SilentlyContinue
if ($smb1 -and $smb1.State -eq 'Enabled') {
Write-Host "`n🔴 SMBv1 已啟用!存在 WannaCry 等勒索軟體漏洞風險,建議停用"
}
**風險等級**:🟢 無(只讀審計)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Get-SmbShare` 報錯 | SMB 未啟用或無許可權 | 管理員身份在伺服器上執行 |
| `Get-SmbSession` 為空 | 當前無 SMB 連線 | 正常 |
| `Access denied on Get-Acl` | 部分目錄 NTFS 許可權限制 | 某些系統目錄屬正常 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| Everyone 擁有完全控制 | 任何網路使用者可讀/寫,極大風險 |
| SMBv1 仍啟用 | 永恆之藍等漏洞存在,應立即停用 |
| 指向不存在的路徑 | 殘留共享,建議刪除 |
| 大量殭屍 SMB 會話 | 客戶端未正常斷開,佔用連線數 |
Write-Host "════════ DNS 快取 ════════"
$dnsCache = Get-DnsClientCache -ErrorAction SilentlyContinue
if ($dnsCache) {
Write-Host " 快取條目: $(($dnsCache | Measure-Object).Count)"
$dnsCache | Select-Object -First 20 @{N='域名';E={$_.Entry}},
@{N='型別';E={$_.RecordType}},
@{N='IP地址';E={$_.Data}},
@{N='TTL(秒)';E={$_.TimeToLive}} |
Format-Table -AutoSize
} else {
Write-Host " 當前無 DNS 快取"
}
Write-Host "`n════════ DNS 快取中異常的記錄 (TTL異常長) ════════"
if ($dnsCache) {
$abnormal = $dnsCache | Where-Object { $_.TimeToLive -gt 86400 }
if ($abnormal) {
$abnormal | Select-Object Entry, Data, TimeToLive | Format-Table -AutoSize
} else {
Write-Host " ✅ 無異常記錄"
}
}
Write-Host "`n💡 如需清空 DNS 快取,說:「確認清空 DNS 快取」"
Write-Host " 清空命令: Clear-DnsClientCache (清空後會自動從DNS伺服器重新獲取)"
### 19.2 DNS 伺服器配置與解析鏈路測試
Write-Host "════════ DNS 伺服器配置 ════════"
Get-DnsClientServerAddress -AddressFamily IPv4 |
Where-Object { $_.ServerAddresses.Count -gt 0 } |
Select-Object @{N='網絡卡';E={$_.InterfaceAlias}},
@{N='索引';E={$_.InterfaceIndex}},
@{N='DNS 伺服器';E={($_.ServerAddresses -join ', ')}} |
Format-Table -AutoSize
Write-Host "`n════════ 解析鏈路測試 (逐級 DNS) ════════"
$testDomains = @('www.baidu.com', 'www.google.com', 'portal.azure.com')
foreach ($domain in $testDomains) {
try {
$result = Resolve-DnsName -Name $domain -Type A -ErrorAction Stop
$ip = ($result | Where-Object { $_.Type -eq 'A' } | Select-Object -First 1).IPAddress
if ($ip) { Write-Host " ✅ $domain → $ip" }
else { Write-Host " ❌ $domain → 解析失敗" }
} catch {
Write-Host " ❌ $domain → $($_.Exception.Message.Split('.')[0])"
}
}
### 19.3 網絡卡 IP 配置概覽
Write-Host "════════ 網絡卡 IP 配置 ════════"
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.InterfaceAlias -notmatch 'Loopback' -and $_.IPAddress -ne '127.0.0.1' } |
Select-Object @{N='網絡卡';E={$_.InterfaceAlias}},
@{N='IP 地址';E={$_.IPAddress}},
@{N='子網掩碼';E={$_.PrefixLength}},
@{N='閘道器';E={
$ifIndex = $_.InterfaceIndex
$gw = Get-NetRoute -InterfaceIndex $ifIndex -DestinationPrefix '0.0.0.0/0' -ErrorAction SilentlyContinue
if ($gw) { $gw.NextHop } else { '-' }
}},
@{N='狀態';E={$_.AddressState}} |
Format-Table -AutoSize
Write-Host "`n════════ 網絡卡 MAC 地址與速率 ════════"
Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } |
Select-Object @{N='網絡卡名';E={$_.Name}},
@{N='MAC';E={$_.MacAddress}},
@{N='速率';E={if($_.LinkSpeed){ "$([math]::Round($_.LinkSpeed/1e9,1)) Gbps" }else{'未知'}}},
@{N='狀態';E={$_.Status}} |
Format-Table -AutoSize
### 19.4 路由表檢查
Write-Host "════════ IPv4 路由表 ════════"
Get-NetRoute -AddressFamily IPv4 |
Where-Object { $_.DestinationPrefix -ne '255.255.255.255/32' } |
Sort-Object @{E={if ($_.DestinationPrefix -eq '0.0.0.0/0') {0} else {1}}},
@{E='RouteMetric'} |
Select-Object @{N='目標網路';E={$_.DestinationPrefix}},
@{N='下一跳';E={$_.NextHop}},
@{N='介面';E={$_.InterfaceAlias}},
@{N='躍點數';E={$_.RouteMetric}},
@{N='協議';E={$_.Protocol}} |
Format-Table -AutoSize
Write-Host "`n💡 躍點數越小優先順序越高"
Write-Host "💡 0.0.0.0/0 為預設路由(所有出網流量由此控制)"
**風險等級**:🟡 中(含 DNS 快取清空操作,需使用者確認)
| 操作 | 風險 | 說明 |
|------|------|------|
| DNS 快取檢視 | 🟢 無 | 純只讀 |
| DNS 解析測試 | 🟢 無 | 純只讀,零風險 |
| 網絡卡配置檢視 | 🟢 無 | 純只讀 |
| 路由表檢視 | 🟢 無 | 純只讀 |
| **DNS 快取清空** | 🟡 中 | 清空後需使用者說「確認清空 DNS 快取」,清空後自動從 DNS 伺服器重建,非破壞性 |
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Resolve-DnsName` 超時 | DNS 伺服器不可達 | 檢查 DNS 伺服器配置和網路連通性 |
| `Get-NetIPAddress` 為空 | 網絡卡未分配 IPv4 | 檢查 DHCP 或靜態 IP 配置 |
| `Get-NetRoute` 無預設路由 | 無網際網路出口 | 檢查閘道器配置 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| DNS 快取中大量 TTL 異常長的記錄 | 可能 DNS 伺服器配置問題或域名劫持 |
| 多網絡卡存在多個預設路由 | 路由衝突導致網路不穩定 |
| DNS 伺服器指向不可達 IP | 域名解析全部失敗 |
| 網絡卡速率不匹配(1Gbps vs 100Mbps) | 網線/交換機埠故障 |
Write-Host "════════ 本機證書過期檢查 ════════"
$warningDays = 30
$now = Get-Date
$stores = @(
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine,
[System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
)
$storeNames = @('My', 'WebHosting', 'Root', 'CA')
$certs = foreach ($loc in $stores) {
foreach ($name in $storeNames) {
try {
$store = [System.Security.Cryptography.X509Certificates.X509Store]::new($name, $loc)
$store.Open('ReadOnly')
foreach ($cert in $store.Certificates) {
$daysLeft = ($cert.NotAfter - $now).Days
[PSCustomObject]@{
主體 = ($cert.Subject -replace 'CN=', '' -split ',')[0].Trim()
儲存位置 = "$loc\$name"
到期時間 = $cert.NotAfter.ToString('yyyy-MM-dd')
剩餘天數 = $daysLeft
狀態 = if ($daysLeft -lt 0) { '❌ 已過期' }
elseif ($daysLeft -lt $warningDays) { '⚠️ 即將過期' }
else { '✅ 正常' }
頒發者 = ($cert.Issuer -replace 'CN=', '' -split ',')[0].Trim()
}
}
$store.Close()
} catch {}
}
}
$result = $certs | Where-Object { $_.剩餘天數 -lt 90 } |
Sort-Object 剩餘天數
if ($result) {
$result | Format-Table -AutoSize
} else {
Write-Host "✅ 未發現 90 天內到期的證書"
}
### 20.2 IIS 繫結的 HTTPS 證書檢查
Write-Host "════════ IIS HTTPS 繫結證書 ════════"
Import-Module WebAdministration -ErrorAction SilentlyContinue
$httpsBindings = Get-WebBinding | Where-Object { $_.protocol -eq 'https' }
if (-not $httpsBindings) {
Write-Host " 未發現 HTTPS 繫結"
} else {
foreach ($binding in $httpsBindings) {
$hash = $binding.certificateHash
if ($hash) {
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $hash }
if ($cert) {
$daysLeft = ($cert.NotAfter - (Get-Date)).Days
$status = if ($daysLeft -lt 0) { '❌ 已過期' }
elseif ($daysLeft -lt 30) { '⚠️ 即將過期' }
else { '✅ 正常' }
Write-Host " 站點: $($binding.bindingInformation)"
Write-Host " 證書: $($cert.Subject)"
Write-Host " 到期: $($cert.NotAfter.ToString('yyyy-MM-dd')) 剩餘: $daysLeft 天 $status"
Write-Host ""
}
}
}
}
### 20.3 遠端域名證書探測(本地無證書的站點)
# 將下方域名替換為你需要檢測的站點
$domains = @('www.baidu.com', 'www.taobao.com') # 示例,替換為你的域名
Write-Host "════════ 遠端域名 SSL 證書檢測 ════════"
foreach ($domain in $domains) {
try {
$tcpClient = New-Object System.Net.Sockets.TcpClient($domain, 443)
$sslStream = New-Object System.Net.Security.SslStream($tcpClient.GetStream(), $false, { $true })
$sslStream.AuthenticateAsClient($domain)
$cert = $sslStream.RemoteCertificate
$expiry = [DateTime]::Parse($cert.GetExpirationDateString())
$daysLeft = ($expiry - (Get-Date)).Days
$status = if ($daysLeft -lt 0) { '❌ 已過期' }
elseif ($daysLeft -lt 30) { '⚠️ 即將過期' }
else { '✅ 正常' }
Write-Host " $domain → 到期: $($expiry.ToString('yyyy-MM-dd')) 剩餘: $daysLeft 天 $status"
$sslStream.Close()
$tcpClient.Close()
} catch {
Write-Host " $domain → ❌ 連線失敗: $($_.Exception.Message.Split('.')[0])"
}
}
### 20.4 即將過期證書彙總報告
Write-Host "════════ 30 天內到期證書彙總 ════════"
$now = Get-Date
$soon = @()
foreach ($loc in @('LocalMachine','CurrentUser')) {
foreach ($name in @('My','WebHosting')) {
try {
$store = [System.Security.Cryptography.X509Certificates.X509Store]::new($name, $loc)
$store.Open('ReadOnly')
$soon += $store.Certificates | Where-Object {
($_.NotAfter - $now).Days -le 30 -and ($_.NotAfter - $now).Days -ge 0
} | Select-Object @{N='證書名';E={($_.Subject -replace 'CN=','' -split ',')[0].Trim()}},
@{N='剩餘天數';E={($_.NotAfter - $now).Days}},
@{N='到期日期';E={$_.NotAfter.ToString('yyyy-MM-dd')}},
@{N='儲存';E={"$loc\$name"}}
$store.Close()
} catch {}
}
}
if ($soon.Count -gt 0) {
$soon | Sort-Object 剩餘天數 | Format-Table -AutoSize
Write-Host "`n🔴 以上證書需要儘快續簽!"
} else {
Write-Host "✅ 30 天內無證書過期"
}
**風險等級**:🟢 無(只讀,不修改任何證書)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Access denied` | 需管理員許可權 | 管理員身份執行 |
| `WebAdministration not found` | IIS 未安裝 | 跳過模組 20.2 |
| `Connection refused` | 遠端域名 443 不可達 | 檢查域名和防火牆 |
| `AuthenticationException` | SSL 握手失敗 | 域名可能證書異常 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 證書顯示"已過期"但網站仍在訪問 | 瀏覽器有快取,實際使用者已經開始看到警告 |
| IIS 繫結的證書和倉庫裡的不一致 | IIS 用指紋引用,需手動在 IIS 管理器重新繫結 |
| Let's Encrypt 證書 90 天到期週期 | 需設定自動續簽(如 win-acme) |
Write-Host "════════ 入站防火牆規則(啟用中)════════"
$inbound = Get-NetFirewallRule -Direction Inbound -Enabled True -ErrorAction SilentlyContinue
Write-Host " 啟用的入站規則總數: $(($inbound | Measure-Object).Count)"
Write-Host ""
$inbound | ForEach-Object {
$rule = $_
$filter = $rule | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue
$addrFilter = $rule | Get-NetFirewallAddressFilter -ErrorAction SilentlyContinue
[PSCustomObject]@{
規則名 = $rule.DisplayName.Substring(0, [Math]::Min(40, $rule.DisplayName.Length))
協議 = if ($filter.Protocol) { $filter.Protocol } else { '任意' }
本地埠 = if ($filter.LocalPort) { $filter.LocalPort -join ',' } else { '任意' }
來源地址 = if ($addrFilter.RemoteAddress) { ($addrFilter.RemoteAddress -join ',').Substring(0, [Math]::Min(30, ($addrFilter.RemoteAddress -join ',').Length)) } else { '任意' }
動作 = $rule.Action
}
} | Select-Object -First 30 | Format-Table -AutoSize
### 21.2 高危規則檢測(Any → Any / 暴露全埠)
Write-Host "════════ 高危防火牆規則掃描 ════════"
$risks = @()
Get-NetFirewallRule -Direction Inbound -Action Allow -Enabled True -ErrorAction SilentlyContinue | ForEach-Object {
$rule = $_
$portFilter = $rule | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue
$addrFilter = $rule | Get-NetFirewallAddressFilter -ErrorAction SilentlyContinue
$anyPort = ($portFilter.LocalPort -eq 'Any' -or -not $portFilter.LocalPort)
$anyAddr = ($addrFilter.RemoteAddress -eq 'Any' -or $addrFilter.RemoteAddress -contains 'Any' -or -not $addrFilter.RemoteAddress)
if ($anyPort -and $anyAddr) {
$risks += [PSCustomObject]@{
風險等級 = '🔴 高危'
規則名 = $rule.DisplayName
說明 = '允許任意來源訪問任意埠'
建議 = '收緊來源地址或埠範圍'
}
} elseif ($anyAddr) {
# 檢查是否暴露高危埠(135/445/3389/5985)
$dangerousPorts = @('135','445','3389','5985','5986','23','21','1433','3306','6379')
$portStr = ($portFilter.LocalPort -join ',')
foreach ($p in $dangerousPorts) {
if ($portStr -match "\b$p\b" -or $anyPort) {
$risks += [PSCustomObject]@{
風險等級 = '⚠️ 中危'
規則名 = $rule.DisplayName
說明 = "埠 $p 對任意來源開放"
建議 = '限制來源 IP 白名單'
}
break
}
}
}
}
if ($risks.Count -gt 0) {
$risks | Format-Table -AutoSize -Wrap
Write-Host "`n建議: 使用「來源 IP 白名單」替代「任意來源」"
} else {
Write-Host "✅ 未發現明顯高危防火牆規則"
}
### 21.3 按埠檢視誰在放行
# 修改此處為你想查詢的埠
$targetPorts = @('3389', '445', '80', '443', '1433', '3306')
Write-Host "════════ 關鍵埠放行規則 ════════"
foreach ($port in $targetPorts) {
$rules = Get-NetFirewallRule -Direction Inbound -Action Allow -Enabled True -ErrorAction SilentlyContinue | Where-Object {
$pf = $_ | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue
($pf.LocalPort -eq 'Any' -or $pf.LocalPort -contains $port)
}
if ($rules) {
Write-Host "`n 埠 $port — 有 $(($rules | Measure-Object).Count) 條放行規則:"
$rules | Select-Object -First 5 @{N='規則名';E={$_.DisplayName}} |
ForEach-Object { Write-Host " - $($_.規則名)" }
} else {
Write-Host "`n 埠 $port — 無放行規則(預設拒絕)"
}
}
### 21.4 防火牆配置檔案狀態
Write-Host "════════ 防火牆配置檔案狀態 ════════"
Get-NetFirewallProfile | Select-Object @{N='配置檔案';E={$_.Name}},
@{N='是否啟用';E={if($_.Enabled){'✅ 啟用'}else{'❌ 已停用 ⚠️'}}},
@{N='入站預設';E={$_.DefaultInboundAction}},
@{N='出站預設';E={$_.DefaultOutboundAction}},
@{N='通知';E={$_.NotifyOnListen}} |
Format-Table -AutoSize
Write-Host "`n💡 防火牆應對所有配置檔案均啟用,入站預設「Block」"
Write-Host "💡 如果任一配置檔案顯示「已停用」,立即排查原因"
**風險等級**:🟢 無(只讀審計,不修改任何規則)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Get-NetFirewallRule` 報錯 | 需管理員許可權 | 管理員身份執行 |
| `WinRM` 相關報錯 | 防火牆服務異常 | 檢查 `mpssvc` 服務狀態 |
| `Access denied on Get-NetFirewallPortFilter` | 域策略限制 | 以域管理員身份執行 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 防火牆被關閉(Enabled=False) | 極高風險,任何連線均可進入 |
| 3389 對 Any 開放 | RDP 暴力破解首要目標 |
| 445 對 Any 開放 | WannaCry/勒索病毒入侵路徑 |
| 大量"Any → Any"規則 | 常見於軟體安裝時自動新增,需逐條清理 |
Write-Host "════════ 應執行卻已停止的服務 ════════"
$deadServices = Get-Service | Where-Object {
$_.StartType -in @('Automatic', 'AutomaticDelayedStart') -and
$_.Status -eq 'Stopped'
}
if ($deadServices) {
$deadServices | Select-Object @{N='服務名';E={$_.Name}},
@{N='顯示名';E={$_.DisplayName}},
@{N='啟動型別';E={$_.StartType}},
@{N='狀態';E={$_.Status}} |
Sort-Object 顯示名 |
Format-Table -AutoSize
Write-Host "`n⚠️ 以上服務設定了自動啟動但當前已停止,需要排查原因"
} else {
Write-Host "✅ 所有自動啟動的服務均在執行"
}
### 22.2 服務崩潰事件記錄(事件日誌 7034/7036)
Write-Host "════════ 服務崩潰記錄(最近 7 天)════════"
$events = Get-WinEvent -FilterHashtable @{
LogName = 'System'
Id = @(7034, 7035, 7036, 7031, 7040)
StartTime = (Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue
if ($events) {
$events | Select-Object @{N='時間';E={$_.TimeCreated}},
@{N='事件ID';E={$_.Id}},
@{N='說明';E={
switch ($_.Id) {
7034 { "❌ 服務意外停止: " + ($_.Message -replace "`r`n",' ').Substring(0,[Math]::Min(80,$_.Message.Length)) }
7031 { "❌ 服務停止後觸發恢復動作: " + ($_.Message -replace "`r`n",' ').Substring(0,[Math]::Min(60,$_.Message.Length)) }
7035 { "→ 服務控制: " + ($_.Message -replace "`r`n",' ').Substring(0,[Math]::Min(60,$_.Message.Length)) }
7036 { "● 服務狀態變更: " + ($_.Message -replace "`r`n",' ').Substring(0,[Math]::Min(60,$_.Message.Length)) }
7040 { "⚙️ 啟動型別變更: " + ($_.Message -replace "`r`n",' ').Substring(0,[Math]::Min(60,$_.Message.Length)) }
default { $_.Message.Substring(0,[Math]::Min(80,$_.Message.Length)) }
}
}} |
Where-Object { $_.事件ID -in @(7034, 7031) } |
Sort-Object 時間 -Descending |
Select-Object -First 20 |
Format-Table -AutoSize -Wrap
} else {
Write-Host "✅ 最近 7 天無服務崩潰記錄"
}
### 22.3 服務自動恢復策略檢視
Write-Host "════════ 關鍵服務恢復策略 ════════"
$keyServices = @(
'W3SVC', # IIS
'MSSQLSERVER', # SQL Server
'WSearch', # Windows Search
'Spooler', # 印表機
'EventLog', # 事件日誌
'WinRM', # 遠端管理
'Schedule', # 計劃任務
'LanmanServer' # 檔案共享
)
foreach ($svcName in $keyServices) {
$svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
if ($svc) {
# 使用 sc.exe 讀取恢復策略
$scOutput = sc.exe qfailure $svcName 2>&1
$resetPeriod = ($scOutput | Select-String 'RESET_PERIOD') -replace '.*: ', ''
$actions = ($scOutput | Select-String 'FAILURE_ACTIONS') -replace '.*: ', ''
Write-Host " $($svc.DisplayName) [$svcName]"
Write-Host " 狀態: $($svc.Status)"
Write-Host " 恢復動作: $(if($actions){$actions}else{'(未配置)'} )"
Write-Host ""
}
}
Write-Host "💡 未配置恢復動作的關鍵服務,崩潰後不會自動重啟,需手動干預"
### 22.4 服務依賴關係檢查(關鍵服務是否有依賴未啟動)
Write-Host "════════ 關鍵服務依賴鏈檢查 ════════"
$keyServices = @('W3SVC', 'MSSQLSERVER', 'WinRM', 'Schedule', 'Netlogon')
foreach ($svcName in $keyServices) {
$svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
if ($svc) {
$deps = $svc.ServicesDependedOn
$brokenDeps = $deps | Where-Object { $_.Status -ne 'Running' }
if ($brokenDeps) {
Write-Host "⚠️ $($svc.DisplayName) 依賴以下未執行的服務:"
$brokenDeps | ForEach-Object {
Write-Host " ❌ $($_.Name) ($($_.Status))"
}
} else {
Write-Host "✅ $($svc.DisplayName) — 所有依賴服務正常"
}
}
}
**風險等級**:🟢 無(只讀,不啟動/停止/修改任何服務)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Get-WinEvent` 無許可權 | 需管理員許可權 | 管理員身份執行 |
| `sc.exe qfailure` 返回空 | 部分服務無恢復策略 | 正常,說明未配置 |
| `Get-Service` 找不到服務 | 該服務未安裝 | 跳過該服務 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| IIS 停止但事件日誌無崩潰記錄 | 可能是被人為停止 |
| 服務崩潰但無恢復動作 | 需通過"服務屬性 → 恢復"配置自動重啟 |
| 依賴服務未執行導致主服務無法啟動 | 先啟動依賴服務 |
| 事件 7034 大量重複 | 服務反覆崩潰重啟,存在底層問題 |
---
# 掃描所有系統檔案完整性(不修復)
sfc /scanfile=C:\Windows\System32\kernel32.dll
# 掃描並嘗試修復(需管理員)
# sfc /scannow
# 掃描元件儲存是否損壞(不修復)
DISM /Online /Cleanup-Image /ScanHealth
# 檢查元件儲存健康狀態
DISM /Online /Cleanup-Image /CheckHealth
# 檢視可修復的元件列表
DISM /Online /Cleanup-Image /AnalyzeComponentStore
# 清理元件儲存中的舊版本元件
# DISM /Online /Cleanup-Image /StartComponentCleanup
# 修復元件儲存(需聯網)
# DISM /Online /Cleanup-Image /RestoreHealth
# 標準修復流程:先 DISM 修復元件儲存,再 SFC 修復系統檔案
# DISM /Online /Cleanup-Image /RestoreHealth
# sfc /scannow
# 完成後重啟系統
**風險等級**:🟢 掃描只讀 / 🟡 修復需確認
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Windows Resource Protection did not find any integrity violations` | 系統檔案正常 | 無需修復 |
| `There is a system repair pending which requires reboot` | 有掛起的修復 | 重啟後再掃描 |
| `The source files could not be downloaded` | 需聯網 | 使用安裝介質作為源 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| SFC 報告無法修復某些檔案 | 通常是元件儲存損壞,先執行 DISM |
| DISM 報錯 0x800f081f | 找不到原始檔,需指定安裝介質 |
| 修復後問題依舊 | 可能是檔案許可權問題,檢查 ACL |
Write-Host "════════ 儲存池健康狀態 ════════"
Get-StoragePool | Where-Object { $_.IsPrimordial -eq $false } |
Select-Object @{N='名稱';E={$_.FriendlyName}},
@{N='狀態';E={$_.HealthStatus}},
@{N='操作狀態';E={$_.OperationalStatus}},
@{N='物理磁碟數';E={$_.PhysicalDisks.Count}},
@{N='已用空間';E={[math]::Round($_.AllocatedSize/1GB,1)}},
@{N='總容量';E={[math]::Round($_.Size/1GB,1)}},
@{N='使用率';E={[math]::Round(($_.AllocatedSize/$_.Size)*100,1)}} |
Format-Table -AutoSize
Write-Host "════════ 虛擬磁碟狀態 ════════"
Get-VirtualDisk | Select-Object @{N='名稱';E={$_.FriendlyName}},
@{N='狀態';E={$_.HealthStatus}},
@{N='操作狀態';E={$_.OperationalStatus}},
@{N='RAID型別';E={$_.ResiliencySettingName}},
@{N='已用空間';E={[math]::Round($_.AllocatedSize/1GB,1)}},
@{N='總容量';E={[math]::Round($_.Size/1GB,1)}},
@{N='使用率';E={[math]::Round(($_.AllocatedSize/$_.Size)*100,1)}} |
Format-Table -AutoSize
Write-Host "════════ 儲存池中物理磁碟健康 ════════"
Get-StoragePool | Where-Object { $_.IsPrimordial -eq $false } |
Get-PhysicalDisk | Select-Object @{N='磁碟';E={$_.FriendlyName}},
@{N='狀態';E={$_.HealthStatus}},
@{N='操作狀態';E={$_.OperationalStatus}},
@{N='使用時長(小時)';E={[math]::Round($_.UsageHours,0)}},
@{N='溫度';E={if($_.Temperature){"$($_.Temperature)°C"}else{"-"}}},
@{N='讀取錯誤';E={(Get-StorageReliabilityCounter -PhysicalDisk $_ -ErrorAction SilentlyContinue).ReadErrorsTotal}} |
Format-Table -AutoSize
Write-Host "════════ 儲存層使用情況 ════════"
Get-StorageTier | Select-Object @{N='名稱';E={$_.FriendlyName}},
@{N='總容量';E={[math]::Round($_.Size/1GB,1)}},
@{N='已用';E={[math]::Round(($_.Size - $_.AllocatedSize)/1GB,1)}},
@{N='媒體型別';E={$_.MediaType}} |
Format-Table -AutoSize
**風險等級**:🟢 無(只讀)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `No MSFT_StoragePool objects` | 系統未配置儲存池 | 僅適用於 Storage Spaces 環境 |
| `Get-VirtualDisk` 報錯 | 需要管理員許可權 | 提升執行 |
| `HealthStatus` 顯示 `Warning` | 儲存池存在降級 | 立即檢查物理磁碟 |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 儲存池狀態顯示 `Degraded` | 有物理磁碟離線或故障 |
| 虛擬磁碟顯示 `Incomplete` | 部分成員磁碟離線,需儘快恢復 |
| 容量使用率 >90% | 需要擴容,否則寫入將失敗 |
| 物理磁碟 `UsageHours` 異常高 | 磁碟可能過熱或長期高負載 |
Write-Host "════════ Windows Server Backup 狀態 ════════"
# 檢查備份策略
try {
$policy = Get-WBPolicy -ErrorAction Stop
Write-Host " ✅ 備份策略已配置"
Write-Host " 備份目標: $($policy.BackupTargets | ForEach-Object { $_.Path })"
Write-Host " 包含卷: $($policy.VolumesToBackup | ForEach-Object { $_.Path })"
} catch {
Write-Host " ⚠️ 未配置備份策略"
}
# 檢視最近備份
Write-Host "`n════════ 最近備份歷史 ════════"
$backups = Get-WBSummary -ErrorAction SilentlyContinue
if ($backups) {
$backups | Select-Object @{N='最後備份時間';E={$_.LastBackupTime}},
@{N='備份大小';E={[math]::Round($_.TotalBackupSize/1GB,2)}},
@{N='狀態';E={if($_.LastBackupTime){"✅ 成功"}else{"❌ 無記錄"}}} |
Format-Table -AutoSize
} else {
Write-Host " ⚠️ 沒有備份歷史記錄"
}
Write-Host "════════ 備份目標磁碟檢查 ════════"
$policy = Get-WBPolicy -ErrorAction SilentlyContinue
if ($policy -and $policy.BackupTargets.Count -gt 0) {
foreach ($target in $policy.BackupTargets) {
$targetPath = $target.Path
$vol = Get-Volume -FilePath $targetPath -ErrorAction SilentlyContinue
if ($vol) {
$freeGB = [math]::Round($vol.SizeRemaining/1GB,1)
$totalGB = [math]::Round($vol.Size/1GB,1)
$pctFree = [math]::Round(($vol.SizeRemaining/$vol.Size)*100,1)
Write-Host " 目標: $targetPath"
Write-Host " 容量: ${totalGB} GB 剩餘: ${freeGB} GB (${pctFree}%)"
if ($pctFree -lt 10) {
Write-Host " ⚠️ 備份目標即將寫滿!"
}
}
}
} else {
Write-Host " ⚠️ 未配置備份目標"
}
Write-Host "════════ 可用還原點 ════════"
Get-WBBackupSet -ErrorAction SilentlyContinue | Sort-Object BackupTime -Descending |
Select-Object -First 10 @{N='備份時間';E={$_.BackupTime}},
@{N='備份大小(GB)';E={[math]::Round($_.TotalBackupSize/1GB,2)}},
@{N='卷數';E={$_.Volumes.Count}},
@{N='應用一致性';E={if($_.ApplicationConsistent){"✅"}else{"-"}}} |
Format-Table -AutoSize
Write-Host "════════ 當前備份作業狀態 ════════"
$job = Get-WBJob -ErrorAction SilentlyContinue
if ($job) {
Write-Host " 狀態: $($job.JobState)"
Write-Host " 當前操作: $($job.CurrentOperation)"
Write-Host " 進度: $($job.Progress)%"
} else {
Write-Host " ✅ 當前無備份作業執行"
}
**風險等級**:🟢 無(只讀)
| 報錯 | 含義 | 解決 |
|-----|------|-----|
| `Get-WBPolicy` 報錯 | Windows Server Backup 功能未安裝 | `Install-WindowsFeature Windows-Server-Backup` |
| `Get-WBSummary` 返回空 | 無備份歷史 | 先配置並執行一次備份 |
| `The operation is not supported` | 非 Server 版本 | 僅適用於 Windows Server |
**常見坑 & 解決**:
| 場景 | 說明 |
|-----|------|
| 備份目標剩餘 <10% | 備份將失敗,需清理或擴容 |
| 備份作業狀態顯示 `Running` 但長時間無進度 | 可能卡住,檢查日誌 |
| 應用一致性為 `false` | VSS 卷影複製失敗,檢查 VSS 服務 |
| 無可用還原點 | 備份從未成功,需排查 |
用途:管理 Windows Server 容器化部署環境,監控 Docker 容器與 K8s 叢集健康狀態。
常你說:"Docker 容器正常嗎" / "哪個容器吃資源" / "K8s 叢集狀態" / "容器日誌"
⚠️ 本模組僅在檢測到 Docker 或 kubectl 安裝時啟用,未安裝時給出友好提示。
$dockerPath = Get-Command docker -ErrorAction SilentlyContinue
$kubectlPath = Get-Command kubectl -ErrorAction SilentlyContinue
if (-not $dockerPath -and -not $kubectlPath) {
Write-Host "⚠️ 未檢測到 Docker 或 kubectl,跳過模組 26"
Write-Host " → 安裝 Docker: https://docs.docker.com/engine/install/windows-server/"
Write-Host " → 安裝 kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/"
}
用途:一鍵檢視容器、映象、網路、卷的使用情況。
常你說:"Docker 狀態總覽" / "容器列表" / "映象多大"
$dockerPath = Get-Command docker -ErrorAction SilentlyContinue
if (-not $dockerPath) {
Write-Host "⚠️ Docker 未安裝"
return
}
Write-Host "════════ Docker 狀態總覽 ════════"
# 容器狀態
$containers = docker ps -a --format "table {{.Names}} {{.Status}} {{.Image}} {{.Ports}}" 2>$null
if ($containers) {
Write-Host "`n📦 容器列表:"
$containers | ForEach-Object { Write-Host " $_" }
}
# 映象列表
$images = docker images --format "table {{.Repository}}:{{.Tag}} {{.Size}} {{.CreatedSince}}" 2>$null
if ($images) {
Write-Host "`n🖼️ 映象列表:"
$images | ForEach-Object { Write-Host " $_" }
}
# 網路列表
$networks = docker network ls --format "table {{.Name}} {{.Driver}} {{.Scope}}" 2>$null
if ($networks) {
Write-Host "`n🌐 網路列表:"
$networks | ForEach-Object { Write-Host " $_" }
}
# 卷列表
$volumes = docker volume ls --format "table {{.Name}} {{.Driver}}" 2>$null
if ($volumes) {
Write-Host "`n💾 卷列表:"
$volumes | ForEach-Object { Write-Host " $_" }
}
# 磁碟佔用
$diskUsage = docker system df 2>$null
if ($diskUsage) {
Write-Host "`n📊 Docker 磁碟佔用:"
$diskUsage | ForEach-Object { Write-Host " $_" }
}
風險等級:🟢 無(只讀)
用途:即時檢視每個容器的 CPU / 記憶體 / 網路 / 磁碟 I/O 佔用。
常你說:"哪個容器吃資源" / "容器 CPU 佔用" / "容器記憶體"
$dockerPath = Get-Command docker -ErrorAction SilentlyContinue
if (-not $dockerPath) {
Write-Host "⚠️ Docker 未安裝"
return
}
Write-Host "════════ 容器資源監控 ════════"
Write-Host "(單次採集,不持續佔用資源)`n"
# 獲取 CPU 核心數,決定採集併發數
$cpuCores = (Get-CimInstance Win32_Processor).NumberOfLogicalProcessors
$sampleCount = if ($cpuCores -le 4) { 1 } else { 3 }
# 容器資源統計(不流式輸出,避免佔用資源)
$stats = docker stats --no-stream --format "table {{.Name}} {{.CPUPerc}} {{.MemUsage}} {{.MemPerc}} {{.NetIO}} {{.BlockIO}}" 2>$null
if ($stats) {
$stats | ForEach-Object { Write-Host " $_" }
} else {
Write-Host " ⚠️ 無執行中的容器"
}
Write-Host "`n💡 單次採集完成,共 $sampleCount 次取樣"
風險等級:🟢 無(只讀,單次採集不佔用資源)
用途:檢查 Docker 服務狀態、磁碟佔用、守護程序日誌異常。
常你說:"Docker 服務正常嗎" / "Docker 磁碟佔用" / "Docker 日誌異常"
$dockerPath = Get-Command docker -ErrorAction SilentlyContinue
if (-not $dockerPath) {
Write-Host "⚠️ Docker 未安裝"
return
}
Write-Host "════════ Docker 健康檢查 ════════"
# 1. Docker 服務狀態
$dockerSvc = Get-Service com.docker.service -ErrorAction SilentlyContinue
if ($dockerSvc) {
$svcStatus = if ($dockerSvc.Status -eq 'Running') { '✅ 執行中' } else { '❌ 已停止' }
Write-Host " Docker 服務: $svcStatus"
} else {
Write-Host " Docker 服務: ⚠️ 未找到服務"
}
# 2. Docker 版本資訊
$dockerVersion = docker version --format "{{.Server.Version}}" 2>$null
if ($dockerVersion) {
Write-Host " Docker 版本: $dockerVersion"
}
# 3. Docker 磁碟佔用
$diskUsage = docker system df 2>$null
if ($diskUsage) {
Write-Host "`n📊 磁碟佔用:"
$diskUsage | ForEach-Object { Write-Host " $_" }
}
# 4. 容器健康狀態(如果容器配置了 HEALTHCHECK)
$containers = docker ps --format "{{.Names}}" 2>$null
if ($containers) {
Write-Host "`n🏥 容器健康狀態:"
foreach ($c in $containers) {
$health = docker inspect --format "{{.State.Health.Status}}" $c 2>$null
if ($health) {
$icon = if ($health -eq 'healthy') { '✅' } elseif ($health -eq 'unhealthy') { '❌' } else { '⏳' }
Write-Host " $icon $c : $health"
} else {
Write-Host " ⏳ $c : 未配置健康檢查"
}
}
}
# 5. 最近 24 小時 Docker 相關係統錯誤日誌
$since = (Get-Date).AddHours(-24)
$dockerEvents = Get-WinEvent -FilterHashtable @{
LogName='System'; ProviderName='Docker'; Level=2; StartTime=$since
} -ErrorAction SilentlyContinue
if ($dockerEvents) {
Write-Host "`n⚠️ 最近 24h Docker 錯誤事件: $($dockerEvents.Count) 條"
$dockerEvents | Select-Object -First 5 TimeCreated, Message |
Format-Table -AutoSize -Wrap
} else {
Write-Host "`n✅ 最近 24h 無 Docker 錯誤日誌"
}
風險等級:🟢 無(只讀)
用途:檢視 Kubernetes 節點、Pod 健康、資源配額使用率。
常你說:"K8s 叢集狀態" / "Pod 健康" / "節點狀態"
$kubectlPath = Get-Command kubectl -ErrorAction SilentlyContinue
if (-not $kubectlPath) {
Write-Host "⚠️ kubectl 未安裝"
return
}
Write-Host "════════ K8s 叢集狀態 ════════"
# 1. 節點狀態
Write-Host "`n🖥️ 節點狀態:"
$nodes = kubectl get nodes -o wide 2>$null
if ($nodes) {
$nodes | ForEach-Object { Write-Host " $_" }
} else {
Write-Host " ⚠️ 無法獲取節點資訊,檢查叢集連線"
}
# 2. Pod 狀態(所有名稱空間)
Write-Host "`n📦 Pod 狀態:"
$pods = kubectl get pods --all-namespaces -o wide 2>$null
if ($pods) {
$pods | ForEach-Object { Write-Host " $_" }
}
# 3. 資源配額
Write-Host "`n📊 資源配額:"
$resourceQuota = kubectl get resourcequota --all-namespaces 2>$null
if ($resourceQuota) {
$resourceQuota | ForEach-Object { Write-Host " $_" }
} else {
Write-Host " 未配置資源配額"
}
# 4. 異常 Pod 檢測
$allPods = kubectl get pods --all-namespaces --no-headers 2>$null
if ($allPods) {
$abnormalPods = @()
foreach ($podLine in $allPods) {
$parts = $podLine -split '\s+'
if ($parts.Count -ge 4) {
$ns = $parts[0]
$name = $parts[1]
$ready = $parts[2]
$status = $parts[3]
if ($status -ne 'Running' -and $status -ne 'Succeeded') {
$abnormalPods += [PSCustomObject]@{
名稱空間 = $ns
Pod名 = $name
狀態 = $status
就緒 = $ready
}
}
}
}
if ($abnormalPods.Count -gt 0) {
Write-Host "`n⚠️ 異常 Pod:"
$abnormalPods | Format-Table -AutoSize
} else {
Write-Host "`n✅ 所有 Pod 狀態正常"
}
}
# 5. 節點資源使用(需 metrics-server)
Write-Host "`n📈 節點資源使用:"
$topNodes = kubectl top nodes 2>$null
if ($topNodes) {
$topNodes | ForEach-Object { Write-Host " $_" }
} else {
Write-Host " ⚠️ metrics-server 未安裝,無法獲取資源使用率"
}
Write-Host "`n💡 K8s 叢集狀態採集完成"
風險等級:🟢 無(只讀)
用途:檢視指定容器的最近日誌,檢測異常模式。
常你說:"看容器日誌" / "容器報錯了" / "容器最近日誌"
⚠️ 採集限制:預設採集最近 100 行,記憶體不足 2 GB 時自動縮減到 50 行,避免佔用過多記憶體。
$dockerPath = Get-Command docker -ErrorAction SilentlyContinue
if (-not $dockerPath) {
Write-Host "⚠️ Docker 未安裝"
return
}
# 獲取可用記憶體,決定採集行數
$totalRAM = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory/1GB, 1)
$tailCount = if ($totalRAM -lt 2) { 50 } else { 100 }
Write-Host "════════ 容器日誌採集 ════════"
Write-Host "可用記憶體: ${totalRAM} GB,採集行數: $tailCount`n"
# 列出所有容器供選擇
$containerList = docker ps -a --format "{{.Names}} | {{.Status}}" 2>$null
if ($containerList) {
Write-Host "可用容器:"
$containerList | ForEach-Object { Write-Host " $_" }
Write-Host ""
}
# 替換為你要檢視的容器名
$targetContainer = "your_container_name" # ← 改成你要看的容器名
if ($targetContainer -eq "your_container_name") {
Write-Host "⚠️ 請修改 `$targetContainer 變數為要檢視的容器名"
return
}
Write-Host "📋 容器: $targetContainer (最近 $tailCount 行)"
Write-Host "──────────────────────────────────────"
$logs = docker logs --tail $tailCount $targetContainer 2>&1
if ($logs) {
$logs | ForEach-Object { Write-Host " $_" }
# 異常模式檢測
$errorKeywords = @('error', 'exception', 'fatal', 'panic', 'failed', 'refused')
$errorLines = @()
$lineNum = 0
foreach ($line in $logs) {
$lineNum++
foreach ($kw in $errorKeywords) {
if ($line -match $kw) {
$errorLines += " 行 $lineNum : $line"
break
}
}
}
if ($errorLines.Count -gt 0) {
Write-Host "`n⚠️ 檢測到 $($errorLines.Count) 行異常關鍵詞:"
$errorLines | Select-Object -First 20 | ForEach-Object { Write-Host $_ }
if ($errorLines.Count -gt 20) {
Write-Host " ... 還有 $($errorLines.Count - 20) 行未顯示"
}
} else {
Write-Host "`n✅ 未檢測到異常關鍵詞"
}
}
風險等級:🟢 無(只讀)
| 報錯 | 含義 | 解決 |
|---|---|---|
docker: command not found |
Docker 未安裝 | 安裝 Docker Engine |
kubectl: command not found |
kubectl 未安裝 | 安裝 kubectl |
connection refused |
Docker/K8s 服務未啟動 | 啟動對應服務 |
permission denied |
許可權不足 | 管理員身份執行 |
常見坑 & 解決:
| 場景 | 說明 |
|---|---|
| 日誌採集時記憶體不足 | 自動縮減到 50 行,進一步減少可修改 $tailCount |
| 容器未配置 HEALTHCHECK | 健康狀態顯示"未配置",正常 |
| metrics-server 未安裝 | K8s 資源使用率無法獲取,屬常見情況 |
| Docker 在 Windows Server 上需要特定版本 | 確認 Server 版本與 Docker 相容 |
用途:從"診斷"到"修復",提供一鍵式常見問題修復,嚴格遵循「預覽→確認→執行→驗證」四步流程。
常你說:"幫我修 DNS" / "網路不正常" / "Windows Update 卡住" / "服務起不來" / "磁碟滿了" / "時間不準"
⚠️ 本模組所有修復操作均需管理員許可權,執行前自動建立還原點,使用者明確確認後才執行。
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Step 1 │ → │ Step 2 │ → │ Step 3 │ → │ Step 4 │
│ 問題定位 │ │ 修復預覽 │ │ 使用者確認 │ │ 執行+驗證 │
│ 呼叫現有模組 │ │ 展示命令+影響 │ │ 建立還原點 │ │ 重新診斷確認 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
修復命令:ipconfig /flushdns + netsh winsock reset
常你說:"DNS 修一下" / "網頁打不開 DNS 問題"
# ====== Step 1: 問題定位(呼叫模組19) ======
Write-Host "════════ DNS 修復嚮導 ════════"
Write-Host "`n[Step 1] 問題定位..."
$dnsTest = Resolve-DnsName -Name www.baidu.com -ErrorAction SilentlyContinue
if ($dnsTest) {
Write-Host " ✅ DNS 解析正常,無需修復"
return
}
Write-Host " ❌ DNS 解析失敗,需要修復"
# ====== Step 2: 修復預覽 ======
Write-Host "`n[Step 2] 修復預覽"
Write-Host " 將要執行以下命令:"
Write-Host " 1. ipconfig /flushdns — 清空 DNS 快取"
Write-Host " 2. netsh winsock reset — 重置 Winsock 目錄"
Write-Host " 3. ipconfig /registerdns — 重新註冊 DNS 名稱"
Write-Host "`n 影響範圍:"
Write-Host " · 短暫斷開網路連線(約 5-10 秒)"
Write-Host " · 需要重啟後生效"
Write-Host " · 不會丟失任何資料"
# ====== Step 3: 使用者確認 ======
Write-Host "`n[Step 3] 使用者確認"
$confirm = Read-Host " 輸入 '確認修復' 開始執行"
if ($confirm -ne '確認修復') {
Write-Host " ❌ 已取消"
return
}
# 建立還原點
Write-Host " 正在建立系統還原點..."
try {
Checkpoint-Computer -Description "winskill-DNS修復前" -RestorePointType MODIFY_SETTINGS -ErrorAction SilentlyContinue
Write-Host " ✅ 還原點已建立"
} catch {
Write-Host " ⚠️ 無法建立還原點(系統保護可能未啟用)"
}
# ====== Step 4: 執行修復 ======
Write-Host "`n[Step 4] 執行修復..."
Write-Host " 1/3 清空 DNS 快取..."
ipconfig /flushdns
Write-Host " 2/3 重置 Winsock..."
netsh winsock reset
Write-Host " 3/3 註冊 DNS..."
ipconfig /registerdns
# 驗證
Write-Host "`n[驗證] 重新測試 DNS..."
Start-Sleep -Seconds 3
$verifyDns = Resolve-DnsName -Name www.baidu.com -ErrorAction SilentlyContinue
if ($verifyDns) {
Write-Host " ✅ DNS 修復成功!"
} else {
Write-Host " ⚠️ DNS 仍無法解析,建議檢查 DNS 伺服器配置"
Write-Host " → 模組 19 檢視 DNS 伺服器配置"
}
風險等級:🟡 中(需確認,會短暫斷網)
修復命令:netsh int ip reset + 網絡卡停用/啟用
常你說:"網路重置" / "網連不上"
Write-Host "════════ 網路修復嚮導 ════════"
# Step 1: 定位
Write-Host "`n[Step 1] 問題定位..."
$pingTest = Test-NetConnection -ComputerName www.baidu.com -Port 443 -ErrorAction SilentlyContinue
if ($pingTest.TcpTestSucceeded) {
Write-Host " ✅ 網路連線正常"
return
}
Write-Host " ❌ 網路連線失敗"
# Step 2: 預覽
Write-Host "`n[Step 2] 修復預覽"
Write-Host " 將要執行:"
Write-Host " 1. netsh int ip reset — 重置 TCP/IP 協議棧"
Write-Host " 2. 停用再啟用網絡卡 — 重新初始化網路介面卡"
Write-Host "`n 影響:短暫斷網(約 10-30 秒),需管理員許可權"
# Step 3: 確認
$confirm = Read-Host "`n 輸入 '確認修復' 開始執行"
if ($confirm -ne '確認修復') { return }
# Step 4: 執行
Write-Host "`n[Step 4] 執行修復..."
netsh int ip reset
Write-Host " ✅ TCP/IP 已重置"
# 獲取主網絡卡
$adapter = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | Select-Object -First 1
if ($adapter) {
Write-Host " 重啟網絡卡: $($adapter.Name)..."
Disable-NetAdapter -Name $adapter.Name -Confirm:$false
Start-Sleep -Seconds 3
Enable-NetAdapter -Name $adapter.Name -Confirm:$false
Write-Host " ✅ 網絡卡已重啟"
}
# 驗證
Start-Sleep -Seconds 5
$verifyNet = Test-NetConnection -ComputerName www.baidu.com -Port 443 -ErrorAction SilentlyContinue
if ($verifyNet.TcpTestSucceeded) {
Write-Host "`n ✅ 網路修復成功!"
} else {
Write-Host "`n ⚠️ 仍未連通,檢查物理連線和路由器"
}
風險等級:🟡 中(需確認,會短暫斷網)
修復命令:停服務 → 清理 SoftwareDistribution → 重啟服務
常你說:"Windows Update 修一下" / "更新卡住了"
Write-Host "════════ Windows Update 修復嚮導 ════════"
# Step 1: 定位
Write-Host "`n[Step 1] 問題定位..."
$wuService = Get-Service wuauserv -ErrorAction SilentlyContinue
if ($wuService.Status -eq 'Running') {
Write-Host " ✅ Windows Update 服務執行中"
} else {
Write-Host " ❌ Windows Update 服務已停止"
}
# Step 2: 預覽
Write-Host "`n[Step 2] 修復預覽"
Write-Host " 將要執行:"
Write-Host " 1. Stop-Service wuauserv — 停止更新服務"
Write-Host " 2. 清理 SoftwareDistribution — 刪除損壞的下載快取"
Write-Host " 3. Start-Service wuauserv — 重啟更新服務"
Write-Host "`n 影響:正在進行的更新將中斷,已安裝補丁不受影響"
# Step 3: 確認
$confirm = Read-Host "`n 輸入 '確認修復' 開始執行"
if ($confirm -ne '確認修復') { return }
# Step 4: 執行
Write-Host "`n[Step 4] 執行修復..."
Write-Host " 1/3 停止更新服務..."
Stop-Service wuauserv -Force
Write-Host " 2/3 清理快取..."
$sdPath = "C:\Windows\SoftwareDistribution\Download"
if (Test-Path $sdPath) {
Remove-Item -Path "$sdPath\*" -Recurse -Force -ErrorAction SilentlyContinue
}
Write-Host " 3/3 重啟服務..."
Start-Service wuauserv
# 驗證
Start-Sleep -Seconds 3
$verifyWu = Get-Service wuauserv
if ($verifyWu.Status -eq 'Running') {
Write-Host "`n ✅ Windows Update 服務已恢復正常"
} else {
Write-Host "`n ⚠️ 服務未啟動,檢視事件日誌排查"
}
風險等級:🟡 中(需確認,正在進行的更新會中斷)
修復命令:檢查依賴 → 檢查埠 → 嘗試啟動
常你說:"SQL Server 起不來" / "IIS 啟動失敗"
# 替換為你要修復的服務名
$targetService = "W3SVC" # ← 改成你要修復的服務名
Write-Host "════════ 服務修復嚮導 ════════"
Write-Host "目標服務: $targetService"
# Step 1: 定位
Write-Host "`n[Step 1] 問題定位..."
$svc = Get-Service -Name $targetService -ErrorAction SilentlyContinue
if (-not $svc) {
Write-Host " ❌ 服務不存在: $targetService"
return
}
Write-Host " 狀態: $($svc.Status)"
Write-Host " 啟動型別: $($svc.StartType)"
# 檢查依賴
$deps = $svc.ServicesDependedOn
$brokenDeps = $deps | Where-Object { $_.Status -ne 'Running' }
if ($brokenDeps) {
Write-Host " ⚠️ 依賴服務未執行:"
$brokenDeps | ForEach-Object { Write-Host " ❌ $($_.Name)" }
}
# Step 2: 預覽
Write-Host "`n[Step 2] 修復預覽"
Write-Host " 將要執行:"
Write-Host " 1. 啟動依賴服務(如有)"
Write-Host " 2. 檢查端口占用"
Write-Host " 3. 嘗試啟動目標服務"
# Step 3: 確認
$confirm = Read-Host "`n 輸入 '確認修復' 開始執行"
if ($confirm -ne '確認修復') { return }
# Step 4: 執行
Write-Host "`n[Step 4] 執行修復..."
# 啟動依賴
if ($brokenDeps) {
foreach ($dep in $brokenDeps) {
Write-Host " 啟動依賴: $($dep.Name)..."
try {
Start-Service -Name $dep.Name -ErrorAction Stop
Write-Host " ✅ 已啟動"
} catch {
Write-Host " ❌ 啟動失敗: $($_.Exception.Message)"
}
}
}
# 檢查埠(如果是 IIS/SQL/MySQL 等已知服務)
$portMap = @{
'W3SVC' = 80
'MSSQLSERVER' = 1433
'MySQL80' = 3306
}
if ($portMap.ContainsKey($targetService)) {
$port = $portMap[$targetService]
$portUsed = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
if ($portUsed) {
$proc = Get-Process -Id $portUsed.OwningProcess -ErrorAction SilentlyContinue
Write-Host " ⚠️ 埠 $port 被佔用: $($proc.Name)"
}
}
# 嘗試啟動
Write-Host " 嘗試啟動 $targetService..."
try {
Start-Service -Name $targetService -ErrorAction Stop
Write-Host " ✅ 服務啟動成功!"
} catch {
Write-Host " ❌ 啟動失敗: $($_.Exception.Message)"
Write-Host " → 檢視事件日誌: 模組 14"
}
# 驗證
$verifySvc = Get-Service -Name $targetService
Write-Host "`n[驗證] 服務狀態: $($verifySvc.Status)"
風險等級:🟡 中(需確認,會啟動服務)
修復命令:大檔案識別 + 舊檔案建議 + temp 清理
常你說:"磁碟滿了" / "C 盤清理"
Write-Host "════════ 磁碟清理修復嚮導 ════════"
# Step 1: 定位
Write-Host "`n[Step 1] 問題定位..."
$vol = Get-Volume -DriveLetter C
$freeGB = [math]::Round($vol.SizeRemaining/1GB, 1)
$totalGB = [math]::Round($vol.Size/1GB, 1)
$usedPct = [math]::Round((1 - $vol.SizeRemaining/$vol.Size) * 100, 1)
Write-Host " C 盤: ${freeGB} GB 可用 / ${totalGB} GB 總計 (已用 ${usedPct}%)"
if ($freeGB -gt 10) {
Write-Host " ✅ 磁碟空間充足"
return
}
Write-Host " ⚠️ 磁碟空間不足"
# Step 2: 預覽
Write-Host "`n[Step 2] 修復預覽"
Write-Host " 將要執行:"
Write-Host " 1. 掃描大檔案(>100MB)"
Write-Host " 2. 掃描舊檔案(>30 天)"
Write-Host " 3. 清理臨時檔案(>7 天)"
Write-Host " 4. 清空回收站"
Write-Host "`n 影響:清理臨時檔案不可恢復,大檔案僅建議不自動刪除"
# Step 3: 確認
$confirm = Read-Host "`n 輸入 '確認修復' 開始執行"
if ($confirm -ne '確認修復') { return }
# Step 4: 執行
Write-Host "`n[Step 4] 執行清理..."
# 清理臨時檔案
$tempPath = $env:TEMP
$cutoff = (Get-Date).AddDays(-7)
$tempFiles = Get-ChildItem -Path $tempPath -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $cutoff }
$tempKB = ($tempFiles | Measure-Object -Property Length -Sum).Sum
$tempCount = $tempFiles.Count
Write-Host " 清理臨時檔案: $tempCount 個檔案,釋放 $([math]::Round($tempKB/1MB,1)) MB"
foreach ($f in $tempFiles) {
try { Remove-Item $f.FullName -Force -ErrorAction SilentlyContinue } catch {}
}
# 清空回收站
$shell = New-Object -ComObject Shell.Application
$rb = $shell.NameSpace(0x0a)
$rbItems = @($rb.Items())
if ($rbItems.Count -gt 0) {
Write-Host " 清空回收站: $($rbItems.Count) 個專案"
$rbItems | ForEach-Object { Remove-Item $_.Path -Recurse -Force -ErrorAction SilentlyContinue }
}
# 驗證
$volAfter = Get-Volume -DriveLetter C
$freeAfter = [math]::Round($volAfter.SizeRemaining/1GB, 1)
Write-Host "`n[驗證] 清理後 C 盤可用: ${freeAfter} GB(釋放 $([math]::Round($freeAfter - $freeGB, 1)) GB)"
風險等級:🟡 中(需確認,清理臨時檔案不可恢復)
修復命令:w32tm /resync
常你說:"時間不準" / "時間同步"
Write-Host "════════ 時間同步修復嚮導 ════════"
# Step 1: 定位
Write-Host "`n[Step 1] 問題定位..."
$localTime = Get-Date
$ntpTime = $null
# 嘗試從 NTP 伺服器獲取時間
try {
$tcp = New-Object System.Net.Sockets.TcpClient("time.windows.com", 123)
$stream = $tcp.GetStream()
$data = New-Object byte[] 48
$stream.Read($data, 0, 48) | Out-Null
$stream.Close()
$tcp.Close()
# 簡化:直接用 w32tm 查詢
$ntpTime = (w32tm /stripchart /computer:time.windows.com /samples:1 /dataonly 2>$null | Select-Object -Last 1)
} catch {}
$w32tmStatus = w32tm /query /status 2>$null
if ($w32tmStatus) {
Write-Host " Windows Time 服務狀態:"
$w32tmStatus | Select-Object -First 5 | ForEach-Object { Write-Host " $_" }
}
# Step 2: 預覽
Write-Host "`n[Step 2] 修復預覽"
Write-Host " 將要執行:"
Write-Host " 1. w32tm /resync — 強制同步時間"
Write-Host " 2. w32tm /query /status — 驗證同步結果"
Write-Host "`n 影響:無風險,時間同步不會丟失資料"
# Step 3: 確認
$confirm = Read-Host "`n 輸入 '確認修復' 開始執行"
if ($confirm -ne '確認修復') { return }
# Step 4: 執行
Write-Host "`n[Step 4] 執行時間同步..."
w32tm /resync 2>$null
Start-Sleep -Seconds 2
# 驗證
Write-Host "`n[驗證] 同步結果:"
$verifyTime = w32tm /query /status 2>$null
if ($verifyTime) {
$verifyTime | Select-Object -First 5 | ForEach-Object { Write-Host " $_" }
Write-Host "`n ✅ 時間同步完成"
} else {
Write-Host " ⚠️ 同步狀態未知,稍後重試"
}
風險等級:🟢 低(無風險,僅同步時間)
風險等級彙總:
| 子模組 | 風險 | 說明 |
|---|---|---|
| 27.1 DNS 修復 | 🟡 中 | 短暫斷網 5-10 秒 |
| 27.2 網路修復 | 🟡 中 | 短暫斷網 10-30 秒 |
| 27.3 WinUpdate 修復 | 🟡 中 | 正在進行的更新會中斷 |
| 27.4 服務修復 | 🟡 中 | 會啟動服務 |
| 27.5 磁碟清理 | 🟡 中 | 臨時檔案不可恢復 |
| 27.6 時間同步 | 🟢 低 | 無風險 |
| 報錯 | 含義 | 解決 |
|---|---|---|
Access denied |
需要管理員許可權 | 以管理員身份執行 |
Service cannot be started |
服務啟動失敗 | 檢查依賴和埠 |
Network reset failed |
網絡卡重置失敗 | 手動停用/啟用網絡卡 |
w32tm error |
時間服務未執行 | 啟動 W32Time 服務 |
常見坑 & 解決:
| 場景 | 說明 |
|---|---|
| 修復後問題依舊 | 檢視對應診斷模組的詳細日誌 |
| 無法建立還原點 | 系統保護未啟用,不影響修復操作 |
| 服務啟動後立即停止 | 檢查事件日誌,可能是程式崩潰 |
| 磁碟清理後空間未增加 | 可能是系統還原點佔用,清理舊還原點 |
| 需求 | 說明 | 檢測方法 |
|---|---|---|
| PowerShell 5.1+ | Windows 自帶 | $PSVersionTable.PSVersion |
| 管理員許可權 | IIS/清理/安全審計需要 | 自動檢測(見下方) |
| WebAdministration | IIS 管理可選 | Install-WindowsFeature Web-Mgmt-Tools |
| 執行策略 | 可能需調整 | Set-ExecutionPolicy RemoteSigned |
| 儲存診斷模組 | 磁碟健康檢測需要 | Get-PhysicalDisk 可用 |
| Winmgmt (WMI) | 系統事件/會話監控需要 | Get-CimInstance 可用 |
管理員許可權自動檢測:
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Host "⚠️ 非管理員執行,部分功能受限"
Write-Host " → 右鍵 PowerShell → 以管理員身份執行"
}
設計原則:絕不拖累使用者電腦。 所有掃描/採集任務均根據使用者電腦硬體配置自動調整執行緒數和資源佔用。
| 檢測項 | 用途 | 自適應策略 |
|---|---|---|
| CPU 核心數 | 決定並行任務數 | ≤4 核:單執行緒;8 核:2 執行緒;16+ 核:4 執行緒 |
| 實體記憶體 | 決定是否啟用記憶體快取 | ≤4 GB:停用快取;8 GB:小快取;16+ GB:正常快取 |
| 磁碟型別 | 決定是否啟用 I/O 優先順序 | HDD:低優先順序 I/O;SSD:正常優先順序 |
| 系統負載 | 決定是否延遲啟動 | CPU >80% 時延遲 5 秒後執行 |
| 模組 | 自適應行為 |
|---|---|
| 模組 1(磁碟掃描) | 根據 CPU 核心數自動分配掃描執行緒數,HDD 自動降低 I/O 優先順序 |
| 模組 2(重複檔案) | 大檔案自動分批處理,避免記憶體溢位 |
| 模組 3(臨時檔案) | 掃描時自動跳過正在使用的檔案 |
| 模組 9(效能監控) | 採集間隔根據 CPU 負載動態調整(1-5 秒) |
| 模組 12(磁碟健康) | 檢測時自動限制併發 I/O,避免影響業務 |
| 模組 13(網路監控) | 連線數採集分批進行,避免一次性佔用過多資源 |
# 自動檢測硬體並設定執行緒數
$cpuCores = (Get-CimInstance Win32_Processor).NumberOfLogicalProcessors
$totalRAM = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory/1GB, 1)
$diskType = (Get-PhysicalDisk | Select-Object -First 1).MediaType
$threadCount = switch ($cpuCores) {
{ $_ -le 4 } { 1 }
{ $_ -le 8 } { 2 }
default { 4 }
}
$ioPriority = if ($diskType -eq 'HDD') { 'Low' } else { 'Normal' }
Write-Host "硬體檢測: $cpuCores 核 CPU, ${totalRAM} GB RAM, 磁碟: $diskType"
Write-Host "自動設定: $threadCount 執行緒, I/O 優先順序: $ioPriority"
直接說 "幫我掃一下 C 盤" 或 "檢查服務狀態"。
貼上報錯內容,AI 會翻譯並給出解法。每個模組也有"報錯與解決"表格。
三層保護:系統目錄不可操作、刪除前先確認、回收站 API 可恢復。
全程離線、零依賴、零費用。
不會。只清理未完成的下載快取,已安裝補丁不受影響。重新檢查更新時會重新下載。
即時計數器只顯示當前值。如需長期趨勢,可讓 AI 建立資料收集器(需確認),資料會儲存到 C:\PerfLogs。
只讀審計和匯出操作不會產生額外日誌。但效能計數器採集會佔用少量系統資源。
不會。模組 11 所有操作均為只讀分析,絕不修改任何登錄檔鍵值。
不會。SMART 資訊和錯誤計數均為只讀讀取,不會執行磁碟擦除或修復操作。
不會。所有檢測均在本地進行,不會將任何連線資訊傳送到外部。
不會。模組 14 僅讀取日誌內容,不會清除、修改或停止日誌服務。
不會。模組 15 僅讀取登錄檔解除安裝鍵值,不會修改任何安裝資訊或解除安裝程式。
不會。模組 16 僅讀取會話資訊,不會結束、鎖定或斷開任何使用者會話。
skillhub upgrade winskill
不會。模組 17 所有操作均為只讀審計,絕不會建立/刪除/修改任何計劃任務。
不會。模組 18 僅讀取共享配置和 SMB 會話資訊,不會關閉共享或斷開任何使用者連線。
不會。Clear-DnsClientCache 清空後會自動從 DNS 伺服器重新獲取解析記錄。只在使用者明確說「確認清空 DNS 快取」後才執行。
不會。模組 20 僅讀取證書倉庫和 IIS 繫結資訊,不會申請、續簽或刪除任何證書。
不會。模組 21 僅讀取規則配置,不會新增、刪除或修改任何防火牆規則。
不會。模組 22 僅讀取服務狀態和事件日誌,不會啟動、停止或修改任何服務配置。
SFC 修復單個系統檔案,DISM 修復元件儲存(SFC 的源)。建議先 DISM 修復元件儲存,再 SFC 修復系統檔案。
Mirror(映象)提供冗餘,Parity(奇偶校驗)提供更高儲存效率。生產環境推薦 Mirror。
檢查 Windows 事件檢視器 → Applications and Services Logs → Microsoft → Windows → Backup → Operational。
模組 26 使用 Windows 系統自帶的 docker 和 kubectl 命令列工具。Windows Server 2016+ 支援 Docker,但需要手動安裝。未安裝 Docker/kubectl 時模組 26 給出安裝指引,不會報錯。
不會。模組 28 根據 CPU 核心數動態調整採集頻率(≤4 核每 15 分鐘一次,>4 核每 5 分鐘一次),單次採集耗時 <1 秒,記憶體佔用 <10MB。
不會。模組 29 所有檢查項均為只讀檢測,僅輸出合規/不合規判定和修復建議,不會修改任何系統配置。
是的。模組 30 基於 PowerShell Remoting(WinRM),目標伺服器需要開啟 WinRM 服務(Enable-PSRemoting)。模組 30.1 包含一鍵開啟 WinRM 的命令。
支援。模組 30 支援域環境和非域環境,非域環境需配置 TrustedHosts 並使用 IP 地址連線。
⚠️ 本模組所有操作均為只讀分析,不會修改任何系統配置。
# 硬體自適應:確定採集間隔
$cpuCores = (Get-CimInstance Win32_Processor).NumberOfLogicalProcessors
$intervalMinutes = if ($cpuCores -le 4) { 15 } else { 5 }
$tagFile = "$env:USERPROFILE\.workbuddy\output\winskill\baseline_tag.txt"
$csvFile = "$env:USERPROFILE\.workbuddy\output\winskill\perf_baseline.csv"
# 建立輸出目錄
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.workbuddy\output\winskill" | Out-Null
# 檢查是否已註冊定時任務
$taskName = "Winskill_PerfBaseline"
$existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if (-not $existingTask) {
# 註冊定時任務(需管理員許可權)
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument @"
-Command "& {`$csv='$csvFile'; `$cpu=(Get-CimInstance Win32_Processor).LoadPercentage; `$mem=(Get-CimInstance Win32_OperatingSystem); `$memPct=[math]::Round(($mem.TotalVisibleMemorySize - `$mem.FreePhysicalMemory)/`$mem.TotalVisibleMemorySize*100,1); `$disk=(Get-CimInstance Win32_LogicalDisk -Filter 'DriveType=3' | Where-Object DeviceID -eq 'C:'); `$diskPct=[math]::Round((`$disk.Size-`$disk.FreeSpace)/`$disk.Size*100,1); `$net=(Get-CimInstance Win32_PerfFormattedData_Tcpip_NetworkInterface | Where-Object Name -notlike '*Loopback*' | Measure-Object BytesTotalPersec -Sum).Sum; `$ts=Get-Date -Format 'yyyy-MM-dd HH:mm:ss'; if(-not(Test-Path `$csv)){'Timestamp,CPU_Pct,Mem_Pct,Disk_Pct,Net_BytesPerSec' | Out-File `$csv -Encoding utf8}; '`$ts,`$cpu,`$memPct,`$diskPct,`$net' | Out-File `$csv -Append -Encoding utf8}"
'@
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $intervalMinutes) -RepetitionDuration (New-TimeSpan -Days 7)
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Description "Winskill 效能基線採集(7 天)" -Force
Write-Host "✅ 已註冊定時任務 '$taskName',每 $intervalMinutes 分鐘採集一次,持續 7 天"
} else {
Write-Host "⚠️ 定時任務 '$taskName' 已存在,無需重複註冊"
}
# 顯示當前基線狀態
if (Test-Path $csvFile) {
$data = Import-Csv $csvFile
Write-Host "`n📊 當前基線資料:$($data.Count) 條記錄"
Write-Host "CPU 基線:$(($data | Measure-Object CPU_Pct -Average).Average)%(均值)"
Write-Host "記憶體 基線:$(($data | Measure-Object Mem_Pct -Average).Average)%(均值)"
Write-Host "磁碟 基線:$(($data | Measure-Object Disk_Pct -Average).Average)%(均值)"
} else {
Write-Host "📋 尚未開始採集,註冊定時任務後自動開始"
}
### 28.2 異常偏離告警(當前值 vs 基線)
$csvFile = "$env:USERPROFILE\.workbuddy\output\winskill\perf_baseline.csv"
$thresholdMultiplier = 2.0 # 偏離基線 200% 觸發告警
if (-not (Test-Path $csvFile)) {
Write-Host "❌ 尚未建立基線,請先執行 28.1 建立基線"
exit 1
}
$data = Import-Csv $csvFile
if ($data.Count -lt 10) {
Write-Host "⚠️ 基線資料不足($($data.Count) 條),建議至少採集 10 條後再分析"
}
# 計算基線均值
$cpuAvg = ($data | Measure-Object CPU_Pct -Average).Average
$memAvg = ($data | Measure-Object Mem_Pct -Average).Average
$diskAvg = ($data | Measure-Object Disk_Pct -Average).Average
# 獲取當前值
$cpuNow = (Get-CimInstance Win32_Processor).LoadPercentage
$mem = (Get-CimInstance Win32_OperatingSystem)
$memNow = [math]::Round(($mem.TotalVisibleMemorySize - $mem.FreePhysicalMemory) / $mem.TotalVisibleMemorySize * 100, 1)
$disk = (Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Where-Object DeviceID -eq 'C:')
$diskNow = [math]::Round(($disk.Size - $disk.FreeSpace) / $disk.Size * 100, 1)
Write-Host "`n📊 偏離分析報告"
Write-Host ("=" * 50)
Write-Host ("指標 當前值 基線均值 偏離倍數 狀態")
Write-Host ("-" * 50)
$alerts = @()
$cpuDev = if ($cpuAvg -gt 0) { $cpuNow / $cpuAvg } else { 0 }
$status = if ($cpuDev -ge $thresholdMultiplier) { "🚨 告警" } elseif ($cpuDev -ge 1.5) { "⚠️ 警告" } else { "✅ 正常" }
Write-Host ("CPU {0,6}% {1,6}% {2,6:F1}x {3}" -f $cpuNow, $cpuAvg, $cpuDev, $status)
if ($cpuDev -ge $thresholdMultiplier) { $alerts += "CPU 使用率偏離基線 ${cpuDev:F1} 倍(當前 $cpuNow%,基線 $cpuAvg%)" }
$memDev = if ($memAvg -gt 0) { $memNow / $memAvg } else { 0 }
$status = if ($memDev -ge $thresholdMultiplier) { "🚨 告警" } elseif ($memDev -ge 1.5) { "⚠️ 警告" } else { "✅ 正常" }
Write-Host ("記憶體 {0,6}% {1,6}% {2,6:F1}x {3}" -f $memNow, $memAvg, $memDev, $status)
if ($memDev -ge $thresholdMultiplier) { $alerts += "記憶體使用率偏離基線 ${memDev:F1} 倍(當前 $memNow%,基線 $memAvg%)" }
$diskDev = if ($diskAvg -gt 0) { $diskNow / $diskAvg } else { 0 }
$status = if ($diskDev -ge 1.3) { "🚨 告警" } elseif ($diskDev -ge 1.1) { "⚠️ 警告" } else { "✅ 正常" }
Write-Host ("磁碟 {0,6}% {1,6}% {2,6:F1}x {3}" -f $diskNow, $diskAvg, $diskDev, $status)
if ($diskDev -ge 1.3) { $alerts += "磁碟使用率偏離基線 ${diskDev:F1} 倍(當前 $diskNow%,基線 $diskAvg%)" }
Write-Host ("=" * 50)
if ($alerts.Count -eq 0) {
Write-Host "`n✅ 所有指標正常,無異常偏離"
} else {
Write-Host "`n🚨 發現 $($alerts.Count) 項異常偏離:"
$alerts | ForEach-Object { Write-Host " ⚠️ $_" }
}
### 28.3 趨勢預測(磁碟耗盡時間)
$csvFile = "$env:USERPROFILE\.workbuddy\output\winskill\perf_baseline.csv"
if (-not (Test-Path $csvFile)) {
Write-Host "❌ 尚未建立基線,請先執行 28.1 建立基線"
exit 1
}
$data = Import-Csv $csvFile
if ($data.Count -lt 5) {
Write-Host "⚠️ 資料不足($($data.Count) 條),無法預測"
exit 1
}
# 簡單線性迴歸預測磁碟耗盡時間
$diskValues = $data | ForEach-Object { [double]$_.Disk_Pct }
$n = $diskValues.Count
# 計算斜率(每天增長百分比)
$sumX = ($n - 1) * $n / 2
$sumY = ($diskValues | Measure-Object -Sum).Sum
$sumXY = 0
$sumX2 = 0
for ($i = 0; $i -lt $n; $i++) {
$sumXY += $i * $diskValues[$i]
$sumX2 += $i * $i
}
$slope = ($n * $sumXY - $sumX * $sumY) / ($n * $sumX2 - $sumX * $sumX)
$currentDisk = $diskValues[-1]
Write-Host "`n📈 趨勢預測報告"
Write-Host ("=" * 50)
Write-Host ("當前磁碟使用率:{0}%" -f $currentDisk)
if ($slope -le 0) {
Write-Host ("趨勢:磁碟使用率穩定或下降(斜率 {0:F4}%/採集週期)" -f $slope)
Write-Host "✅ 磁碟空間充足,無需擔心"
} else {
# 估算耗盡時間(基於採集間隔)
$cpuCores = (Get-CimInstance Win32_Processor).NumberOfLogicalProcessors
$intervalMinutes = if ($cpuCores -le 4) { 15 } else { 5 }
$periodsPerDay = 24 * 60 / $intervalMinutes
$daysToFull = (100 - $currentDisk) / ($slope * $periodsPerDay)
Write-Host ("增長速率:{0:F4}%/採集週期(約 {1:F2}%/天)" -f $slope, ($slope * $periodsPerDay))
if ($daysToFull -gt 365) {
Write-Host ("預計耗盡時間:> 1 年(約 {0:F0} 天)" -f $daysToFull)
Write-Host "✅ 磁碟空間充足"
} elseif ($daysToFull -gt 30) {
Write-Host ("預計耗盡時間:約 {0:F0} 天({1:F1} 個月)" -f $daysToFull, ($daysToFull / 30))
Write-Host "⚠️ 建議關注磁碟使用趨勢"
} elseif ($daysToFull -gt 7) {
Write-Host ("預計耗盡時間:約 {0:F0} 天" -f $daysToFull)
Write-Host "🚨 建議儘快清理磁碟空間"
} else {
Write-Host ("預計耗盡時間:約 {0:F0} 天" -f $daysToFull)
Write-Host "🔴 磁碟即將耗盡!請立即清理"
}
}
# 記憶體趨勢預測
$memValues = $data | ForEach-Object { [double]$_.Mem_Pct }
$sumY = ($memValues | Measure-Object -Sum).Sum
$sumXY = 0
for ($i = 0; $i -lt $n; $i++) {
$sumXY += $i * $memValues[$i]
}
$slope = ($n * $sumXY - $sumX * $sumY) / ($n * $sumX2 - $sumX * $sumX)
$currentMem = $memValues[-1]
Write-Host ("`n當前記憶體使用率:{0}%" -f $currentMem)
if ($slope -le 0) {
Write-Host "趨勢:記憶體使用率穩定或下降 ✅"
} else {
$periodsPerDay = 24 * 60 / $intervalMinutes
$daysToFull = (95 - $currentMem) / ($slope * $periodsPerDay) # 95% 作為預警線
if ($daysToFull -gt 30) {
Write-Host ("增長速率:{0:F4}%/採集週期 ✅ 記憶體充足" -f $slope)
} elseif ($daysToFull -gt 0) {
Write-Host ("增長速率:{0:F4}%/採集週期 ⚠️ 約 {1:F0} 天后達到 95%" -f $slope, $daysToFull)
} else {
Write-Host "🔴 記憶體使用率持續上升,建議排查記憶體洩漏"
}
}
Write-Host ("=" * 50)
### 28.4 效能瓶頸關聯分析
Write-Host "`n🔍 效能瓶頸關聯分析"
Write-Host ("=" * 60)
# 採集四維指標
$cpu = (Get-CimInstance Win32_Processor).LoadPercentage
$mem = (Get-CimInstance Win32_OperatingSystem)
$memPct = [math]::Round(($mem.TotalVisibleMemorySize - $mem.FreePhysicalMemory) / $mem.TotalVisibleMemorySize * 100, 1)
$disk = Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk | Where-Object Name -eq "_Total"
$diskPct = [math]::Round($disk.PercentDiskTime, 1)
$net = Get-CimInstance Win32_PerfFormattedData_Tcpip_NetworkInterface | Where-Object Name -notlike '*Loopback*'
$netPct = [math]::Round(($net | Measure-Object BytesTotalPersec -Sum).Sum / 1MB, 1) # MB/s
Write-Host ("CPU 使用率: {0,6}%" -f $cpu)
Write-Host ("記憶體使用率: {0,6}%" -f $memPct)
Write-Host ("磁碟活動時間: {0,6}%" -f $diskPct)
Write-Host ("網路流量: {0,6} MB/s" -f $netPct)
Write-Host ("-" * 60)
# 瓶頸判定邏輯
$bottleneck = $null
$severity = "正常"
if ($cpu -ge 90 -and $memPct -ge 85) {
$bottleneck = "CPU + 記憶體雙重瓶頸"
$severity = "🔴 嚴重"
} elseif ($diskPct -ge 90 -and $memPct -ge 85) {
$bottleneck = "磁碟 I/O + 記憶體雙重瓶頸(可能存在記憶體交換)"
$severity = "🔴 嚴重"
} elseif ($cpu -ge 90) {
$bottleneck = "CPU 瓶頸"
$severity = "🚨 高"
} elseif ($memPct -ge 90) {
$bottleneck = "記憶體瓶頸"
$severity = "🚨 高"
} elseif ($diskPct -ge 90) {
$bottleneck = "磁碟 I/O 瓶頸"
$severity = "🚨 高"
} elseif ($cpu -ge 70 -and $memPct -ge 70) {
$bottleneck = "CPU + 記憶體輕度壓力"
$severity = "⚠️ 中"
} elseif ($diskPct -ge 70 -and $memPct -ge 70) {
$bottleneck = "磁碟 + 記憶體輕度壓力"
$severity = "⚠️ 中"
} elseif ($cpu -ge 70) {
$bottleneck = "CPU 輕度壓力"
$severity = "⚠️ 中"
} elseif ($memPct -ge 70) {
$bottleneck = "記憶體輕度壓力"
$severity = "⚠️ 中"
} elseif ($diskPct -ge 70) {
$bottleneck = "磁碟 I/O 輕度壓力"
$severity = "⚠️ 中"
}
if ($bottleneck) {
Write-Host ("瓶頸判定:{0}({1})" -f $bottleneck, $severity)
} else {
Write-Host "✅ 系統執行正常,無明顯瓶頸"
}
# 關聯分析建議
Write-Host ("-" * 60)
Write-Host "關聯分析:"
if ($memPct -ge 85 -and $diskPct -ge 80) {
Write-Host " ⚠️ 記憶體高 + 磁碟 I/O 高 → 可能發生記憶體交換(頁面檔案頻繁讀寫)"
Write-Host " 建議:檢查記憶體佔用 Top 程序(模組 9.2),考慮增加實體記憶體"
}
if ($cpu -ge 80 -and $diskPct -ge 80) {
Write-Host " ⚠️ CPU 高 + 磁碟 I/O 高 → 可能在進行大量檔案操作或資料庫查詢"
Write-Host " 建議:檢查磁碟 I/O 程序(模組 9.3),定位高 I/O 來源"
}
if ($netPct -gt 100 -and $cpu -ge 70) {
Write-Host " ⚠️ 網路流量大 + CPU 高 → 可能受到 DDoS 攻擊或大量資料傳輸"
Write-Host " 建議:檢查網路連線(模組 13.1),排查可疑外連(模組 13.3)"
}
if ($cpu -lt 50 -and $memPct -lt 50 -and $diskPct -lt 50) {
Write-Host " ✅ 四維指標均正常,系統健康"
}
Write-Host ("=" * 60)
### 報錯與解決
| 報錯 | 原因 | 解決 |
|------|------|------|
| `Register-ScheduledTask 拒絕訪問` | 需要管理員許可權 | 以管理員身份執行 PowerShell |
| `無法載入檔案,因為在此係統上禁止執行指令碼` | 執行策略限制 | `Set-ExecutionPolicy RemoteSigned -Scope CurrentUser` |
| `基線資料不足` | 採集時間太短 | 等待至少 10 個採集週期後再分析 |
| `Get-CimInstance 找不到類` | WMI 服務異常 | 執行 `winmgmt /resetrepository` |
⚠️ 本模組所有操作均為只讀檢測,不會修改任何系統配置。
Write-Host "`n📋 等保 2.0 合規檢查報告"
Write-Host ("=" * 60)
Write-Host ("檢查時間:{0:yyyy-MM-dd HH:mm:ss}" -f (Get-Date))
Write-Host ("計算機名:$env:COMPUTERNAME")
Write-Host ("=" * 60)
$results = @()
# 1. 密碼策略
Write-Host "`n【1. 密碼策略】"
$pwdPolicy = net accounts | Out-String
$minPwdLen = if ($pwdPolicy -match 'Minimum password length\s+(\d+)') { $matches[1] } else { "0" }
$maxPwdAge = if ($pwdPolicy -match 'Maximum password age \(days\)\s+(\d+|Unlimited)') { $matches[1] } else { "Unknown" }
$minPwdAge = if ($pwdPolicy -match 'Minimum password age \(days\)\s+(\d+)') { $matches[1] } else { "0" }
$pwdHistory = if ($pwdPolicy -match 'Password history length\s+(\d+)') { $matches[1] } else { "0" }
$pass = [int]$minPwdLen -ge 8
$results += [PSCustomObject]@{ Category = "密碼策略"; Item = "密碼最小長度≥8"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "當前值:$minPwdLen" }
Write-Host (" 密碼最小長度≥8:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $minPwdLen)
$pass = $maxPwdAge -ne "Unlimited" -and [int]$maxPwdAge -le 90
$results += [PSCustomObject]@{ Category = "密碼策略"; Item = "密碼最長使用期限≤90天"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "當前值:$maxPwdAge" }
Write-Host (" 密碼最長使用期限≤90天:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $maxPwdAge)
$pass = [int]$pwdHistory -ge 5
$results += [PSCustomObject]@{ Category = "密碼策略"; Item = "密碼歷史記錄≥5"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "當前值:$pwdHistory" }
Write-Host (" 密碼歷史記錄≥5:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $pwdHistory)
# 2. 賬戶鎖定策略
Write-Host "`n【2. 賬戶鎖定策略】"
$lockoutThreshold = if ($pwdPolicy -match 'Lockout threshold\s+(\d+|Never)') { $matches[1] } else { "Unknown" }
$lockoutDuration = if ($pwdPolicy -match 'Lockout duration \(minutes\)\s+(\d+|Never)') { $matches[1] } else { "Unknown" }
$lockoutWindow = if ($pwdPolicy -match 'Lockout observation window \(minutes\)\s+(\d+|Never)') { $matches[1] } else { "Unknown" }
$pass = $lockoutThreshold -ne "Never" -and [int]$lockoutThreshold -le 5
$results += [PSCustomObject]@{ Category = "賬戶鎖定"; Item = "鎖定閾值≤5次"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "當前值:$lockoutThreshold" }
Write-Host (" 鎖定閾值≤5次:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $lockoutThreshold)
$pass = $lockoutDuration -ne "Never" -and [int]$lockoutDuration -ge 15
$results += [PSCustomObject]@{ Category = "賬戶鎖定"; Item = "鎖定時間≥15分鐘"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "當前值:$lockoutDuration" }
Write-Host (" 鎖定時間≥15分鐘:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $lockoutDuration)
# 3. 審計日誌策略
Write-Host "`n【3. 審計日誌策略】"
$auditPolicy = auditpol /get /category:* 2>$null | Out-String
$logonEvents = if ($auditPolicy -match 'Logon\s+Success and Failure') { "Success and Failure" } elseif ($auditPolicy -match 'Logon\s+(\w+)') { $matches[1] } else { "Not Configured" }
$objectAccess = if ($auditPolicy -match 'Object Access\s+Success and Failure') { "Success and Failure" } elseif ($auditPolicy -match 'Object Access\s+(\w+)') { $matches[1] } else { "Not Configured" }
$privilegeUse = if ($auditPolicy -match 'Privilege Use\s+Success and Failure') { "Success and Failure" } elseif ($auditPolicy -match 'Privilege Use\s+(\w+)') { $matches[1] } else { "Not Configured" }
$pass = $logonEvents -eq "Success and Failure"
$results += [PSCustomObject]@{ Category = "審計日誌"; Item = "登入事件審計(成功+失敗)"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "當前值:$logonEvents" }
Write-Host (" 登入事件審計:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $logonEvents)
$pass = $objectAccess -eq "Success and Failure"
$results += [PSCustomObject]@{ Category = "審計日誌"; Item = "物件訪問審計(成功+失敗)"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "當前值:$objectAccess" }
Write-Host (" 物件訪問審計:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $objectAccess)
# 4. 防火牆狀態
Write-Host "`n【4. 防火牆狀態】"
$firewallProfiles = Get-NetFirewallProfile
$allEnabled = ($firewallProfiles | Where-Object Enabled -eq $false).Count -eq 0
$results += [PSCustomObject]@{ Category = "防火牆"; Item = "所有配置檔案已啟用防火牆"; Status = if ($allEnabled) { "✅ 合規" } else { "❌ 不合規" }; Detail = "已啟用:$(($firewallProfiles | Where-Object Enabled).Count)/3" }
Write-Host (" 防火牆全部啟用:{0}(已啟用 {1}/3)" -f $(if ($allEnabled) { "✅" } else { "❌" }), (($firewallProfiles | Where-Object Enabled).Count))
# 5. 補丁更新狀態
Write-Host "`n【5. 補丁更新狀態】"
$hotfixes = Get-HotFix | Sort-Object InstalledOn -Descending
$pass = $false
if ($hotfixes.Count -gt 0) {
$lastUpdate = $hotfixes[0].InstalledOn
$daysSinceUpdate = ((Get-Date) - $lastUpdate).Days
$pass = $daysSinceUpdate -le 30
} else {
$lastUpdate = "無記錄"
$daysSinceUpdate = 9999
$pass = $false
}
$results += [PSCustomObject]@{ Category = "補丁更新"; Item = "最近 30 天內有補丁更新"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "最後更新:$lastUpdate($($daysSinceUpdate.Days) 天前)" }
Write-Host (" 最近 30 天有補丁:{0}(最後:{1:yyyy-MM-dd},{2} 天前)" -f $(if ($pass) { "✅" } else { "❌" }), $lastUpdate, $daysSinceUpdate.Days)
# 6. 遠端桌面安全
Write-Host "`n【6. 遠端桌面安全】"
$rdpEnabled = (Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -ErrorAction SilentlyContinue).fDenyTSConnections
$rdpStatus = if ($rdpEnabled -eq 0) { "已啟用" } else { "已停用" }
$nlaEnabled = (Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name UserAuthentication -Error Action SilentlyContinue).UserAuthentication
$nlaStatus = if ($nlaEnabled -eq 1) { "已啟用" } else { "未啟用" }
$pass = $rdpEnabled -ne 0 -or $nlaEnabled -eq 1 # 如果 RDP 啟用,必須啟用 NLA
$results += [PSCustomObject]@{ Category = "遠端桌面"; Item = "RDP 網路級身份驗證(NLA)"; Status = if ($pass) { "✅ 合規" } else { "❌ 不合規" }; Detail = "RDP:$rdpStatus,NLA:$nlaStatus" }
Write-Host (" NLA 已啟用:{0}(RDP:{1},NLA:{2})" -f $(if ($pass) { "✅" } else { "❌" }), $rdpStatus, $nlaStatus)
# 彙總
$compliant = ($results | Where-Object Status -like "✅*").Count
$nonCompliant = ($results | Where-Object Status -like "❌*").Count
$total = $results.Count
$rate = [math]::Round($compliant / $total * 100, 1)
Write-Host ("`n" + "=" * 60)
Write-Host ("📊 合規彙總:{0}/{1} 項合規(合規率 {2}%)" -f $compliant, $total, $rate)
Write-Host ("✅ 合規:$compliant 項")
Write-Host ("❌ 不合規:$nonCompliant 項")
Write-Host ("=" * 60)
if ($nonCompliant -gt 0) {
Write-Host "`n❌ 不合規項詳情:"
$results | Where-Object Status -like "❌*" | ForEach-Object {
Write-Host (" [{0}] {1}:{2}" -f $_.Category, $_.Item, $_.Detail)
}
}
### 29.2 CIS Benchmark 檢查(精選關鍵控制項)
Write-Host "`n🔒 CIS Benchmark 檢查(Windows Server 精選)"
Write-Host ("=" * 60)
$cisResults = @()
# CIS 1.1.1 - 密碼最小長度(CIS 要求 14)
Write-Host "`n【密碼策略】"
$minLen = (net accounts | Select-String 'Minimum password length').ToString() -replace '.*?\s+(\d+).*','$1'
$pass = [int]$minLen -ge 14
$cisResults += [PSCustomObject]@{ Control = "1.1.1"; Item = "密碼最小長度≥14"; Status = if ($pass) { "✅" } else { "❌" }; Detail = "當前:$minLen" }
Write-Host (" 1.1.1 密碼最小長度≥14:{0}(當前:{1})" -f $(if ($pass) { "✅" } else { "❌" }), $minLen)
# CIS 1.1.2 - 密碼複雜度
$complexity = (Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' -Name PasswordComplexity -ErrorAction SilentlyContinue).PasswordComplexity
$pass = $complexity -eq 1
$cisResults += [PSCustomObject]@{ Control = "1.1.2"; Item = "密碼複雜度已啟用"; Status = if ($pass) { "✅" } else { "❌" }; Detail = "當前:$(if ($complexity -eq 1) { '已啟用' } else { '未啟用' })" }
Write-Host (" 1.1.2 密碼複雜度:{0}" -f $(if ($pass) { "✅" } else { "❌" }))
# CIS 2.2.1 - 禁止 Guest 賬戶
Write-Host "`n【賬戶策略】"
$guestAccount = Get-LocalUser -Name "Guest" -ErrorAction SilentlyContinue
$pass = $guestAccount -and $guestAccount.Enabled -eq $false
$cisResults += [PSCustomObject]@{ Control = "2.2.1"; Item = "Guest 賬戶已停用"; Status = if ($pass) { "✅" } else { "❌" }; Detail = "當前:$(if ($guestAccount.Enabled -eq $false) { '已停用' } else { '已啟用' })" }
Write-Host (" 2.2.1 Guest 已停用:{0}" -f $(if ($pass) { "✅" } else { "❌" }))
# CIS 2.3.1.1 - 限制空密碼登入
$limitBlank = (Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' -Name LimitBlankPasswordUse -ErrorAction SilentlyContinue).LimitBlankPasswordUse
$pass = $limitBlank -eq 1
$cisResults += [PSCustomObject]@{ Control = "2.3.1.1"; Item = "限制空密碼登入"; Status = if ($pass) { "✅" } else { "❌" }; Detail = "當前:$(if ($limitBlank -eq 1) { '已限制' } else { '未限制' })" }
Write-Host (" 2.3.1.1 空密碼限制:{0}" -f $(if ($pass) { "✅" } else { "❌" }))
# CIS 5.1 - Windows Update 服務
Write-Host "`n【系統服務】"
$wuService = Get-Service -Name wuauserv -ErrorAction SilentlyContinue
$pass = $wuService -and $wuService.Status -eq "Running"
$cisResults += [PSCustomObject]@{ Control = "5.1"; Item = "Windows Update 服務執行中"; Status = if ($pass) { "✅" } else { "❌" }; Detail = "當前:$($wuService.Status)" }
Write-Host (" 5.1 Windows Update:{0}({1})" -f $(if ($pass) { "✅" } else { "❌" }), $wuService.Status)
# CIS 9.1 - Windows Firewall
Write-Host "`n【防火牆】"
$fwProfiles = Get-NetFirewallProfile
$allEnabled = ($fwProfiles | Where-Object Enabled -eq $false).Count -eq 0
$cisResults += [PSCustomObject]@{ Control = "9.1"; Item = "所有防火牆配置檔案已啟用"; Status = if ($allEnabled) { "✅" } else { "❌" }; Detail = "已啟用:$(($fwProfiles | Where-Object Enabled).Count)/3" }
Write-Host (" 9.1 防火牆全部啟用:{0}" -f $(if ($allEnabled) { "✅" } else { "❌" }))
# CIS 17.1 - 審計策略
Write-Host "`n【審計策略】"
$auditPolicy = auditpol /get /category:* 2>$null | Out-String
$securityLogon = if ($auditPolicy -match 'Logon\s+Success and Failure') { $true } else { $false }
$cisResults += [PSCustomObject]@{ Control = "17.1"; Item = "登入事件審計(成功+失敗)"; Status = if ($securityLogon) { "✅" } else { "❌" }; Detail = "當前:$(if ($securityLogon) { '已配置' } else { '未配置' })" }
Write-Host (" 17.1 登入審計:{0}" -f $(if ($securityLogon) { "✅" } else { "❌" }))
# 彙總
$compliant = ($cisResults | Where-Object Status -eq "✅").Count
$nonCompliant = ($cisResults | Where-Object Status -eq "❌").Count
$total = $cisResults.Count
$rate = [math]::Round($compliant / $total * 100, 1)
Write-Host ("`n" + "=" * 60)
Write-Host ("📊 CIS 彙總:{0}/{1} 項合規(合規率 {2}%)" -f $compliant, $total, $rate)
Write-Host ("=" * 60)
### 29.3 合規報告生成(Markdown 文本)
# 生成 Markdown 格式合規報告(純文本,可複製儲存為 .md 檔案)
$reportTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$reportName = "合規檢查報告_$env:COMPUTERNAME`_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
$report = @"
# Windows Server 合規檢查報告
| 專案 | 內容 |
|------|------|
| 計算機名 | $env:COMPUTERNAME |
| 檢查時間 | $reportTime |
| 作業系統 | $((Get-CimInstance Win32_OperatingSystem).Caption) |
| 版本 | $((Get-CimInstance Win32_OperatingSystem).Version) |
## 等保 2.0 檢查結果
| 類別 | 檢查項 | 狀態 | 詳情 |
|------|--------|------|------|
| 密碼策略 | 密碼最小長度≥8 | 待檢查 | - |
| 密碼策略 | 密碼最長使用期限≤90天 | 待檢查 | - |
| 密碼策略 | 密碼歷史記錄≥5 | 待檢查 | - |
| 賬戶鎖定 | 鎖定閾值≤5次 | 待檢查 | - |
| 賬戶鎖定 | 鎖定時間≥15分鐘 | 待檢查 | - |
| 審計日誌 | 登入事件審計(成功+失敗) | 待檢查 | - |
| 審計日誌 | 物件訪問審計(成功+失敗) | 待檢查 | - |
| 防火牆 | 所有配置檔案已啟用 | 待檢查 | - |
| 補丁更新 | 最近 30 天內有補丁更新 | 待檢查 | - |
| 遠端桌面 | RDP 網路級身份驗證(NLA) | 待檢查 | - |
## CIS Benchmark 檢查結果
| 控制項 | 檢查內容 | 狀態 | 詳情 |
|--------|----------|------|------|
| 1.1.1 | 密碼最小長度≥14 | 待檢查 | - |
| 1.1.2 | 密碼複雜度已啟用 | 待檢查 | - |
| 2.2.1 | Guest 賬戶已停用 | 待檢查 | - |
| 2.3.1.1 | 限制空密碼登入 | 待檢查 | - |
| 5.1 | Windows Update 服務執行中 | 待檢查 | - |
| 9.1 | 所有防火牆配置檔案已啟用 | 待檢查 | - |
| 17.1 | 登入事件審計(成功+失敗) | 待檢查 | - |
## 說明
- 本報告由 winskill 自動生成,僅作參考
- 實際合規判定需結合企業安全策略和行業標準
- 建議定期執行檢查並儲存報告備查
"@
# 輸出到控制台
Write-Host $report
# 儲存到使用者輸出目錄
$outputDir = "$env:USERPROFILE\.workbuddy\output\winskill"
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
$outputFile = Join-Path $outputDir "$reportName.md"
$report | Out-File -FilePath $outputFile -Encoding utf8
Write-Host ("`n✅ 報告已儲存到:{0}" -f $outputFile)
Write-Host "提示:可將 .md 檔案內容複製到 Word 或線上 Markdown 轉 PDF 工具生成 PDF"
### 29.4 不合規項修復建議
Write-Host "`n🔧 不合規項修復建議"
Write-Host ("=" * 60)
Write-Host "以下命令僅作參考,請確認後再執行:`n"
Write-Host "【密碼策略修復】"
Write-Host "net accounts /minpwlen:8 # 設定密碼最小長度 8 位"
Write-Host "net accounts /maxpwage:90 # 設定密碼最長使用期限 90 天"
Write-Host "net accounts /minpwage:1 # 設定密碼最短使用期限 1 天"
Write-Host "net accounts /uniquepy:5 # 密碼歷史記錄 5 個"
Write-Host "`n【賬戶鎖定修復】"
Write-Host "net accounts /lockoutthreshold:5 # 鎖定閾值 5 次"
Write-Host "net accounts /lockoutduration:15 # 鎖定時間 15 分鐘"
Write-Host "net accounts /lockoutwindow:15 # 鎖定觀察視窗 15 分鐘"
Write-Host "`n【審計策略修復】"
Write-Host "auditpol /set /subcategory:'Logon' /success:enable /failure:enable"
Write-Host "auditpol /set /subcategory:'Object Access' /success:enable /failure:enable"
Write-Host "auditpol /set /subcategory:'Privilege Use' /success:enable /failure:enable"
Write-Host "`n【防火牆修復】"
Write-Host "Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True"
Write-Host "`n【Guest 賬戶修復】"
Write-Host "Disable-LocalUser -Name 'Guest' # 停用 Guest 賬戶"
Write-Host "`n【RDP NLA 修復】"
Write-Host "Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name UserAuthentication -Value 1"
Write-Host ("`n" + "=" * 60)
Write-Host "⚠️ 以上命令僅供參考,請確認後再執行"
### 報錯與解決
| 報錯 | 原因 | 解決 |
|------|------|------|
| `auditpol 不是內部或外部命令` | 需要管理員許可權 | 以管理員身份執行 PowerShell |
| `Get-NetFirewallProfile 拒絕訪問` | 需要管理員許可權 | 以管理員身份執行 PowerShell |
| `無法儲存報告到輸出目錄` | 目錄不存在或許可權不足 | 手動建立 `%USERPROFILE%\.workbuddy\output\winskill` |
| `net accounts 輸出格式不符` | 系統語言非中文 | 根據實際輸出調整正則匹配 |
⚠️ 本模組需要 WinRM 服務支援,所有操作需管理員許可權。
# 伺服器註冊配置檔案
$serverFile = "$env:USERPROFILE\.workbuddy\output\winskill\servers.json"
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.workbuddy\output\winskill" | Out-Null
# 載入已有伺服器列表
$servers = @()
if (Test-Path $serverFile) {
$servers = Get-Content $serverFile -Raw | ConvertFrom-Json
}
Write-Host "`n🖥️ 遠端伺服器管理"
Write-Host ("=" * 50)
Write-Host "1. 新增伺服器"
Write-Host "2. 檢視已註冊伺服器"
Write-Host "3. 刪除伺服器"
Write-Host "4. 測試連線"
Write-Host ("=" * 50)
# 示例:新增伺服器(實際使用時通過 AI 互動輸入)
$action = Read-Host "請選擇操作 (1-4)"
switch ($action) {
"1" {
$ip = Read-Host "伺服器 IP 或主機名"
$user = Read-Host "使用者名稱(如 Administrator)"
$auth = Read-Host "認證方式 (1=密碼 2=證書)"
if ($auth -eq "1") {
$cred = Get-Credential -Message "輸入 $ip 的憑據"
# 儲存到 Windows Credential Manager(通過 cmdkey)
$target = "TERMSRV/$ip"
cmdkey /generic:$target /user:$($cred.UserName) /pass:$($cred.GetNetworkCredential().Password) 2>$null
Write-Host "✅ 憑據已儲存到 Windows Credential Manager"
}
$newServer = @{
ip = $ip
user = $user
addedAt = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
tags = @()
}
$servers += $newServer
$servers | ConvertTo-Json -Depth 3 | Out-File $serverFile -Encoding utf8
Write-Host "✅ 伺服器 $ip 已註冊"
}
"2" {
if ($servers.Count -eq 0) {
Write-Host "⚠️ 暫無已註冊伺服器"
} else {
Write-Host "`n已註冊伺服器:"
$servers | ForEach-Object { Write-Host " - $($_.ip)($($_.user))添加於 $($_.addedAt)" }
}
}
"3" {
$ip = Read-Host "要刪除的伺服器 IP"
$servers = $servers | Where-Object ip -ne $ip
$servers | ConvertTo-Json -Depth 3 | Out-File $serverFile -Encoding utf8
Write-Host "✅ 伺服器 $ip 已刪除"
}
"4" {
$ip = Read-Host "要測試的伺服器 IP"
$result = Test-WSMan -ComputerName $ip -ErrorAction SilentlyContinue
if ($result) {
Write-Host "✅ $ip WinRM 服務正常"
} else {
Write-Host "❌ $ip 無法連線,請檢查:"
Write-Host " 1. 目標伺服器是否開啟 WinRM(Enable-PSRemoting)"
Write-Host " 2. 防火牆是否放行 5985/5986 埠"
Write-Host " 3. 網路是否可達"
}
}
}
### 30.2 批次命令執行(並行)
$serverFile = "$env:USERPROFILE\.workbuddy\output\winskill\servers.json"
if (-not (Test-Path $serverFile)) {
Write-Host "❌ 暫無已註冊伺服器,請先執行 30.1 註冊"
exit 1
}
$servers = Get-Content $serverFile -Raw | ConvertFrom-Json
if ($servers.Count -eq 0) {
Write-Host "⚠️ 暫無已註冊伺服器"
exit 1
}
# 硬體自適應:並行數 = min(CPU 核心數, 伺服器數量)
$cpuCores = (Get-CimInstance Win32_Processor).NumberOfLogicalProcessors
$maxParallel = [Math]::Min($cpuCores, $servers.Count)
Write-Host ("`n⚡ 並行執行:{0} 個任務同時執行(CPU {1} 核)" -f $maxParallel, $cpuCores)
# 要執行的命令
$command = Read-Host "要在所有伺服器執行的命令"
Write-Host ("`n在 {0} 臺伺服器上執行:{1}" -f $servers.Count, $command)
Write-Host ("-" * 60)
# PowerShell 5.1 相容:使用 Invoke-Command 並行執行(原生支援多計算機)
$computerNames = $servers.ip
$cred = Get-Credential -Message "輸入所有伺服器的通用憑據"
Write-Host ("`n在 {0} 臺伺服器上執行:{1}" -f $servers.Count, $command)
Write-Host ("-" * 60)
try {
# Invoke-Command 原生支援多計算機並行執行
$results = Invoke-Command -ComputerName $computerNames -Credential $cred -ScriptBlock {
param($cmd)
$output = Invoke-Expression $cmd 2>&1
[PSCustomObject]@{
Server = $env:COMPUTERNAME
Status = "✅ 成功"
Output = $output | Out-String
}
} -ArgumentList $command -ErrorAction Stop
# 彙總結果
Write-Host ("`n" + "=" * 60)
Write-Host "📊 執行結果彙總:"
$results | ForEach-Object {
Write-Host ("`n[{0}] {1}" -f $_.Server, $_.Status)
if ($_.Output) { Write-Host $_.Output }
}
Write-Host ("=" * 60)
} catch {
Write-Host ("❌ 執行失敗:{0}" -f $_.Exception.Message)
Write-Host "提示:確認所有伺服器都已開啟 WinRM(執行 Enable-PSRemoting -Force)"
}
### 30.3 統一監控面板
$serverFile = "$env:USERPROFILE\.workbuddy\output\winskill\servers.json"
if (-not (Test-Path $serverFile)) {
Write-Host "❌ 暫無已註冊伺服器"
exit 1
}
$servers = Get-Content $serverFile -Raw | ConvertFrom-Json
Write-Host "`n📊 多伺服器統一監控面板"
Write-Host ("=" * 80)
Write-Host ("{0,-20} {1,8} {2,8} {3,8} {4,10} {5,10}" -f "伺服器", "CPU%", "記憶體%", "磁碟%", "狀態", "執行時間")
Write-Host ("-" * 80)
foreach ($server in $servers) {
try {
$session = New-PSSession -ComputerName $server.ip -ErrorAction Stop
$info = Invoke-Command -Session $session -ScriptBlock {
$cpu = (Get-CimInstance Win32_Processor).LoadPercentage
$mem = (Get-CimInstance Win32_OperatingSystem)
$memPct = [math]::Round(($mem.TotalVisibleMemorySize - $mem.FreePhysicalMemory) / $mem.TotalVisibleMemorySize * 100, 1)
$disk = (Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Where-Object DeviceID -eq 'C:')
$diskPct = [math]::Round(($disk.Size - $disk.FreeSpace) / $disk.Size * 100, 1)
$uptime = (Get-Date) - $mem.LastBootUpTime
[PSCustomObject]@{
CPU = $cpu
Mem = $memPct
Disk = $diskPct
Uptime = "{0}天{1}小時" -f $uptime.Days, $uptime.Hours
}
}
Remove-PSSession $session
$statusEmoji = if ($info.CPU -ge 90 -or $info.Mem -ge 90 -or $info.Disk -ge 90) { "🔴" } elseif ($info.CPU -ge 70 -or $info.Mem -ge 70 -or $info.Disk -ge 70) { "🟡" } else { "🟢" }
Write-Host ("{0,-20} {1,8} {2,8} {3,8} {4,10} {5,10}" -f $server.ip, "$($info.CPU)%", "$($info.Mem)%", "$($info.Disk)%", $statusEmoji, $info.Uptime)
} catch {
Write-Host ("{0,-20} {1,8} {2,8} {3,8} {4,10} {5,10}" -f $server.ip, "-", "-", "-", "❌ 離線", "-")
}
}
Write-Host ("-" * 80)
Write-Host "圖例:🟢 正常 🟡 警告 🔴 嚴重 ❌ 離線"
Write-Host ("=" * 80)
### 30.4 配置差異對比
$serverFile = "$env:USERPROFILE\.workbuddy\output\winskill\servers.json"
if (-not (Test-Path $serverFile)) {
Write-Host "❌ 暫無已註冊伺服器"
exit 1
}
$servers = Get-Content $serverFile -Raw | ConvertFrom-Json
if ($servers.Count -lt 2) {
Write-Host "⚠️ 至少需要 2 臺伺服器才能對比"
exit 1
}
Write-Host "`n🔍 配置差異對比"
Write-Host ("=" * 60)
# 選擇對比專案
Write-Host "對比專案:"
Write-Host "1. IIS 站點配置"
Write-Host "2. 服務狀態"
Write-Host "3. 防火牆規則"
Write-Host "4. 已安裝補丁"
$choice = Read-Host "請選擇 (1-4)"
$scriptBlock = switch ($choice) {
"1" { { Get-Website | Select-Object Name, State, PhysicalPath, Bindings | ConvertTo-Json } }
"2" { { Get-Service | Where-Object Status -ne 'Running' | Select-Object Name, Status, StartType | ConvertTo-Json } }
"3" { { Get-NetFirewallRule | Where-Object Enabled -eq 'True' | Select-Object DisplayName, Direction, Action | ConvertTo-Json } }
"4" { { Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 10 HotFixID, InstalledOn | ConvertTo-Json } }
}
# 採集所有伺服器資料
$allData = @{}
foreach ($server in $servers) {
try {
$session = New-PSSession -ComputerName $server.ip -ErrorAction Stop
$data = Invoke-Command -Session $session -ScriptBlock $scriptBlock
$allData[$server.ip] = $data | ConvertFrom-Json
Remove-PSSession $session
} catch {
Write-Host "⚠️ $($server.ip) 連線失敗:$($_.Exception.Message)"
}
}
# 對比輸出
if ($allData.Count -ge 2) {
$firstServer = $allData.Keys | Select-Object -First 1
$firstData = $allData[$firstServer]
Write-Host ("`n基準伺服器:$firstServer" -f $firstServer)
Write-Host ("-" * 60)
foreach ($server in $allData.Keys) {
if ($server -eq $firstServer) { continue }
$serverData = $allData[$server]
# 簡單差異對比(基於 JSON 字串比較)
$firstJson = $firstData | ConvertTo-Json -Compress
$serverJson = $serverData | ConvertTo-Json -Compress
if ($firstJson -eq $serverJson) {
Write-Host ("✅ {0}:與基準一致" -f $server)
} else {
Write-Host ("⚠️ {0}:存在差異" -f $server)
# 輸出具體差異(簡化版)
Write-Host (" 基準項數:{0},對比項數:{1}" -f @($firstData).Count, @($serverData).Count)
}
}
}
Write-Host ("=" * 60)
### 報錯與解決
| 報錯 | 原因 | 解決 |
|------|------|------|
| `WinRM 客戶端無法處理請求` | 目標 WinRM 未開啟 | 在目標伺服器執行 `Enable-PSRemoting -Force` |
| `訪問被拒絕` | 憑據錯誤或許可權不足 | 檢查使用者名稱密碼,確認使用者有遠端管理許可權 |
| `無法連線到遠端伺服器` | 網路不通或防火牆攔截 | 檢查網路連通性和 5985/5986 埠 |
| `New-PSSession 超時` | 伺服器響應慢或網路延遲高 | 增加 `-OperationTimeout` 引數 |
| `TrustedHosts 未配置` | 非域環境需配置 TrustedHosts | `Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.1.0/24" -Concatenate` |
| 你說 | AI 執行 |
|---|---|
"掃 C 盤大檔案" |
模組 1 |
"找重複檔案" |
模組 2 |
"清理臨時檔案" |
模組 3(需確認) |
"IIS 正常嗎" |
模組 4 |
"檢查服務" |
模組 5 |
"批次改名" |
模組 6(需確認) |
"磁碟報告" |
模組 7 |
"更新正常嗎" |
模組 8 |
"伺服器卡" |
模組 9 |
"有沒有被入侵" |
模組 10 |
"可疑啟動項" |
模組 11 |
"硬碟健康嗎" |
模組 12 |
"誰在連我" |
模組 13 |
"系統日誌錯誤" |
模組 14 |
"裝了什麼軟體" |
模組 15 |
"誰在伺服器上" |
模組 16 |
"可疑計劃任務" |
模組 17 |
"共享資料夾" |
模組 18 |
"DNS/網絡卡診斷" |
模組 19 |
"SSL證書快到期了嗎" |
模組 20 |
"防火牆規則有沒有漏洞" |
模組 21 |
"哪些服務崩過" |
模組 22 |
"系統檔案修復" |
模組 23 |
"儲存池狀態" |
模組 24 |
"備份正常嗎" |
模組 25 |
"Docker 狀態" |
模組 26 |
"幫我修 DNS" |
模組 27 |
"效能趨勢" |
模組 28 |
"合規檢查" |
模組 29 |
"遠端伺服器" |
模組 30 |
| 不支援 | 原因 |
|---|---|
| 編輯已壓縮的檔案 | 壓縮包內檔案無法直接修改 |
| 恢復已刪除的回收站檔案 | 超出工具範圍 |
| 修改登錄檔鍵值 | 風險過高,不在工具範圍 |
| BIOS/硬體層操作 | 超出作業系統層面 |
| 磁碟修復/擦除 | 超出工具範圍,需專業工具 |
| 終止網路連線 | 超出工具範圍 |
| 清除/停止事件日誌 | 超出工具範圍,僅只讀 |
| 解除安裝/安裝軟體 | 超出工具範圍,只讀審計 |
| 結束/斷開使用者會話 | 超出工具範圍,只讀監控 |
| 建立/刪除計劃任務 | 超出工具範圍,只讀審計 |
| 修改共享許可權 | 超出工具範圍,只讀審計 |
| 修改網絡卡 IP/DNS 配置 | 超出工具範圍,只讀診斷 |
| 申請/續簽/刪除 SSL 證書 | 超出工具範圍,只讀檢測 |
| 新增/刪除/修改防火牆規則 | 超出工具範圍,只讀審計 |
| 啟動/停止/修改服務配置 | 超出工具範圍,只讀診斷 |
| v3.1.0 | 2026-08-16 | 新增日誌輪轉功能(Write-WinskillLog,10MB/7天自動輪轉清理);新增模組索引命令(一鍵列出30個模組用途);改進:移除廢棄的 wmic 宣告(已全面遷移 CIM cmdlet) | | v3.0.0 | 2026-08-06 | 新增效能基線與趨勢分析模組(基線建立、異常偏離告警、趨勢預測、瓶頸關聯分析)、安全合規檢查清單模組(等保2.0、CIS Benchmark、合規報告、修復建議)、遠端多伺服器管理模組(伺服器註冊、批次命令執行、統一監控面板、配置差異對比),總計30個模組,從單機工具升級為多機管理平臺;修改展示方式:頂部模組導航錨點+每模組返回頂部連結 | | v2.0.0 | 2026-07-24 | 新增自動化修復嚮導模組(DNS修復、網路修復、WinUpdate修復、服務修復、磁碟清理、時間同步),總計27個模組,從診斷工具升級為修復工具 | | v1.9.0 | 2026-07-16 | 修復邏輯bug,新增 Docker / K8s 容器管理模組(Docker狀態總覽、容器資源監控、Docker健康檢查、K8s叢集狀態、容器日誌採集),總計26個模組,覆蓋Windows Server容器化場景 | | v1.8.0 | 2026-07-16 | 新增 Docker / K8s 容器管理模組(Docker狀態總覽、容器資源監控、Docker健康檢查、K8s叢集狀態、容器日誌採集),總計26個模組,覆蓋Windows Server容器化場景 | | v1.7.0 | 2026-07-10 | 新增系統檔案完整性檢查(SFC/DISM)、儲存池管理、備份狀態檢查3個模組,總計25個模組;每個模組加摺疊塊;效能硬體自適應最佳化;新增更新提醒和禁止檔案型別宣告 | | v1.6.0 | 2026-07-09 | 新增SSL證書過期檢測、防火牆規則審計、服務崩潰恢復狀態3個模組,總計22個模組 | | v1.5.0 | 2026-07-08 | 新增計劃任務審計、檔案共享審計、DNS網絡卡診斷3個模組,總計19個模組 | | v1.4.0 | 2026-07-07 | 新增事件日誌診斷、已安裝程式管理、使用者會話監控3個模組,總計16個模組 | | v1.3.0 | 2026-07-06 | 新增登錄檔啟動項審計、磁碟健康檢測、網路埠監控3個模組,總計13個模組 | | v1.2.0 | 2026-07-05 | 新增Windows Update效能監控、安全審計3個模組,所有命令摺疊隱藏 | | v1.1.0 | 2026-07-04 | 新增快速開始/報錯指引/FAQ | | v1.0.0 | 2026-07-03 | 初始版本,7個模組 |
如何獲取 winskill 最新版本:
skillhub upgrade winskill
當前版本:v3.0.0,如有新版本可用,請執行上述命令升級。
以下 5 大類檔案型別在所有涉及檔案操作的模組中均會被攔截,同時也會被 SkillHub 打包排除規則過濾。
.bat .cmd .ps1 .vbs .exe .dll .lnk .msi
.docx .xlsx .pptx .doc .xls .ppt .xlsm .docm .pptm
.iso .dmg .zip .rar .7z .tar .gz .apk .jar
.DS_Store .git 目錄 .env .log .tmp
.sh .com .scr .hta .reg
這款工具功能非常強大,30個模組基本覆蓋了伺服器運維的各種場景,安全保護機制做得很到位。但文件太長太複雜,找功能不夠方便,對普通使用者不太友好。