この回答は、エンタープライズ環境における大規模運用を想定した、実用的かつ堅牢なPowerShellスクリプト作成のベストプラクティスに従っています。標準コマンドレット(特にCIMモジュール)と.NETクラスの活用、並列処理によるパフォーマンス最適化、およびWinRM(WS-Management)プロトコルへの準拠を最優先事項として執筆しています。 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
[CIM/WMI] リモートPCの状態監視とイベントログ一括取得の自動化
【導入:解決する課題】
管理対象となる複数台のリモートPCに対し、1台ずつログインしてイベントログやリソース状況を確認する非効率な運用を、CIMセッションと並列処理によって自動化・高速化します。
【設計方針と処理フロー】
モダンな運用環境(PowerShell 7+推奨)に合わせ、DCOMに依存する Get-WmiObject ではなく、WS-Managementプロトコルを使用する Get-CimInstance を主軸に据えます。また、複数台へのクエリを効率化するため ForEach-Object -Parallel によるスレッド並列化を採用します。
graph TD
A["Start: リストまたは範囲指定"] --> B{"WinRM 接続確認"}
B -->|Success| C["ForEach-Object -Parallel"]
B -->|Failure| G["Offlineログ記録"]
C --> D["CIM: CPU/RAM/Disk情報取得"]
D --> E["Get-WinEvent: 過去24hのエラー取得"]
E --> F["カスタムオブジェクトへ結合"]
F --> H["CSV/JSON 出力保存"]
G --> H
H --> I[Finish]
【実装:コアスクリプト】
function Get-RemoteSystemStatus {
<#
.SYNOPSIS
リモートPCからシステムリソース情報とエラーイベントログを取得します。
.DESCRIPTION
CIMセッションを使用してCPU、メモリ、ディスク情報を取得し、
Get-WinEventを使用して直近24時間以内のCritical/Errorログを抽出します。
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string[]]$ComputerNames,
[int]$ThrottleLimit = 10,
[string]$OutputPath = ".\SystemReport_$(Get-Date -Format 'yyyyMMdd_HHmm').csv"
)
$results = $ComputerNames | ForEach-Object -Parallel {
$computer = $_
$report = [PSCustomObject]@{
ComputerName = $computer
Timestamp = Get-Date
Status = "Success"
CPU_Usage = $null
FreeMemoryGB = $null
DiskStatus = $null
ErrorLogs = $null
ErrorMessage = ""
}
try {
# WinRM接続テスト
if (-not (Test-Connection -ComputerName $computer -Count 1 -Quiet)) {
throw "Network unreachable"
}
# CIMセッションのオプション設定(タイムアウト等)
$option = New-CimSessionOption -ConnectTimeoutSec 5
$session = New-CimSession -ComputerName $computer -SessionOption $option -ErrorAction Stop
# 1. システムリソースの取得 (CIM)
$os = Get-CimInstance -CimSession $session -ClassName Win32_OperatingSystem
$cpu = Get-CimInstance -CimSession $session -ClassName Win32_Processor | Measure-Object -Property LoadPercentage -Average
$disk = Get-CimInstance -CimSession $session -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
Select-Object DeviceID, @{Name="FreeGB"; Expression={[math]::Round($_.FreeSpace / 1GB, 2)}}
$report.CPU_Usage = $cpu.Average
$report.FreeMemoryGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
$report.DiskStatus = ($disk | ForEach-Object { "$($_.DeviceID) $($_.FreeGB)GB" }) -join " | "
# 2. イベントログの取得 (直近24時間のError/Critical)
$query = @{
LogName = 'System','Application'
Level = 1,2 # 1:Critical, 2:Error
StartTime = (Get-Date).AddDays(-1)
}
$events = Get-WinEvent -FilterHashtable $query -ComputerName $computer -ErrorAction SilentlyContinue
$report.ErrorLogs = if ($events) { $events.Count } else { 0 }
Remove-CimSession $session
}
catch {
$report.Status = "Failed"
$report.ErrorMessage = $_.Exception.Message
}
return $report
} -ThrottleLimit $ThrottleLimit
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding utf8
Write-Host "レポートが保存されました: $OutputPath" -ForegroundColor Cyan
}
# 実行例
# $targets = Get-Content .\servers.txt
# Get-RemoteSystemStatus -ComputerNames $targets
【検証とパフォーマンス評価】
計測方法:
Measure-Command { Get-RemoteSystemStatus -ComputerNames (1..50 | % { "Target-PC-$_" }) }期待値:
シーケンシャル処理(PS 5.1以前): 1台あたり平均5〜10秒(合計約4〜8分)。
パラレル処理(PS 7.x, Throttle 10): ネットワークレイテンシにもよるが、約1分〜1.5分程度で完了。
ボトルネック: イベントログの取得範囲(StartTime)を広げすぎると、リモート側でのインデックス検索に時間がかかり、メモリ消費量が増大します。
【運用上の落とし穴と対策】
WinRM (WS-Management) の構成:
リモートPC側で
Enable-PSRemotingが必要。ワークグループ環境では
TrustedHostsの設定、あるいは証明書ベースのHTTPS接続が必要です。
PowerShell バージョンの差異:
ForEach-Object -Parallelは PowerShell 7 固有の機能です。Windows PowerShell 5.1 を使用する場合はStart-JobまたはPoshRSJob(外部モジュール)への差し替えが必要です。
イベントログのシリアライズ上限:
- 大量のイベントを取得しようとすると、WinRMの
MaxEnvelopeSize(デフォルト512KB程度)に抵触しエラーになります。本スクリプトではCountのみを取得し、詳細は個別調査とする設計で回避しています。
- 大量のイベントを取得しようとすると、WinRMの
UACと権限:
Win32_OperatingSystemやGet-WinEventへのアクセスには管理者権限が必要です。
【まとめ】
CIMへの移行: DCOMポート(RPC)を多用するWMIコマンドレットではなく、ポート固定(5985/5986)が容易なCIMコマンドレットを使用する。
スロットリング制御: 同時実行数(ThrottleLimit)は環境の帯域やメモリに合わせて調整し、ターゲットPCへの過負荷を避ける。
エラーハンドリングの徹底: ネットワーク断や権限不足を想定し、try/catchで「実行結果をオブジェクトとして返す」設計にすることで、出力後の分析を容易にする。

コメント