Get-WinEventでイベントログを実務的に調べる ── 絞り込みの速さが調査時間を決める
· 更新日: · 小村 豪 · PowerShell, Windows, イベントログ, 障害調査, 運用改善, 情報システム, 監査, トラブルシューティング
更新履歴(3件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- 各レシピに結果の読み方(返る列、並び順、何を見て正しさを判断するか)を追加しました。あわせて前提環境の表、Event Log Readersグループへの追加手順、XPathの骨格の表とイベントビューアーからのコピー手順、Levelの表示名が表示言語に依存する旨を追加しています。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21547458)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「Get-WinEventでイベントログを実務的に調べる ── 絞り込みの速さが調査時間を決める」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21547458 https://staging.comcomponent.com/blog/powershell-get-winevent-eventlog/
- DOI(最新版)
- 10.5281/zenodo.21547458
- DOI(この版)
- 10.5281/zenodo.21733166
「先週の金曜の夜中に、サーバーが勝手に再起動していたようだ」「特定の端末だけ、業務アプリが月に数回落ちる」── こうした調査の出発点は、ほぼ必ずWindowsのイベントログです。しかしイベントビューアーのGUIで数十万件のログをスクロールしていると、それだけで午前中が終わります。
PowerShellの Get-WinEvent を使えば、この作業は数十秒で終わります。ただし条件があります。絞り込みを正しい場所で行うことです。Get-WinEvent | Where-Object { ... } と書いてしまうと、全イベントを読み込んでから捨てることになり、GUIより遅くなることさえあります。
この記事では、Get-WinEvent の絞り込みを正しく使う方法と、現場で頻出する調査(予期しない再起動、ログオン、アプリの異常終了、サービスの停止)のレシピ、そして複数台からの収集までをまとめます。
前提環境
| 項目 | 内容 |
|---|---|
| 対象OS | Windows 10/11、Windows Server。Get-WinEvent が読むのはWindows Vista以降のイベントログ基盤で、クラシックログとETWのログの両方を扱えます1 |
| PowerShellのバージョン | Windows PowerShell 5.1・PowerShell 7のどちらでも使えます(Get-WinEvent はWindows専用のコマンドレットで、Linux/macOSのPowerShell 7では利用できません)。本記事のコードは5.1と7の両方で動く書き方にしています1 |
| 権限 | System・Applicationなど多くのログは一般ユーザーでも読めますが、Securityログの読み取りには管理者権限か、それに代わる権限の付与が必要です。管理者として実行していないと情報を取得できないログがあります(第4章(2)に権限付与の手順)12 |
| リモート取得 | 第5章の方法1(-ComputerName)はPowerShellリモート処理(WinRM)に依存しません。代わりに、対象機のファイアウォールでイベントログサービスへのリモートアクセスを許可しておく必要があります。方法2(Invoke-Command)はWinRMが前提です1 |
1. まず結論
Get-EventLogではなくGet-WinEventを使います。前者はWindows PowerShell専用かつクラシックログのみで、PowerShell 7では使えません。1- 絞り込みは
-FilterHashtableで行います。イベントログ側でフィルターされるため、Where-Objectによる後段の絞り込みより桁違いに速くなります。3 -FilterHashtableのキーは決まっています。LogNameProviderNameIDLevelStartTimeEndTimeKeywordsPathUserIDなどです。3- 複雑な条件やイベントデータでの絞り込みはXPath(
-FilterXPath)です。イベントビューアーの「カスタムビュー」からXPathをコピーできます。1 Levelは数値です。1=重大、2=エラー、3=警告、4=情報、5=詳細。4- セキュリティログの読み取りには特別な権限が要ります。管理者として実行するのが手軽ですが、調査担当者には「Event Log Readers」グループやチャネルACLで読み取りだけを与えるほうが最小権限に沿います。12
- メッセージ文字列ではなくイベントデータを見ます。
ToXml()で構造化データを取得すれば、OSの言語設定に依存しないスクリプトになります。1 .evtxファイルも解析できます。-Pathを指定すれば、現地で採取したログを手元で調べられます。1- 恒常的な集約はWindowsイベント転送(WEF)です。単発調査なら
Invoke-Commandによる並列実行で十分です。5
この記事の知識マップ
この記事はGet-WinEventによるイベントログ調査を扱い、Windows PowerShell専用でクラシックログしか読めないGet-EventLogに対しGet-WinEventがその後継であると位置づけます。絞り込みはWhere-Objectでパイプ後段に行うと全件を読み込んでから捨てるため遅くなり、FilterHashtableやFilterXPathでイベントログ側に絞り込ませることでこれを防げます。ToXml()のイベントデータを使えば表示言語に依存する値の揺れを避けられ、セキュリティログの読み取りには管理者権限かEvent Log Readersグループへの追加が必要です。複数台の調査は単発ならInvoke-Command、恒常的な集約ならWindowsイベント転送が推奨され、いずれも最終的にはGet-WinEventによる取得を前提とします。
flowchart LR
accTitle: Get-WinEventによるイベントログ調査の知識マップ
accDescr: Get-WinEventがGet-EventLogの後継であること、FilterHashtableとFilterXPathによるサーバー側の絞り込みが後段のWhere-Objectより優れること、ToXml()のイベントデータで言語非依存の値を取り出せること、セキュリティログの読み取り権限、複数台からの収集手段の関係を示す図
get_winevent["Get-WinEvent"]
filter_hashtable["FilterHashtable"]
get_eventlog["Get-EventLog"]
where_object["Where-Object"]
slow_eventlog_query["後段フィルタによる調査の遅延"]
filter_xpath["FilterXPath"]
task_scheduler_operational_log["TaskScheduler/Operationalログ"]
toxml_eventdata["ToXml()のイベントデータ(EventData)"]
locale_dependent_message["表示言語に依存するメッセージ表示"]
security_eventlog["セキュリティログ(Securityログ)"]
admin_rights["管理者権限"]
event_log_readers_group["Event Log Readersグループ"]
wevtutil["wevtutil"]
invoke_command["Invoke-Command"]
multi_server_log_collection["複数台のイベントログ収集"]
windows_event_forwarding["Windowsイベント転送(WEF)"]
eventlog_retention["イベントログの保持期間(ログサイズ)"]
get_winevent -->|"の後継"| get_eventlog
where_object -->|"原因になり得る"| slow_eventlog_query
filter_hashtable -->|"防止する"| slow_eventlog_query
filter_xpath -->|"防止する"| slow_eventlog_query
get_winevent -->|"利用する"| filter_hashtable
get_winevent -->|"利用する"| filter_xpath
filter_xpath -->|"推奨される対応"| task_scheduler_operational_log
get_winevent -->|"利用する"| toxml_eventdata
toxml_eventdata -->|"軽減する"| locale_dependent_message
security_eventlog -.->|"前提とする"| admin_rights
event_log_readers_group -->|"推奨される対応"| security_eventlog
get_winevent -.->|"利用する"| security_eventlog
security_eventlog -->|"で構成できる"| wevtutil
invoke_command -.->|"推奨される対応"| multi_server_log_collection
windows_event_forwarding -->|"推奨される対応"| multi_server_log_collection
multi_server_log_collection -.->|"前提とする"| get_winevent
get_winevent -.->|"利用する"| task_scheduler_operational_log
get_eventlog -->|"用いるのは非推奨"| task_scheduler_operational_log
get_winevent -.->|"前提とする"| eventlog_retention
eventlog_retention -->|"で構成できる"| wevtutil
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全20件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. 2種類のログと、コマンドレットの選択
Windowsのイベントログには大きく2系統あります。
| 種類 | 例 | 読めるコマンドレット |
|---|---|---|
| クラシックログ | System / Application / Security | Get-EventLog(5.1のみ)・Get-WinEvent |
| アプリケーションとサービス ログ | Microsoft-Windows-TaskScheduler/Operational など |
Get-WinEvent のみ |
後者にこそ、調査に有用な情報が入っています。タスクスケジューラの実行履歴、PowerShellのスクリプトブロックログ、WindowsUpdateの適用履歴など、原因究明の決め手になるログの多くはこちら側です。したがって、これから書くスクリプトは Get-WinEvent に統一するのが正解です。1
まずは、どんなログがあるかを確認します。
# ログの一覧(件数が多い順)。RecordCountが0のログは記録されていない
Get-WinEvent -ListLog * -ErrorAction SilentlyContinue |
Where-Object RecordCount -gt 0 |
Sort-Object RecordCount -Descending |
Select-Object LogName, RecordCount, MaximumSizeInBytes, IsEnabled -First 20
# 特定製品のログを探す
Get-WinEvent -ListLog *TaskScheduler* | Format-Table LogName, IsEnabled, RecordCount
Get-WinEvent -ListProvider *PowerShell* | Select-Object Name
3. 絞り込みは「どこで行うか」がすべて
同じ結果を得る3つの書き方を比べます。
# 【最悪】全件を読み込んでからPowerShell側で捨てる
Get-WinEvent -LogName System | Where-Object { $_.Id -eq 41 }
# 【推奨】イベントログ側で絞る(FilterHashtable)
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ID = 41 }
# 【複雑な条件】XPathで絞る
Get-WinEvent -LogName System -FilterXPath "*[System[EventID=41]]"
1つ目は、数十万件のイベントをすべてオブジェクト化してから捨てるため、待ち時間の大半が無駄になります。2つ目・3つ目はイベントログのAPI側で絞り込むため、必要なものだけが返ります。3
-FilterHashtable で使えるキーは決まっています。3
| キー | 指定するもの | 例 |
|---|---|---|
LogName |
ログ名 | 'System', 'Microsoft-Windows-TaskScheduler/Operational' |
ProviderName |
イベントの発生元 | 'Application Error', 'Service Control Manager' |
ID |
イベントID(配列可) | 41, @(1000, 1001) |
Level |
重大度(数値) | 2(エラー), @(1,2) |
StartTime / EndTime |
期間 | (Get-Date).AddDays(-7) |
Keywords |
キーワード(監査の成功/失敗など) | 9007199254740992(監査成功) |
Path |
.evtx ファイル |
'D:\collect\srv01_System.evtx' |
UserID |
ユーザーSID | 'S-1-5-21-...' |
Level の数値は次のとおりです。4
値(Level) |
日本語環境の表示 | 英語環境の表示 |
|---|---|---|
| 1 | 重大 | Critical |
| 2 | エラー | Error |
| 3 | 警告 | Warning |
| 4 | 情報 | Information |
| 5 | 詳細 | Verbose |
右2列は、イベントビューアーの「レベル」列と、Get-WinEvent の結果の LevelDisplayName プロパティに出る文字列です。LevelDisplayName はOSの表示言語で変わります。出力を目で突き合わせるときはこの対応表を使い、スクリプトで絞り込むときは必ず数値の Level を指定してください(表示名で判定すると、英語環境で動かないスクリプトになります)。
実用的な形にすると、こうなります。
# 直近7日間のエラー・重大イベントを、発生元ごとに集計する(状況把握の第一手)
$filter = @{
LogName = 'System', 'Application'
Level = 1, 2
StartTime = (Get-Date).AddDays(-7)
}
try {
Get-WinEvent -FilterHashtable $filter -ErrorAction Stop |
Group-Object ProviderName, Id |
Sort-Object Count -Descending |
Select-Object Count, Name -First 15
}
catch {
# 「該当イベントなし」だけを黙って流す。ロケールに依存しない
# FullyQualifiedErrorId で判定する(メッセージ文字列は日本語環境で一致しない)
if ($_.FullyQualifiedErrorId -notlike 'NoMatchingEventsFound*') { throw }
}
結果の読み方。Group-Object の出力は Count と Name の2列です。ProviderName, Id のように複数のプロパティでグループ化しているので、Name には 発生元, イベントID がカンマ区切りで並びます(例: Service Control Manager, 7034)。件数の多い順に並んでいるので、上位に見覚えのない発生元が来ていないか、障害が起きた時間帯に対応する件数の山があるかを見るのが最初の一手です。ここで当たりを付けてから、次章のレシピで個別のイベントを掘り下げます。
該当イベントが1件もない場合、Get-WinEvent はエラーを出します。ここで安易に -ErrorAction SilentlyContinue を付けないでください。「該当なし」も「そのログを読む権限がない」も「対象に到達できない」も、すべて同じ“空の結果”になってしまいます。調査の場面では、これが最も困る壊れ方です。
上のように -ErrorAction Stop で受けて、FullyQualifiedErrorId が NoMatchingEventsFound のときだけ握りつぶすのが正解です。メッセージ文字列で判定すると、日本語環境では一致せず機能しません(エラー処理の考え方は「PowerShellのエラー処理と再実行設計」)。
4. 現場で頻出する調査レシピ
(1) 予期しない再起動・シャットダウン
# 41: 正常なシャットダウンなしに再起動された(Kernel-Power)
# 6008: 予期しないシャットダウン(EventLog)
# 1074: プロセス/ユーザーによるシャットダウン要求(誰が・何が落としたか)
# 6005/6006: イベントログサービスの開始/停止(= 起動/停止の目印)
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ID = 41, 1074, 6005, 6006, 6008
StartTime = (Get-Date).AddDays(-30)
} | Select-Object TimeCreated, Id, ProviderName,
@{ n = 'Message'; e = { ($_.Message -split "`r?`n")[0] } } |
Sort-Object TimeCreated -Descending | Format-Table -AutoSize
結果の読み方。出力は TimeCreated / Id / ProviderName / Message(長いので1行目だけ)の4列で、新しい順に並びます。見るべきは1行ずつの中身より、1回の再起動あたりに、どのIDがどの順で並んでいるかです。計画的な再起動なら 1074(誰かが要求した)→ 6006(イベントログサービス停止=正常なシャットダウン)→ 6005(開始=起動完了)が組になって現れます。
1074は「誰が再起動を要求したか」が分かる重要なイベントです。ここに WindowsUpdate や特定のプロセス名が出れば、原因がほぼ確定します。41と6008だけが並んでいて1074がない場合は、電源断やハングによる異常停止を疑います。
(2) ログオン・ログオフの追跡(セキュリティログ)
# 4624: ログオン成功 / 4625: ログオン失敗 / 4634: ログオフ
# セキュリティログの読み取り権限が必要(管理者として実行するか、
# 実行アカウントをEvent Log Readersグループに追加しておく)
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624, 4625, 4634
StartTime = (Get-Date).AddDays(-1)
} | ForEach-Object {
$xml = [xml]$_.ToXml()
$d = @{}
foreach ($n in $xml.Event.EventData.Data) { $d[$n.Name] = $n.'#text' }
[pscustomobject]@{
時刻 = $_.TimeCreated
種別 = switch ($_.Id) { 4624 { 'ログオン成功' } 4625 { 'ログオン失敗' } 4634 { 'ログオフ' } }
ユーザー = $d['TargetUserName']
ログオン種別 = $d['LogonType'] # 2=対話 3=ネットワーク 10=RDP
接続元 = $d['IpAddress']
}
} | Where-Object ユーザー -notlike '*$' | Format-Table -AutoSize
ここがイベントデータを使う典型例です。Message を正規表現で切るとOSの言語設定に依存しますが、ToXml() の EventData は名前で参照できるため、日本語環境でも英語環境でも同じスクリプトが動きます。6
結果の読み方。出力は 時刻 / 種別 / ユーザー / ログオン種別 / 接続元 の5列です([pscustomobject] で自分で組み立てているので、列名も並びも自分で決められます)。末尾に Where-Object ユーザー -notlike '*$' を付けてコンピューターアカウント(名前が $ で終わる)を落としているため、残るのは人のアカウントです。ログオン種別 が 3(ネットワーク)や 10(RDP)の行で、接続元 に見覚えのないIPアドレスがないか、同じユーザーの4625(失敗)が短時間に連続していないかを見ます。
監査が有効でないとそもそもイベントが記録されない点にも注意してください。「1件も出ない」は「何も起きていない」ではなく「記録されていない」かもしれません。
調査担当者に「読み取りだけ」を与える。セキュリティログを読ませるために管理者権限を配るのは避けたいところです。ビルトインの Event Log Readers グループ(SIDは S-1-5-32-573)にアカウントを追加すれば、管理者権限なしでイベントログを読めるようになります。2
# 対象のコンピューターで実行する(要管理者権限)。
# ビルトイングループの表示名はOSの言語で変わりうるため、SIDで指定するのが確実
Add-LocalGroupMember -SID 'S-1-5-32-573' -Member 'EXAMPLE\監査担当'
# 追加できたかを確認する
Get-LocalGroupMember -SID 'S-1-5-32-573'
複数台に配るなら、グループポリシーで「コンピューターの構成 > ポリシー > Windows の設定 > セキュリティの設定 > 制限されたグループ」、またはグループポリシーの基本設定「コンピューターの構成 > 基本設定 > コントロール パネルの設定 > ローカル ユーザーとグループ」を使い、ドメインのグループを各サーバーのローカル Event Log Readers に追加する設定を配布します。
チャネル単位でさらに絞りたい場合は、ログごとのアクセス許可(SDDL)を wevtutil で確認・変更します。2
wevtutil gl Security # 現在の設定を表示する(channelAccess がSDDL)
# wevtutil sl Security /ca:<SDDL> # 変更する。/ca は既存のSDDLを「置き換える」
/ca は追記ではなく置き換えです。必ず wevtutil gl の出力を控えてから変更してください。
(3) アプリケーションの異常終了
# 1000: Application Error(アプリのクラッシュ)
# 1026: .NET Runtime(マネージド例外による終了)
# 1001: Windows Error Reporting(障害バケット情報)
Get-WinEvent -FilterHashtable @{
LogName = 'Application'
ID = 1000, 1001, 1026
StartTime = (Get-Date).AddDays(-14)
} | Select-Object TimeCreated, Id, ProviderName, Message |
Sort-Object TimeCreated -Descending | Format-List
1000には落ちたモジュール名とオフセットが、1026には.NETの例外スタックが入ります。ここで当たりを付けてから、ダンプ解析に進むのが効率的です(「Windowsクラッシュダンプ収集入門」「WinDbg + SOSでクラッシュダンプを読む」)。
(4) サービスの停止・再起動
# 7034: サービスが予期せず終了 / 7031: 終了後に回復動作 / 7045: 新規サービスが導入された
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ProviderName = 'Service Control Manager'
ID = 7031, 7034, 7045
StartTime = (Get-Date).AddDays(-30)
} | Select-Object TimeCreated, Id, Message | Format-List
7045(新規サービスの導入)は、意図しないソフトウェアの導入を検知する用途でも有用です。Windowsサービスの運用設計は「Windowsサービスの作り方と運用」を参照してください。
(5) タスクスケジューラの実行履歴
この節ではXPathを使うので、先に骨格を押さえておきます。イベントログのXPathで覚えるのは、実質2種類だけです。1
| 書き方 | 指すもの | 例 |
|---|---|---|
*[System[ ... ]] |
どのイベントにも共通の項目(イベントID、時刻、レベル、プロバイダー) | *[System[EventID=41]] |
*[EventData[Data[@Name='項目名']='値']] |
そのイベント固有の項目(名前付きのイベントデータ) | *[EventData[Data[@Name='TaskName']='\夜間集計']] |
この2つを and / or でつなぐだけです。値の比較にはシングルクォートを使うため、PowerShell側ではヒアストリング(@" 〜 "@)に入れると、引用符の入れ子で悩まずに済みます(下のコードがその形です)。
自分で組み立てるより、イベントビューアーに書かせてコピーするほうが速くて確実です。「イベント ビューアー > (対象のログを右クリック) > 現在のログをフィルター」でGUIから条件を指定し、ダイアログの「XML」タブに切り替えると、同じ条件のクエリがXMLで表示されます。その <Select Path="..."> と </Select> に挟まれた部分が、そのまま -FilterXPath に渡せる文字列です。1
# 【NG】全件を取ってから、表示用メッセージ(言語依存)で絞る
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-TaskScheduler/Operational'
StartTime = (Get-Date).AddDays(-3)
} -ErrorAction SilentlyContinue |
Where-Object { $_.Message -like '*夜間集計*' }
# 【OK】タスク名(イベントデータ)でサーバー側から絞る。速く、言語にも依存しない
$xpath = @"
*[System[TimeCreated[timediff(@SystemTime) <= 259200000]]]
and
*[EventData[Data[@Name='TaskName']='\夜間集計']]
"@
Get-WinEvent -LogName 'Microsoft-Windows-TaskScheduler/Operational' `
-FilterXPath $xpath -ErrorAction SilentlyContinue |
Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-List
timediff はミリ秒単位で「現在からの経過時間」を指定するXPath関数で、259200000 は3日です。タスク名は登録時のフルパス(ルート直下なら \タスク名)で指定します。表示用の Message は言語設定に依存し、しかもクライアント側での絞り込みになるため、この記事の方針どおりイベントデータで絞ってください。
このログは既定で無効になっている場合があります。有効化の方法とタスクが動かないときの切り分けは「タスクスケジューラのタスクが実行されない・0x1で終わる」にまとめています。
5. 複数台・別マシンのログを調べる
方法1: リモートで直接読む
Get-WinEvent -ComputerName 'srv01' -FilterHashtable @{ LogName='System'; ID=41 } -MaxEvents 10
方法2: Invoke-Command で並列に問い合わせる(台数が多いときはこちら)
$servers = 'srv01', 'srv02', 'srv03'
Invoke-Command -ComputerName $servers -ScriptBlock {
Get-WinEvent -FilterHashtable @{
LogName = 'System'; Level = 1, 2; StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
Select-Object TimeCreated, Id, ProviderName, LevelDisplayName
} | Sort-Object PSComputerName, TimeCreated
Invoke-Command は複数コンピューターに対して並列で実行されます(「PowerShell Remoting(WinRM)入門」「PowerShellの並列処理」)。
方法3: .evtx を採取して手元で解析する
現地でエクスポートしてもらったファイルを、そのまま読めます。ネットワーク越しに何度も問い合わせるより速く、証跡としても残ります。
# 現地で: wevtutil epl System D:\collect\srv01_System.evtx
Get-WinEvent -Path 'D:\collect\srv01_System.evtx' -FilterXPath "*[System[(Level=1 or Level=2)]]" |
Select-Object TimeCreated, Id, ProviderName, Message
方法4: Windowsイベント転送(WEF) ── 恒常的に集約するならこれです。収集サーバーに各端末のイベントを転送する標準機能で、エージェントの追加導入が不要です。5
6. ログのサイズと保持期間
「調べようとしたら、その時間帯のログがすでに上書きされていた」は非常によくある失敗です。既定の最大サイズは小さめで、イベントが多い環境では数日で一巡します。
# 現在のサイズ設定と保持状況を確認する
Get-WinEvent -ListLog 'System', 'Application', 'Security' |
Select-Object LogName, IsEnabled, LogMode,
@{ n='MaxMB'; e={ [math]::Round($_.MaximumSizeInBytes / 1MB, 1) } },
RecordCount, OldestRecordNumber
# 最大サイズを変更する(管理者権限。例: Systemログを256MBに)
wevtutil sl System /ms:268435456
調査を前提とするサーバーや、障害が起きている端末では、先にログサイズを広げてから再現を待つのが定石です。ログの世代管理と自動アーカイブについては「PowerShellスクリプト応用 ── ログ調査・アーカイブ・レポート化」も参照してください。
7. 実務の定石(判断表)
| 状況 | 選択 | 補足 |
|---|---|---|
| これから書くスクリプト | Get-WinEvent |
Get-EventLog は5.1限定・クラシックログのみ1 |
| ログ名・ID・期間で絞る | -FilterHashtable |
最も速く、読みやすい3 |
| イベントデータの中身で絞る | -FilterXPath / -FilterXml |
イベントビューアーのカスタムビューからコピーできる1 |
| 件数が多く時間がかかる | 期間を狭める・-MaxEvents |
絞り込みをPowerShell側でやらない |
| メッセージから値を抜きたい | ToXml() のEventData |
言語設定に依存しないスクリプトになる1 |
| 複数台を単発調査 | Invoke-Command |
並列実行される5 |
| 恒常的に集約 | Windowsイベント転送(WEF) | 追加エージェント不要の標準機能5 |
| 過去のログが消えている | ログサイズの拡張 | 再現待ちの前にやっておく |
8. まとめ
Get-WinEventに統一します。Get-EventLogはPowerShell 7で使えず、扱えるログも限られます。- 絞り込みは
-FilterHashtableか-FilterXPathで行います。Where-Objectによる後段の絞り込みは、調査時間を桁で悪化させます。 - 再起動の調査は41・6008に加えて1074(誰が要求したか) を見ます。アプリの異常終了は1000・1026・1001が起点です。
- メッセージ文字列ではなく
ToXml()のイベントデータを使えば、言語設定に依存しないスクリプトになります。 - 複数台の単発調査は
Invoke-Command、恒常的な集約はWindowsイベント転送、証跡が要るなら.evtxの採取と手元解析です。 - 調べたい時間帯のログが残っていなければ何もできません。ログサイズの見直しは、障害対応の準備として最優先です。
サンプルコードのダウンロード
この記事で扱ったコードは、そのまま動かせる形にまとめて配布しています。再起動履歴・ログオン履歴・タスク失敗・複数台収集が入っています。
この記事のサンプルは、Windowsやテナントに依存するため実行検証はしていません。構文解析とPSScriptAnalyzerによる静的解析までは全ファイルに対して実施していますが、動作は必ずご自身の検証機で確認してください。
# 構文解析 + 静的解析(Windows以外でも実行できる)
./Invoke-SampleTests.ps1
試す場所がないという場合、業務端末で本番のログを相手に練習する必要はありません。Windowsサンドボックスなら数分でクリーンなWindowsを起動でき、閉じれば消えます(「Windows Sandboxでアプリ検証を速くする方法」)。ただしサンドボックスは起動直後の環境なのでログの蓄積がほとんどなく、再起動履歴やログオン履歴のように「時間の経過で溜まるログ」を試すなら評価用の仮想マシンのほうが向きます。まずは第3章のコマンドを自分の端末で実行して、絞り込みの速さの違いを体感するところからで十分です。
設定値(パス、サーバー名、テナントIDなど)は例です。そのまま本番環境で実行せず、自社の環境に合わせて読み替えてください。
関連記事
- タスクスケジューラのタスクが実行されない・0x1で終わる ── 原因の切り分けと安全な運用設計
- Windowsのイベントログ・ETWと構造化ログ
- Windowsクラッシュダンプ収集入門 - WER/ProcDump/WinDbg
- PowerShell Remoting(WinRM)入門 ── 複数台のWindowsを一括管理する
- Windowsサービスの作り方と運用
- PowerShellスクリプト応用 ── ログ調査・アーカイブ・レポート化を安全に自動化する
関連する相談領域
合同会社小村ソフトでは、Windows環境の障害調査、間欠的に発生する再起動・アプリ異常終了の原因解析、ログ収集と監視の仕組み作りを扱っています。
参考リンク
-
Microsoft Learn, Get-WinEvent. クラシックログとWindows Vista以降のイベントログの両方を取得できること、-ListLog / -ListProviderによる一覧取得、-FilterHashtable / -FilterXPath / -FilterXmlによる絞り込み、-Pathによるアーカイブ済みログ(.evtx)の読み取り、-ComputerNameによるリモート取得、-MaxEventsによる件数制限、セキュリティログの読み取りに管理者権限が必要なこと、各イベントのToXml()メソッドでXML表現を取得できることについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15
-
Microsoft Learn, Active Directory security groups ─ Event Log Readers. ビルトインの「Event Log Readers」グループのメンバーがローカルコンピューターのイベントログを読み取れること(管理者権限の付与を伴わないこと)について。個別チャネルのアクセス許可を変更する方法としてはwevtutilのsl /ca(チャネルアクセス)も参照。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Creating Get-WinEvent queries with FilterHashtable. -FilterHashtableで指定できるキー(LogName・ProviderName・Path・Keywords・ID・Level・StartTime・EndTime・UserID・Dataなど)、サーバー側でフィルターされるためWhere-Objectによる絞り込みより効率的であること、キーワードや重大度の値の指定方法について。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Event Levels. イベントの重大度レベルの標準値(1=Critical、2=Error、3=Warning、4=Informational、5=Verbose)について。 ↩ ↩2
-
Microsoft Learn, Windows Event Forwarding. エージェントを追加導入せずに複数のWindowsから収集サーバーへイベントを転送できること、サブスクリプションによる収集対象の指定について。あわせてInvoke-Commandによる複数コンピューターへの並列実行について。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, 4624(S): An account was successfully logged on. ログオン成功イベントのイベントデータに含まれる項目(TargetUserName、LogonType、IpAddressなど)とログオン種別の値の意味、監査ポリシーの設定によって記録の有無が変わることについて。 ↩
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
PowerShellのセキュリティ強化 ── ログ・AMSI・言語モード・JEA
PowerShellを禁止せずに安全に使うための実務をまとめます。スクリプトブロックログとトランスクリプションの有効化、AMSIと古いバージョンの無効化、言語モードによる制限、JEAによる権限委任までを解説します。
Windowsセキュリティ監査ポリシーとイベントログ調査の実務 ── 4625を読める情シスになる
「サインイン失敗のログを調べてほしい」に応えるための実務ガイドです。基本と詳細の監査ポリシーの関係、最低限有効化すべきサブカテゴリ、イベントID 4624/4625/4688の読み方、Securityログの容量設計、Get-WinEventでの抽出までを解説します。
winget + PowerShellでPCキッティングを自動化する ── 手順書を実行可能にする
新入社員PCのセットアップを再現可能にする方法をまとめます。wingetによるアプリ導入とexport/import、WinGet Configurationの宣言的な構成、PowerShellで補う設定、無人実行時の注意点までを解説します。
グループポリシー(GPO)実務入門 ── 仕組み・反映確認・Intuneとの使い分け
「GPOで配布」の意味が分からないままAD環境を触っていませんか。グループポリシーの仕組みとLSDOUの適用順序、gpupdate・gpresultでの反映確認、Intuneとの使い分け、客先GPOがアプリの動作を変える落とし穴まで実務目線で解説します。
Windows LAPS実務ガイド ── 全PC共通のローカル管理者パスワードをやめる
全PC共通のローカル管理者パスワードは、1台の侵害が全台に波及するPass-the-Hash攻撃の温床です。OS標準機能になったWindows LAPSによる自動ローテーションと、AD/Entra IDへの保存設定、運用の落とし穴を解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- Get-EventLogとGet-WinEventはどちらを使うべきですか?
- Get-WinEventです。Get-EventLogはWindows PowerShell 5.1でしか使えないうえ、System・Application・Securityといった従来型(クラシック)のログしか扱えません。Windows Vista以降に追加された「アプリケーションとサービス ログ」配下のログ、たとえばMicrosoft-Windows-TaskScheduler/Operationalのような詳細なログは、Get-WinEventでなければ読めません。PowerShell 7ではGet-EventLog自体が利用できないため、これから書くスクリプトはGet-WinEventに統一してください。
- Get-WinEventが遅くて、調査になりません。
- パイプの後ろでWhere-Objectを使って絞り込んでいる可能性が高いです。その書き方では、まずログの全イベントをオブジェクト化してPowerShellに読み込み、その後で不要なものを捨てることになります。数十万件のログでは非現実的です。-FilterHashtableや-FilterXPathを使うと、絞り込みがイベントログ側で行われ、必要なイベントだけが返ってくるため、桁違いに速くなります。まずログ名・期間・イベントIDをFilterHashtableで指定する癖をつけてください。
- セキュリティログを読もうとすると、アクセスが拒否されます。
- セキュリティログの読み取りには既定で特別な権限が必要です。手元で確認するだけならPowerShellを「管理者として実行」すれば済みますが、調査担当者に管理者権限を渡したくない場合は、ビルトインの「Event Log Readers(イベントログリーダー)」グループに追加する、またはチャネルのアクセス許可(ACL)で読み取りだけを許可する方法があります。最小権限の観点では後者を推奨します。なお、そもそも監査ログが記録されていない場合もあります。ログオン監査などは監査ポリシーの設定に依存するため、イベントが1件も見つからないときは、ポリシー自体が有効かどうかも確認してください。
- イベントのメッセージから、特定の値(ユーザー名やプロセス名)だけを取り出したいです。
- Messageプロパティを正規表現で切り出すより、Propertiesまたはイベントデータを使うほうが確実です。各イベントはToXml()メソッドでXML表現を取得でき、そこにはEventData配下に名前付きの項目が入っています。表示用のメッセージ文字列はOSの言語設定によって変わりますが、イベントデータの構造は変わらないため、日本語環境と英語環境の両方で動くスクリプトが書けます。
- 複数台のサーバーのイベントログをまとめて調べたいです。
- 台数が少なければ、Get-WinEventの-ComputerNameパラメーターか、Invoke-Commandによるリモート実行で足ります。Invoke-Commandは指定した複数台に対して並列で実行されるため、数十台程度なら実用的です。恒常的に集約したいなら、Windowsイベント転送(WEF)で収集サーバーに集める仕組みを検討してください。単発の調査であれば、各サーバーでevtxファイルをエクスポートし、手元でGet-WinEvent -Pathで解析する方法も有効です。