适用于 PowerShell 7.0 及以上版本
PowerShell 不仅能调用 API,还能创建 API。Polaris、Pode 等模块让 PowerShell 可以快速搭建 Web 服务器,处理 HTTP 请求。虽然不建议用 PowerShell 替代专业的 Web 框架,但在内网工具、运维自动化接口、轻量级中间服务等场景中,PowerShell API 服务器非常实用——快速实现一个健康检查接口、暴露系统信息给监控平台、提供配置查询 API。
本文将讲解如何使用 PowerShell 构建轻量级 REST API 服务。
使用 Pode 搭建 API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 Install-Module -Name Pode -Force -Scope CurrentUser$startApi = { Start-PodeServer { Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http Add-PodeMiddleware -Name 'Logger' -ScriptBlock { $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $method = $WebEvent .Method $path = $WebEvent .Path Write-PodeHost "[$timestamp ] $method $path " } Add-PodeRoute -Method Get -Path '/api/health' -ScriptBlock { $os = Get-CimInstance Win32_OperatingSystem $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 $uptime = [math ]::Round(((Get-Date ) - $os .LastBootUpTime).TotalDays, 1 ) Write-PodeJsonResponse -Value @ { status = 'healthy' timestamp = (Get-Date ).ToString('yyyy-MM-ddTHH:mm:ss' ) computer = $env:COMPUTERNAME uptime = "$uptime 天" cpuUsage = "$ ($cpu .LoadPercentage)%" memoryUsage = "$ ([math]::Round(($os .TotalVisibleMemorySize - $os .FreePhysicalMemory) / $os .TotalVisibleMemorySize * 100, 1))%" } } Add-PodeRoute -Method Get -Path '/api/processes' -ScriptBlock { $top = if ($WebEvent .Query['top' ]) { [int ]$WebEvent .Query['top' ] } else { 10 } $sortBy = if ($WebEvent .Query['sort' ]) { $WebEvent .Query['sort' ] } else { 'Memory' } $processes = Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First $top | ForEach-Object { @ { name = $_ .Name pid = $_ .Id memoryMB = [math ]::Round($_ .WorkingSet64 / 1 MB, 1 ) cpu = [math ]::Round($_ .CPU, 2 ) } } Write-PodeJsonResponse -Value @ { count = $processes .Count data = $processes } } Add-PodeRoute -Method Get -Path '/api/disks' -ScriptBlock { $disks = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object { $totalGB = [math ]::Round($_ .Size / 1 GB, 2 ) $freeGB = [math ]::Round($_ .FreeSpace / 1 GB, 2 ) @ { drive = $_ .DeviceID totalGB = $totalGB freeGB = $freeGB usagePct = [math ]::Round(($_ .Size - $_ .FreeSpace) / $_ .Size * 100 , 1 ) } } Write-PodeJsonResponse -Value @ { data = $disks } } } } Write-Host "API 服务器启动中..." -ForegroundColor CyanWrite-Host "端点:" -ForegroundColor YellowWrite-Host " GET http://localhost:8080/api/health" -ForegroundColor GreenWrite-Host " GET http://localhost:8080/api/processes?top=5" -ForegroundColor GreenWrite-Host " GET http://localhost:8080/api/disks" -ForegroundColor Green
执行结果示例:
1 2 3 4 5 6 API 服务器启动中...端点: GET http: GET http: GET http: [2025 -07 -23 08 :30 :15 ] GET /api/health
POST 接口与请求处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 $apiWithPost = { Start-PodeServer { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http Add-PodeRoute -Method Post -Path '/api/execute' -ScriptBlock { $body = $WebEvent .Data if (-not $body .command) { Set-PodeResponseStatus -Code 400 Write-PodeJsonResponse -Value @ { error = '缺少 command 参数' } return } $allowed = @ ('Get-Process' , 'Get-Service' , 'Get-EventLog' , 'Get-CimInstance' ) $cmdName = ($body .command -split '\s+' )[0 ] if ($cmdName -notin $allowed ) { Set-PodeResponseStatus -Code 403 Write-PodeJsonResponse -Value @ { error = "命令不允许:$cmdName " } return } try { $result = Invoke-Expression $body .command -ErrorAction Stop $jsonResult = $result | ConvertTo-Json -Depth 3 -Compress Write-PodeJsonResponse -Value @ { success = $true command = $body .command result = $result } } catch { Set-PodeResponseStatus -Code 500 Write-PodeJsonResponse -Value @ { success = $false error = $_ .Exception.Message } } } Add-PodeRoute -Method Post -Path '/api/alert' -ScriptBlock { $body = $WebEvent .Data $alertInfo = @ { timestamp = (Get-Date ).ToString('yyyy-MM-ddTHH:mm:ss' ) source = $body .source level = $body .level message = $body .message hostname = $body .hostname receivedAt = Get-Date -Format 'HH:mm:ss' } $alertInfo | ConvertTo-Json | Add-Content "C:\Logs\alerts.json" -Encoding UTF8 Write-PodeJsonResponse -Value @ { received = $true id = [guid ]::NewGuid().ToString() } } Add-PodeRoute -Method Get -Path '/api/alerts' -ScriptBlock { $count = if ($WebEvent .Query['count' ]) { [int ]$WebEvent .Query['count' ] } else { 10 } $level = $WebEvent .Query['level' ] if (Test-Path "C:\Logs\alerts.json" ) { $alerts = Get-Content "C:\Logs\alerts.json" -Tail $count | ConvertFrom-Json | Where-Object { if ($level ) { $_ .level -eq $level } else { $true } } Write-PodeJsonResponse -Value @ { count = $alerts .Count data = $alerts } } else { Write-PodeJsonResponse -Value @ { count = 0 ; data = @ () } } } } }
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 $body = @ { command = "Get-Process | Select-Object -First 3 Name,Id" } | ConvertTo-Json Invoke-RestMethod -Uri "http://localhost:8081/api/execute" -Method Post -Body $body -ContentType "application/json" $alert = @ { source = "monitor" level = "ERROR" message = "磁盘空间不足" hostname = "SRV01" } | ConvertTo-Json Invoke-RestMethod -Uri "http://localhost:8081/api/alert" -Method Post -Body $alert -ContentType "application/json"
API 客户端封装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function Get-ServerHealth { param ([string ]$ServerUrl = "http://localhost:8080" ) try { $response = Invoke-RestMethod -Uri "$ServerUrl /api/health" -TimeoutSec 5 return $response } catch { Write-Host "健康检查失败:$ ($_ .Exception.Message)" -ForegroundColor Red return $null } } function Get-ServerProcesses { param ( [string ]$ServerUrl = "http://localhost:8080" , [int ]$Top = 10 ) return Invoke-RestMethod -Uri "$ServerUrl /api/processes?top=$Top " -TimeoutSec 10 } $servers = @ ( "http://web-01:8080" , "http://web-02:8080" , "http://db-01:8080" ) Write-Host "集群健康检查:" -ForegroundColor Cyanforeach ($server in $servers ) { $health = Get-ServerHealth -ServerUrl $server if ($health ) { $status = $health .status $color = if ($status -eq "healthy" ) { "Green" } else { "Red" } Write-Host " $server : $status (CPU: $ ($health .cpuUsage), MEM: $ ($health .memoryUsage))" -ForegroundColor $color } else { Write-Host " $server : 不可达" -ForegroundColor Red } }
执行结果示例:
1 2 3 4 集群健康检查: http: http: http:
注意事项
安全风险 :暴露命令执行接口极度危险,生产环境必须加认证、限白名单、审计日志
性能限制 :PowerShell HTTP 服务器性能有限,不适合高并发场景(建议使用反向代理)
认证 :生产 API 必须添加认证(API Key、JWT、Basic Auth),Pode 内置认证中间件
HTTPS :生产环境使用 HTTPS,Pode 支持自签名证书和 Let’s Encrypt
服务化 :长期运行的 API 应注册为 Windows 服务,使用 nssm 或 Register-PodeService
错误处理 :API 接口必须有完善的错误处理和统一的错误响应格式