这是入门教程的最后一篇,希望读者可以实际用起来,实现逐步从入门到精通的全过程。
📅 第10天:综合项目实战与进阶
🔧 核心目标
将前9天所学(函数、REST API、作业、错误处理、JSON、模块化等)融会贯通,构建 可落地、可扩展、跨平台 的自动化工具。
✅ 适用环境:Windows / Linux / macOS(PowerShell 7+)
✅ 输出形式:独立脚本 + 模块化结构 + 用户友好交互
🎯 三大实战项目概览
| 项目 | 技术栈 | 应用场景 |
|---|---|---|
| 1. 自动化系统健康仪表盘 | Get-Process, Get-Volume, Test-Connection, ConvertTo-Html |
本地/远程服务器状态可视化 |
| 2. 跨平台服务检查器 | Invoke-RestMethod, Start-ThreadJob, 参数验证 |
微服务/API 健康监测(HTTP/S) |
| 3. RESTful 管理代理(Mock Agent) | Register-ObjectEvent, New-PSSessionConfiguration, 自定义 REST 接口 |
安全接收外部指令并执行本地操作 |
🛠️ 项目1:自动化系统健康仪表盘
💡 需求
生成一个 HTML 报告,展示:
- CPU 使用率 Top 5 进程
- 内存使用率
- 磁盘剩余空间(所有卷)
- 网络连通性(到关键服务如 DNS、网关)
- 最后更新时间
✅ 实现代码(Get-SystemHealthReport.ps1)
cs
function Get-SystemHealthReport {
[CmdletBinding()]
param(
[string]$OutputPath = "./system_health_$(Get-Date -Format 'yyyyMMdd_HHmm').html"
)
# 获取数据
$cpuTop = Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 Name, CPU
$memUsage = [math]::Round((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB, 2)
$totalMem = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
$diskInfo = Get-Volume | Where-Object DriveLetter | Select-Object DriveLetter,
@{Name="FreeGB"; Expression={[math]::Round($_.SizeRemaining / 1GB, 2)}},
@{Name="TotalGB"; Expression={[math]::Round($_.Size / 1GB, 2)}}
$networkTests = @(
@{Target="8.8.8.8"; Status=(Test-Connection 8.8.8.8 -Count 1 -Quiet)}
@{Target="gateway"; Status=(Test-Connection (Get-NetRoute 0.0.0.0).NextHop -Count 1 -Quiet -ErrorAction SilentlyContinue)}
)
# 构建 HTML
$html = @"
<!DOCTYPE html>
<html>
<head><title>系统健康报告</title>
<style>
body { font-family: Arial; margin: 20px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.green { color: green; }
.red { color: red; }
</style>
</head>
<body>
<h1>📊 系统健康仪表盘</h1>
<p>生成时间: $(Get-Date)</p>
<h2>🔥 CPU Top 5 进程</h2>
$($cpuTop | ConvertTo-Html -Fragment)
<h2>🧠 内存使用</h2>
<p>总内存: ${totalMem} GB | 可用: ${memUsage} GB</p>
<h2>💾 磁盘空间</h2>
$($diskInfo | ConvertTo-Html -Fragment)
<h2>🌐 网络连通性</h2>
<table>
<tr><th>目标</th><th>状态</th></tr>
$(
foreach ($test in $networkTests) {
$color = if ($test.Status) { "green" } else { "red" }
$statusText = if ($test.Status) { "✅ 在线" } else { "❌ 离线" }
"<tr><td>$($test.Target)</td><td class='$color'>$statusText</td></tr>"
}
)
</table>
</body>
</html>
"@
$html | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "✅ 报告已生成: $OutputPath" -ForegroundColor Green
if ($IsWindows) { Start-Process $OutputPath }
}
🔍 技术亮点
- 跨平台兼容 :使用
Get-CimInstance(Windows)或可替换为free(Linux) - HTML 内嵌 CSS:无需外部依赖,直接浏览器打开
- 自动打开报告 :
Start-Process(Windows)/open(macOS)/xdg-open(Linux)可进一步适配
🌐 项目2:跨平台服务检查器
💡 需求
并发检测多个 HTTP 服务状态(支持 Basic Auth、自定义 Header),输出表格并高亮异常。
✅ 实现代码(Test-ServiceHealth.ps1)
cs
function Test-ServiceHealth {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[hashtable[]]$Services, # 示例: @{Url="https://api.example.com"; Auth="user:pass"}
[int]$TimeoutSec = 10,
[int]$MaxConcurrency = 5
)
# 安装 ThreadJob(如未安装)
if (-not (Get-Module ThreadJob -ListAvailable)) {
Install-Module ThreadJob -Scope CurrentUser -Force
}
$jobs = foreach ($svc in $Services) {
Start-ThreadJob -ThrottleLimit $MaxConcurrency -ScriptBlock {
param($url, $auth, $timeout)
$headers = @{}
if ($auth) {
$bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
$base64 = [Convert]::ToBase64String($bytes)
$headers.Authorization = "Basic $base64"
}
$sw = [System.Diagnostics.Stopwatch]::StartNew()
try {
$response = Invoke-RestMethod -Uri $url -Headers $headers -TimeoutSec $timeout -UseBasicParsing
$status = "OK"
$statusCode = 200
} catch {
$status = "FAIL"
$statusCode = $_.Exception.Response?.StatusCode?.value__ -as [int] -or 0
}
$sw.Stop()
[PSCustomObject]@{
Service = $url
Status = $status
Code = $statusCode
Latency = $sw.ElapsedMilliseconds
Time = Get-Date
}
} -ArgumentList $svc.Url, $svc.Auth, $TimeoutSec
}
$results = $jobs | Wait-Job | Receive-Job
$jobs | Remove-Job
# 彩色输出
$results | ForEach-Object {
$color = if ($_.Status -eq "OK") { "Green" } else { "Red" }
Write-Host "$($_.Service) | $($_.Status) | Code: $($_.Code) | Latency: $($_.Latency)ms" -ForegroundColor $color
}
return $results
}
# 使用示例
$myServices = @(
@{Url = "https://httpbin.org/status/200"},
@{Url = "https://httpbin.org/status/500"},
@{Url = "https://api.github.com/rate_limit"; Auth = "user:token"} # 如需认证
)
Test-ServiceHealth -Services $myServices -MaxConcurrency 3
🔍 技术亮点
Start-ThreadJob:轻量级并发,避免传统 Job 开销- Basic Auth 支持:动态构造 Authorization Header
- 结构化输出 :返回对象数组,便于后续
Export-Csv或告警集成
🤖 项目3:RESTful 管理代理(安全执行本地命令)
⚠️ 仅限受控内网环境!需严格权限控制
💡 需求
启动一个本地 HTTP 服务,接收 JSON 指令(如 {"action": "restart-service", "service": "spooler"}),执行对应 PowerShell 命令并返回结果。
✅ 实现思路(简化版,基于 HttpListener)
cs
# rest-agent.ps1
function Start-ManagementAgent {
param([int]$Port = 8080)
$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add("http://localhost:$Port/")
$listener.Start()
Write-Host "🚀 管理代理已启动: http://localhost:$Port/" -ForegroundColor Cyan
while ($listener.IsListening) {
$context = $listener.GetContext()
$request = $context.Request
$response = $context.Response
if ($request.HttpMethod -ne "POST") {
$msg = "仅支持 POST"
$buffer = [System.Text.Encoding]::UTF8.GetBytes($msg)
$response.StatusCode = 405
$response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.Close()
continue
}
# 读取请求体
$reader = New-Object System.IO.StreamReader($request.InputStream)
$rawBody = $reader.ReadToEnd()
$reader.Dispose()
try {
$payload = $rawBody | ConvertFrom-Json
$action = $payload.action
switch ($action) {
"get-process" {
$result = Get-Process | Select-Object Name, Id, CPU | ConvertTo-Json
}
"restart-service" {
$svc = $payload.service
Restart-Service $svc -PassThru | Out-Null
$result = @{ status = "success"; service = $svc } | ConvertTo-Json
}
default {
throw "不支持的操作: $action"
}
}
$buffer = [System.Text.Encoding]::UTF8.GetBytes($result)
$response.ContentType = "application/json"
$response.StatusCode = 200
} catch {
$err = @{ error = $_.Exception.Message } | ConvertTo-Json
$buffer = [System.Text.Encoding]::UTF8.GetBytes($err)
$response.StatusCode = 500
}
$response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.Close()
}
}
# 启动代理(后台运行)
Start-ManagementAgent -Port 8080
🔒 安全加固建议(生产环境必须):
- 绑定到
127.0.0.1(禁止外网访问) - 添加 API Token 认证 (检查 Header 中
X-API-Key) - 白名单操作(只允许预定义的 action)
- 以最小权限账户运行
🧪 测试命令(另一终端):
cs
# 重启打印服务
$body = @{ action = "restart-service"; service = "spooler" } | ConvertTo-Json
Invoke-RestMethod -Uri "http://localhost:8080/" -Method Post -Body $body -ContentType "application/json"
# 获取进程列表
Invoke-RestMethod -Uri "http://localhost:8080/" -Method Post -Body '{"action":"get-process"}' -ContentType "application/json"
🧠 第10天能力升华
| 能力维度 | 体现 |
|---|---|
| 工程化思维 | 模块化、参数化、错误隔离 |
| 跨平台意识 | 避免 Windows 专属 Cmdlet(如用 Get-CimInstance → 可替换) |
| 安全第一 | 输入验证、最小权限、网络隔离 |
| 用户体验 | 彩色输出、HTML 报告、进度反馈 |
| 可扩展性 | 函数设计支持管道、导出 CSV、集成监控系统 |
🚀 课后挑战(选做)
- 将 仪表盘 改为 Web 服务(用
Start-ManagementAgent返回 HTML) - 为 服务检查器 添加 Slack/邮件告警(当状态 ≠ 200)
- 将 管理代理 封装为 Windows Service(使用
nssm.exe)