Get-WinEventでイベントログを実務的に調べる ── 絞り込みの速さが調査時間を決める

· · PowerShell, Windows, イベントログ, 障害調査, 運用改善, 情報システム, 監査, トラブルシューティング

更新履歴(初版のみ・2026年07月25日公開)
初版公開
この記事を引用する(DOI: 10.5281/zenodo.21547458)

この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。

小村 豪(2026)「Get-WinEventでイベントログを実務的に調べる ── 絞り込みの速さが調査時間を決める」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21547458

DOI(最新版)
10.5281/zenodo.21547458
DOI(この版)
10.5281/zenodo.21547459

「先週の金曜の夜中に、サーバーが勝手に再起動していたようだ」「特定の端末だけ、業務アプリが月に数回落ちる」── こうした調査の出発点は、ほぼ必ずWindowsのイベントログです。しかしイベントビューアーのGUIで数十万件のログをスクロールしていると、それだけで午前中が終わります。

PowerShellの Get-WinEvent を使えば、この作業は数十秒で終わります。ただし条件があります。絞り込みを正しい場所で行うことです。Get-WinEvent | Where-Object { ... } と書いてしまうと、全イベントを読み込んでから捨てることになり、GUIより遅くなることさえあります。

この記事では、Get-WinEvent の絞り込みを正しく使う方法と、現場で頻出する調査(予期しない再起動、ログオン、アプリの異常終了、サービスの停止)のレシピ、そして複数台からの収集までをまとめます。

1. まず結論

  • Get-EventLog ではなく Get-WinEvent を使います。前者はWindows PowerShell専用かつクラシックログのみで、PowerShell 7では使えません。1
  • 絞り込みは -FilterHashtable で行います。イベントログ側でフィルターされるため、Where-Object による後段の絞り込みより桁違いに速くなります。2
  • -FilterHashtable のキーは決まっています。LogName ProviderName ID Level StartTime EndTime Keywords Path UserID などです。2
  • 複雑な条件やイベントデータでの絞り込みはXPath(-FilterXPath)です。イベントビューアーの「カスタムビュー」からXPathをコピーできます。1
  • Level は数値です。1=重大、2=エラー、3=警告、4=情報、5=詳細。3
  • セキュリティログの読み取りには特別な権限が要ります。管理者として実行するのが手軽ですが、調査担当者には「Event Log Readers」グループやチャネルACLで読み取りだけを与えるほうが最小権限に沿います。14
  • メッセージ文字列ではなくイベントデータを見ます。ToXml() で構造化データを取得すれば、OSの言語設定に依存しないスクリプトになります。1
  • .evtx ファイルも解析できます。-Path を指定すれば、現地で採取したログを手元で調べられます。1
  • 恒常的な集約はWindowsイベント転送(WEF)です。単発調査なら Invoke-Command による並列実行で十分です。5

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側で絞り込むため、必要なものだけが返ります。2

-FilterHashtable で使えるキーは決まっています。2

キー 指定するもの
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 の数値は次のとおりです。3

意味
1 重大(Critical)
2 エラー(Error)
3 警告(Warning)
4 情報(Information)
5 詳細(Verbose)

実用的な形にすると、こうなります。

# 直近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 }
}

該当イベントが1件もない場合、Get-WinEvent はエラーを出します。ここで安易に -ErrorAction SilentlyContinue を付けないでください。「該当なし」も「そのログを読む権限がない」も「対象に到達できない」も、すべて同じ“空の結果”になってしまいます。調査の場面では、これが最も困る壊れ方です。

上のように -ErrorAction Stop で受けて、FullyQualifiedErrorIdNoMatchingEventsFound のときだけ握りつぶすのが正解です。メッセージ文字列で判定すると、日本語環境では一致せず機能しません(エラー処理の考え方は「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

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

監査が有効でないとそもそもイベントが記録されない点にも注意してください。「1件も出ない」は「何も起きていない」ではなく「記録されていない」かもしれません。

(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) タスクスケジューラの実行履歴

# 【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 最も速く、読みやすい2
イベントデータの中身で絞る -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 の採取と手元解析です。
  • 調べたい時間帯のログが残っていなければ何もできません。ログサイズの見直しは、障害対応の準備として最優先です。

サンプルコードのダウンロード

この記事で扱ったコードは、そのまま動かせる形にまとめて配布しています。再起動履歴・ログオン履歴・タスク失敗・複数台収集が入っています。

サンプルコードをダウンロード(zip)

この記事のサンプルは、Windowsやテナントに依存するため実行検証はしていません。構文解析とPSScriptAnalyzerによる静的解析までは全ファイルに対して実施していますが、動作は必ずご自身の検証機で確認してください。

# 構文解析 + 静的解析(Windows以外でも実行できる)
./Invoke-SampleTests.ps1

設定値(パス、サーバー名、テナントIDなど)は例です。そのまま本番環境で実行せず、自社の環境に合わせて読み替えてください。

関連記事

関連する相談領域

合同会社小村ソフトでは、Windows環境の障害調査、間欠的に発生する再起動・アプリ異常終了の原因解析、ログ収集と監視の仕組み作りを扱っています。

参考リンク

  1. 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

  2. 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

  3. Microsoft Learn, Event Levels. イベントの重大度レベルの標準値(1=Critical、2=Error、3=Warning、4=Informational、5=Verbose)について。  2

  4. Microsoft Learn, Active Directory security groups ─ Event Log Readers. ビルトインの「Event Log Readers」グループのメンバーがローカルコンピューターのイベントログを読み取れること(管理者権限の付与を伴わないこと)について。個別チャネルのアクセス許可を変更する方法としてはwevtutilのsl /ca(チャネルアクセス)も参照。 

  5. Microsoft Learn, Windows Event Forwarding. エージェントを追加導入せずに複数のWindowsから収集サーバーへイベントを転送できること、サブスクリプションによる収集対象の指定について。あわせてInvoke-Commandによる複数コンピューターへの並列実行について。  2 3 4

  6. Microsoft Learn, 4624(S): An account was successfully logged on. ログオン成功イベントのイベントデータに含まれる項目(TargetUserName、LogonType、IpAddressなど)とログオン種別の値の意味、監査ポリシーの設定によって記録の有無が変わることについて。 

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

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で解析する方法も有効です。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る