Windowsの正常性を1分でチェック!ログ出力する自動化スクリプト

WindowsやPCの保守運用において、避けて通れないのが「正常性確認」です。
皆さんは、毎朝の点検でこんな作業を繰り返していませんか?
「イベントビューアーを立ち上げて、膨大なログの中からエラーを探す」「サービス管理画面を開いて、重要なサービスが止まっていないか一つずつ目視する」「エクスプローラーからドライブ容量の空きを確認する」……。
一つひとつは数分の作業かもしれません。
しかし、確認対象のサーバーが増えたり、忙しい業務の合間に行ったりするには、あまりにも非効率で「ノイズ」の多い作業です。
何より、手動の確認はヒューマンエラーを招きやすく、深刻なトラブルの予兆を見落とすリスクも孕んでいます。
そこで活用したいのが、Windows標準の強力なシェル環境「PowerShell」です。
本記事では、これら全ての確認項目を「わずか1分」で一括点検し、結果をログ出力する自動化スクリプトをご紹介します。
単にログを出すだけでなく、監視不要なエラーを除外する「ホワイトリスト機能」など、実務で即戦力となる設計指針も解説します。
この記事を読み終える頃には、退屈なルーチン作業から解放され、よりクリエイティブな業務に時間を使えるようになっているはずです。
それでは、具体的なスクリプトの構築に入りましょう。
サーバーの正常性確認を、手動でやっていませんか?
サーバー管理者にとって、日々の「朝イチの健康診断」は欠かせないルーチンワークです。
しかし、その確認作業、いまだに一つひとつのサーバーへリモートデスクトップでログインし、マウスをカチカチ動かして行っていませんか?
「数台程度なら手動でもすぐ終わる」 「昔からの慣習だから」
そんな風に考えている方も多いかもしれません。
ですが、システムが大規模化し、スピード感が求められる現代の運用において、「手作業による目視確認」はもはやリスクでしかありません。本記事では、そんな退屈で神経を使う作業をPowerShellでスマートに解決する方法を解説します。
イベントビューアーを毎回開く手間を削減するメリット
Windowsのトラブルシューティングにおいて、イベントビューアーは強力な武器です。
しかし、それを「毎日・手動で」開くのは非効率の極みと言わざるを得ません。
自動化によってこの手間を省くことで、以下のような絶大なメリットが得られます。
- 「数分」が「数秒」に:
ログイン、管理ツールの起動、フィルター設定といった一連の動作が、スクリプト実行一発(あるいはタスクスケジューラによる完全無人化)で完了します。 - ヒューマンエラーの撲滅:
眠い目をこすりながらエラーログを流し読みして、重要な警告を見落とす……。
そんな人間特有のミスを、プログラムによる厳密な判定で防ぎます。 - 「攻めの保守」への転換:
ログの確認に費やしていた時間を、システム改善や自動化の高度化といった、より生産性の高い業務に充てられるようになります。
スクリプトの設計指針
「ただ動くスクリプト」を作るのは簡単ですが、実務で「使えるスクリプト」にするためには、明確な設計指針が必要です。
本スクリプトでは、管理者の負担を最小限にしつつ、見落としをゼロにするための2つのこだわりを詰め込みました。
「1時間以内」という条件設定の理由
このスクリプトでは、イベントログの取得範囲をデフォルトで「過去1時間以内」に絞り込んでいます。
これには2つの大きな理由があります。
- 定時実行(タスクスケジューラ)との相性:
このスクリプトをタスクスケジューラで1時間おきに実行するように設定すれば、常に最新の状態だけを差分でチェックし続けることが可能です。
万が一の際も、最大1時間の遅れで異常を検知できる体制が整います。 - ノイズの遮断と即時性:
数日分のログを一気に出力してしまうと、すでに解決済みの古いエラーまで混ざり込み、本当に確認すべき「今起きている問題」が埋もれてしまいます。
ホワイトリスト(除外リスト)機能の重要性
Windowsを運用している方なら共感していただけると思いますが、イベントビューアーには「実害はないが、仕様上どうしても出てしまうエラー」が数多く存在します。
これらをすべて正直に拾っていては、通知がオオカミ少年化し、やがて管理者はログを見なくなってしまいます。
- 「無視していいエラー」をリスト化し、判定から除外する。
- 「真に警戒すべき異常」だけがログに残るようにする。
この「引き算」の設計こそが、自動化スクリプトを長続きさせるための運用上のキモとなります。
本スクリプトでは、特定のイベントIDやソース名を簡単に除外できる仕組みを取り入れています。
イベントログから「真に警戒すべきエラー」を抽出する
Windowsの異常を知るための第一歩は、イベントログの解析です。
しかし、イベントビューアーを漫然と眺めるだけでは、大量の「情報」ログに埋もれた「異常」を見逃してしまいます。
ここでは、効率的にエラーを炙り出すテクニックを解説します。
Application / System の2系統を網羅
Windowsには多くのログ出力先がありますが、サーバーやPCの健全性を測る上で最低限チェックすべきは以下の3系統です。
- System(システム): OSそのものやドライバ、ハードウェアに起因する重大なエラー。
- Application(アプリケーション): インストールされているソフトウェアやサービスの不具合。
これらを一括で、かつ過去1時間以内に絞って取得することで、「たった今起きている問題」にフォーカスできます。
Get-WinEvent で特定のイベントIDやソースを除外するテクニック
「エラーが出ているけれど、これは仕様だから無視していい」というノイズは、運用現場では必ず発生します。
これをフィルタリングするために、Get-WinEvent の -FilterHashtable と、PowerShellのフィルタリング機能を組み合わせて使います。
以下は、特定のイベントIDを除外しながら、重要度の高いログ(クリティカル・エラー)のみを抽出するコードの要部です。
# 1時間前の時刻を定義
$startTime = (Get-Date).AddHours(-1)
# チェック対象のログ名
$logNames = @("System", "Application")
# 除外したいイベントIDのリスト(例:DCOMの不快なエラーなど)
$excludeIDs = @(10016, 7031)
foreach ($log in $logNames) {
try {
# 効率的なハッシュテーブルによるフィルタリング
Get-WinEvent -FilterHashtable @{
LogName = $log
StartTime = $startTime
Level = 1, 2 # 1:Critical, 2:Error
} -ErrorAction SilentlyContinue |
Where-Object { $excludeIDs -notcontains $_.Id } | # ホワイトリスト判定
Select-Object TimeCreated, Id, LevelDisplayName, Message
} catch {
Write-Host "Log [$log] の取得に失敗しました。" -ForegroundColor Yellow
}
}
Point:
Get-WinEvent は Get-EventLog よりも高速で、かつ詳細なフィルタリングが可能です。
実務用のスクリプトでは Get-WinEvent の使用が推奨されます。
「自動起動」なのに停止しているサービスを特定する
Windowsにおいて、重要な役割を担うプログラムの多くは「サービス」としてバックグラウンドで動作しています。
本来「自動」で立ち上がるはずのサービスが止まっている状態は、何らかのトラブルが発生しているサインです。
しかし、単純に「停止しているサービス」をすべて抽出すると、膨大なリストが出来上がってしまいます。
自動 / 自動(トリガー)/ 自動(遅延)のフィルタリング
実務でチェックすべきは、「スタートアップの種類が『自動』に設定されているのに、現在『停止』しているもの」だけです。
PowerShellの Get-Service コマンドレットを使用すれば、これらを効率的に絞り込めます。
ここで注意したいのが、以下の3つの「自動」です。
- 自動: OS起動時に即座に開始される。これが止まっている場合は要チェック。
- 自動(トリガー開始): 特定のイベントが発生したときだけ動く。停止していても正常な場合が多い。
- 自動(遅延開始): 他のサービスが立ち上がった後に少し遅れて起動する。
スクリプトでは、特に「遅延開始」がまだ起動処理中なのか、それとも失敗して止まっているのかを正しく判定させるロジックが重要になります。
監視不要なサービスをホワイトリストで弾く方法
Windows標準のサービスや、サードパーティ製アプリの中には、スタートアップが「自動」でも、処理が終わると潔く停止するものが存在します。
これらを「異常」として検知しないために、除外リスト(ホワイトリスト)を作成しましょう。
# 監視対象外とするサービス名のリスト
$serviceWhitelist = @(
"Gupdate", # Google Update
"EdgeUpdate", # Microsoft Edge Update
"RemoteRegistry", # 特定環境で不要な場合など
"sppsvc" # Software Protection (必要に応じて停止する)
)
# 自動起動設定なのに停止しているサービスを抽出
$targetServices = Get-Service | Where-Object {
$_.StartType -eq 'Automatic' -and
$_.Status -eq 'Stopped' -and
$serviceWhitelist -notcontains $_.Name
}
# 結果の表示
foreach ($service in $targetServices) {
[PSCustomObject]@{
ServiceName = $service.Name
DisplayName = $service.DisplayName
Status = $service.Status
}
}
運用アドバイス: 構築したばかりのサーバーでは、まずホワイトリストなしで実行し、環境特有の「止まっていても問題ないサービス」を洗い出すことから始めるのがコツです。
リソースの枯渇を見逃さない
イベントログが「過去の記録」であるのに対し、リソース状況は「現在の体調」です。
特に、ディスク容量の不足はOSやアプリケーションのハングアップを招く致命的な要因となります。
ディスク空き容量チェック:しきい値(10%以下など)による警告
「ディスクがいっぱいになってから対処する」のではなく、「少なくなってきたら検知する」のが運用の鉄則です。
本スクリプトでは、すべてのドライブをスキャンし、空き容量が一定の割合(例:10%)を下回った場合に警告を出すロジックを組み込みます。
# 警告を出すしきい値(%)
$threshold = 10
# ドライブ情報の取得
Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object {
$freeSpaceGB = [Math]::Round($_.FreeSpace / 1GB, 2)
$totalSizeGB = [Math]::Round($_.Size / 1GB, 2)
$freePercent = [Math]::Round(($_.FreeSpace / $_.Size) * 100, 2)
if ($freePercent -lt $threshold) {
Write-Host "【警告】$($_.DeviceID) の空き容量が少なくなっています! ($freePercent%)" -ForegroundColor Red
}
}
CPU・メモリ使用率:パフォーマンスのスパイクを確認
CPUやメモリの使用率は常に変動するため、一瞬の数値だけで「異常」と断定するのは難しいものです。
しかし、チェックした瞬間に「使用率が90%を超えている」ような状態が続いていれば、何らかのプロセスが暴走している可能性があります。
スクリプトでは、実行時点のサマリーを取得し、異常な高負荷がかかっていないかを確認します。
- CPU使用率: 高負荷が続くと、管理操作(RDPログインなど)すら受け付けなくなる。
- メモリ使用率: 空きがなくなるとスワップが発生し、システム全体のパフォーマンスが著しく低下する。
特に、DaaSやVDI環境を運用している場合、1台のホストに多数のユーザーが収容されているため、これらのリソース監視は単体の物理サーバー以上に重要度が増します。
# しきい値の設定(%)
$cpuThreshold = 80
$memThreshold = 90
# 1. CPU使用率の取得(1秒間の平均を取得)
$cpuUsage = (Get-Counter '\Processor(_Total)\% Processor Time' -SampleInterval 1).CounterSamples.CookedValue
$cpuUsage = [Math]::Round($cpuUsage, 1)
# 2. メモリ使用率の取得
$os = Get-CimInstance Win32_OperatingSystem
$totalMem = $os.TotalVisibleMemorySize
$freeMem = $os.FreePhysicalMemory
$memUsage = [Math]::Round((($totalMem - $freeMem) / $totalMem) * 100, 1)
# 結果の判定と出力
$cpuStatus = if ($cpuUsage -gt $cpuThreshold) { "【警告】高負荷" } else { "正常" }
$memStatus = if ($memUsage -gt $memThreshold) { "【警告】容量不足" } else { "正常" }
[PSCustomObject]@{
Item = "CPU使用率"
Usage = "$cpuUsage %"
Threshold = "$cpuThreshold %"
Status = $cpuStatus
}
[PSCustomObject]@{
Item = "メモリ使用率"
Usage = "$memUsage %"
Threshold = "$memThreshold %"
Status = $memStatus
}
解説:なぜ「1秒間」のサンプリングが必要か
CPU使用率を Get-CimInstance などで取得すると、その瞬間だけの数値(あるいはOS起動時からの平均)が返ってきてしまい、実態に即さないことがあります。
-SampleInterval 1の意味:Get-Counterを使い、あえて1秒間のサンプリングを行うことで、「今まさに負荷がかかっているか」をより正確に判定できます。- メモリの計算: Windowsのメモリ管理は複雑ですが、運用上は「物理メモリがどれだけ埋まっているか」をパーセンテージで出すのが最も直感的です。
Tips: スクリプト実行時にCPUが常に高い場合は、プロセスの詳細を書き出す
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5といったコマンドを組み合わせるのも有効なカスタマイズです。
保守の要、更新プログラムの状況を確認
セキュリティ対策の基本であるWindows Updateですが、自動更新に任せきりにしていると、「実は失敗し続けていた」「再起動待ちのまま数週間放置されていた」という事態が起こり得ます。
Windows Update 適用履歴:最終更新日が古くなっていないか
まず確認すべきは、「最後にいつ更新が成功したか」です。
本スクリプトでは、OSに記録されているクイック修正エンジニアリング(QFE)の情報を参照し、最新の修正プログラムが適用された日付を確認します。
もし最終適用日から30日以上経過している場合は、何らかの理由で更新が滞っている可能性があるため、警告を出す設計にします。
# 最終更新日の取得(最新の1件)
$lastUpdate = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 1
if ($lastUpdate) {
$daysSinceUpdate = ((Get-Date) - $lastUpdate.InstalledOn).Days
$status = if ($daysSinceUpdate -gt 30) { "【注意】30日以上更新されていません" } else { "正常" }
[PSCustomObject]@{
"最終適用KB" = $lastUpdate.HotFixID
"適用日" = $lastUpdate.InstalledOn.ToShortDateString()
"経過日数" = "$daysSinceUpdate 日"
"ステータス" = $status
}
}
隠れた重要項目:再起動待ち(Pending Reboot)の確認
Windows Updateにおいて、パッチの適用と同じくらい重要なのが「再起動」です。
再起動待ちの状態では、脆弱性が修正されていないばかりか、システムの動作が不安定になることもあります。
以下のレジストリキーをチェックすることで、システムが再起動を求めているかどうかを判定できます。
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequiredHKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending
これらのキーが存在する場合、スクリプトの結果に「再起動が必要」というフラグを立てることで、管理者は一目で「今夜再起動作業が必要だ」と判断できるようになります。
# 再起動が必要かどうかを判定するフラグ
$isRebootPending = $false
# 1. Windows Updateによる再起動要求
if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") {
$isRebootPending = $true
}
# 2. コンポーネントベースサービシング(CBS)による保留
if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") {
$isRebootPending = $true
}
# 3. ファイルの置換保留(PendingFileRenameOperations)
$pendingFileRename = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name "PendingFileRenameOperations" -ErrorAction SilentlyContinue
if ($pendingFileRename) {
$isRebootPending = $true
}
# 結果の出力
$rebootStatus = if ($isRebootPending) { "【警告】再起動待ちです" } else { "正常(再起動不要)" }
[PSCustomObject]@{
CheckItem = "再起動ステータス"
Result = $rebootStatus
Action = if ($isRebootPending) { "速やかに再起動を計画してください" } else { "継続運用可能" }
}
解説:チェックしている3つのポイント
このスクリプトでは、実務上重要となる3つのレジストリ箇所を網羅しています。
- Windows Update関連: 一般的な更新プログラム適用後に作成されるフラグです。
- CBS(Component Based Servicing): OSの機能追加や、より深いシステムコンポーネントの更新時に参照されます。
- PendingFileRenameOperations: 使用中のファイルを上書きするために「次回の起動時にファイル名を変更する」という予約が入っている状態です。ドライバの更新などでよく発生します。
運用上の注意: サーバー運用では、勝手に再起動させるわけにはいきません。
このスクリプトで「再起動待ち」を検知し、ログとして出力させることで、「今夜のメンテナンス時間で再起動が必要かどうか」を日中のうちに判断できるのが最大のメリットです。
1つのスクリプトでログを一括出力
これまで解説してきた「イベントログ」「サービス状況」「リソース消費」「更新プログラム」の4項目は、どれも欠かすことのできない重要点検項目です。
しかし、これらを一つずつ手動でコマンド実行していては、結局「手作業の煩わしさ」からは解放されません。
そこで、これらすべての点検ロジックを1つのファイルに統合し、実行一発で「点検レポート」を生成する完成版スクリプトを作成しました。
特に、多数の仮想マシンを管理するVDIやDaaS、また安定稼働が絶対条件となるWindows環境においては、こうした「一括チェック」の仕組みが、トラブル対応の初動を劇的に早めてくれます。
実行結果を見やすくテキスト/CSVへ出力する
本スクリプトは、実行結果をコンソールに表示するだけでなく、自動的に「日付入りのログファイル」として保存します。
これにより、「昨日の朝はどうだったか?」という過去の健康状態との比較が可能になります。
# ==========================================================
# Windows 正常性一括チェックスクリプト(ポコぺんlaboratory版)
# ==========================================================
# --- 1. 実行環境の準備と管理者権限の確認 ---
# 管理者権限がないとイベントログや一部のサービス情報が取得できないため、チェックを入れます。
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Error "このスクリプトは管理者権限で実行する必要があります。PowerShellを『管理者として実行』してください。"
exit
}
# ログ保存用ディレクトリとファイル名の設定
$logDir = "C:\MaintenanceLogs"
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory | Out-Null }
$logFile = Join-Path $logDir ("HealthCheck_$(Get-Date -Format 'yyyyMMdd_HHmm').txt")
# コンソールとファイルの両方に出力するための関数
function Write-Report {
param([string]$message, [string]$type = "INFO")
$timestamp = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
$formattedMessage = "[$timestamp][$type] $message"
# 画面出力(色分け)
$color = switch($type) { "WARN" {"Yellow"} "ERROR" {"Red"} default {"White"} }
Write-Host $formattedMessage -ForegroundColor $color
# ファイル出力
$formattedMessage | Out-File -FilePath $logFile -Append -Encoding UTF8
}
Write-Report "=== Windows正常性点検レポート 開始 ==="
# --- 2. 【実践1】重要イベントログの抽出 ---
Write-Report "--- [1] イベントログチェック (直近1時間) ---"
$startTime = (Get-Date).AddHours(-1)
$excludeIDs = @(10016, 7031) # 環境に応じて無視してよいIDをここに追加
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System','Application'; StartTime=$startTime; Level=1,2} -ErrorAction SilentlyContinue |
Where-Object { $excludeIDs -notcontains $_.Id }
if ($events) {
$events | ForEach-Object { Write-Report ("ID:$($_.Id) - $($_.Message.Replace([Environment]::NewLine, ' ').Substring(0, [Math]::Min($_.Message.Length, 80)))") "WARN" }
} else {
Write-Report "重大なエラーは見つかりませんでした。"
}
} catch {
Write-Report "イベントログの取得中にエラーが発生しました。" "ERROR"
}
# --- 3. 【実践2】自動起動サービスの稼働確認 ---
Write-Report "--- [2] サービス稼働状況チェック ---"
$serviceWhitelist = @("gupdate", "sppsvc", "MapsBroker") # 止まっていてもよい自動サービス
$targetServices = Get-Service | Where-Object {$_.StartType -eq 'Automatic' -and $_.Status -eq 'Stopped' -and $serviceWhitelist -notcontains $_.Name}
if ($targetServices) {
$targetServices | ForEach-Object { Write-Report ("停止中の重要サービス: $($_.DisplayName) ($($_.Name))") "WARN" }
} else {
Write-Report "すべての自動起動サービスが正常に稼働しています。"
}
# --- 4. 【実践3】リソース消費状況のサマリー ---
Write-Report "--- [3] リソース使用率チェック ---"
# ディスク容量
Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object {
$freePercent = [Math]::Round(($_.FreeSpace / $_.Size) * 100, 1)
$status = if ($freePercent -lt 10) { "WARN" } else { "INFO" }
Write-Report ("ドライブ $($_.DeviceID) 空き容量: $freePercent %") $status
}
# CPU/メモリ
$cpu = [Math]::Round((Get-Counter '\Processor(_Total)\% Processor Time' -SampleInterval 1).CounterSamples.CookedValue, 1)
$os = Get-CimInstance Win32_OperatingSystem
$mem = [Math]::Round((($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize) * 100, 1)
Write-Report ("CPU使用率: $cpu % / メモリ使用率: $mem %") if($cpu -gt 80 -or $mem -gt 90){"WARN"}else{"INFO"}
# --- 5. 【実践4】更新プログラムと再起動状態 ---
Write-Report "--- [4] 保守・更新ステータス ---"
$lastHotFix = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 1
Write-Report ("最終更新適用: $($lastHotFix.InstalledOn.ToShortDateString()) ($($lastHotFix.HotFixID))")
$pendingReboot = (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") -or
(Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending")
Write-Report ("再起動待ち状態: $(if($pendingReboot){'【あり】'}else{'なし'})") if($pendingReboot){"WARN"}else{"INFO"}
Write-Report "=== 正常性点検レポート 完了 ==="
Write-Report "レポートを保存しました: $logFile"
スクリプトを「生きたツール」にするために
このスクリプトを単なる「コピペ用サンプル」で終わらせないための、2つの運用アドバイスを記します。
- タスクスケジューラでの定期実行:
このスクリプトを毎朝、業務開始の15分前に実行するようにタスクスケジューラへ登録してみてください。
出社してログフォルダを開くだけで、その日のサーバーのコンディションがすべて把握できるようになります。 - 独自の「除外ルール」を育てる:
運用するサーバーの役割(Webサーバー、DBサーバー、VDIホストなど)によって、発生しやすいエラーや無視してよいサービスは異なります。
スクリプト内の$excludeIDsや$serviceWhitelistを自分専用にカスタマイズしていくプロセスこそが、真の自動化への近道です。
まとめ:自動化で「トラブルの予兆」を早期発見しよう
WindowsサーバーやPCの保守運用において、最も恐ろしいのは「気づかないうちに致命的なトラブルが進行していること」です。
今回ご紹介したPowerShellスクリプトは、これまで皆さんが手動で行ってきた煩雑な確認作業を、わずか1分足らずの「一括点検」へと昇華させます。
- イベントログ: 膨大なノイズから「真の異常」だけを抽出する
- サービス状況: 「自動起動」の不備を漏らさずチェックする
- リソース消費: 枯渇する前にしきい値で警告を出す
- 更新プログラム: セキュリティの要であるパッチ適用状況と再起動待ちを可視化する
これらの点検を習慣化(自動化)することで、管理者の役割は「トラブルが起きてから対処する」受動的なものから、「異常の予兆を捉えて未然に防ぐ」能動的なものへと変わります。
最初はホワイトリストの調整に少し手間がかかるかもしれませんが、一度自分たちの環境に最適化してしまえば、それは強力な「守護神」になってくれるはずです。
「手動の5分」を「自動の1分」に変え、浮いた時間でより創造的な、技術者としてワクワクするような業務に取り組んでいきましょう!
