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

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

更新履歴(3件・最終更新 2026年08月02日)

この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。

記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
各レシピに結果の読み方(返る列、並び順、何を見て正しさを判断するか)を追加しました。あわせて前提環境の表、Event Log Readersグループへの追加手順、XPathの骨格の表とイベントビューアーからのコピー手順、Levelの表示名が表示言語に依存する旨を追加しています。
初版公開
この記事を引用する(DOI(登録済みアーカイブ): 10.5281/zenodo.21547458)

以下のDOIは過去に登録されたアーカイブを指しており、現在の本文とは一致しない場合があります。現在の本文を参照するときは、このページのURLを使用してください。

小村 豪(2026)「Get-WinEventでイベントログを実務的に調べる ── 絞り込みの速さが調査時間を決める」合同会社小村ソフト. https://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より遅くなることさえあります。取得時に絞れば、数十秒で必要なイベントに辿り着けることがあります。ただし、所要時間はログの量や条件によって変わります。1

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

調査の流れは、ログを探す → 期間・IDなどで絞る → 結果を読んで個別のイベントを追う、の3段階です。コードを順にすべて実行する記事ではありません。まず下の案内から、目的に合う節を選んでください。

目的別の読み方

知りたいこと・困っていること 読むところ
どのログを調べればよいか分からない 2章: ログと発生元を探す
取得が遅い、期間やIDで絞りたい 3章: 取得時のフィルター
結果が空で、何も起きていないのか判断できない 3.5: 該当なしと取得失敗
サーバーが再起動した理由を追いたい 4章(1): 再起動の前後を見る
ログオンを追いたい、Securityログを読めない 4章(2): ログオンの追跡と読み取り権限
アプリやサービスが落ちた 4章(3): アプリ、4章(4): サービス
特定のタスクの実行履歴を調べたい 4章(5): XPathでタスク名を指定
別のPC・複数台・採取済みファイルを調べたい 5章: 取得方法を選ぶ
調べたい時期のログが残っていない 6章: サイズと保持期間

手段を選び直すときは7章の判断表、配布版を使うときはサンプルコードを参照してください。権限の付与やログサイズの変更は、ログを読むだけの操作とは別です。該当する説明を読んでから、必要な操作だけを行います。

前提環境

項目 内容
対象OS Windows 10/11、Windows Server。Get-WinEvent が読むのはWindows Vista以降のイベントログ基盤で、クラシックログとETWのログの両方を扱えます2
PowerShellのバージョン Windows PowerShell 5.1・PowerShell 7のどちらでも使えます(Get-WinEvent はWindows専用のコマンドレットで、Linux/macOSのPowerShell 7では利用できません)。本記事のコードは5.1と7の両方で動く書き方にしています2
権限 System・Applicationなど多くのログは一般ユーザーでも読めますが、Securityログの読み取りには管理者権限か、それに代わる権限の付与が必要です。管理者として実行していないと情報を取得できないログがあります(第4章(2)に権限付与の手順)23
リモート取得 第5章の方法1(-ComputerName)はPowerShellリモート処理(WinRM)に依存しません。代わりに、対象機のファイアウォールでイベントログサービスへのリモートアクセスを許可しておく必要があります。方法2(Invoke-Command)はWinRMが前提です2

1. まず結論

取得: 必要なイベントだけを返してもらう

  • Get-EventLog ではなく Get-WinEvent を使います。前者はWindows PowerShell専用かつクラシックログのみで、PowerShell 7では使えません。2
  • 絞り込みは -FilterHashtable で行います。イベントログ側でフィルターされるため、大量のログでは Where-Object による後段の絞り込みと桁違いの差が出ます。1
  • -FilterHashtable のキーは決まっています。LogName ProviderName ID Level StartTime EndTime Keywords Path UserID などです。1
  • 複雑な条件やイベントデータでの絞り込みはXPath(-FilterXPath)です。イベントビューアーの「カスタムビュー」からXPathをコピーできます。2
  • Level は数値です。1=重大、2=エラー、3=警告、4=情報、5=詳細。4

読み取り: 権限とデータの見方を確認する

  • セキュリティログの読み取りには特別な権限が要ります。管理者として実行するのが手軽ですが、調査担当者には「Event Log Readers」グループやチャネルACLで読み取りだけを与えるほうが最小権限に沿います。23
  • メッセージ文字列ではなくイベントデータを見ます。ToXml() で構造化データを取得すれば、OSの言語設定に依存しないスクリプトになります。2

収集: 単発の調査か、継続的な集約かで選ぶ

  • .evtx ファイルも解析できます。-Path を指定すれば、現地で採取したログを手元で調べられます。2
  • 恒常的な集約はWindowsイベント転送(WEF)です。単発調査なら Invoke-Command による並列実行で十分です。5

図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全20件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle

2. 2種類のログと、コマンドレットの選択

2.1 まず、読めるログの範囲を分ける

Windowsのイベントログには大きく2系統あります。

種類 例 読めるコマンドレット
クラシックログ System / Application / Security Get-EventLog(5.1のみ)・Get-WinEvent
アプリケーションとサービス ログ Microsoft-Windows-TaskScheduler/Operational など Get-WinEvent のみ

後者にこそ、調査に有用な情報が入っています。タスクスケジューラの実行履歴、PowerShellのスクリプトブロックログ、WindowsUpdateの適用履歴など、原因究明の決め手になるログの多くはこちら側です。したがって、これから書くスクリプトは Get-WinEvent に統一するのが正解です。2

2.2 ログ名と発生元を探す

まずは、どんなログがあるかを確認します。ここで使う -ListLog と -ListProvider は、個々のイベントの本文ではなく、ログや発生元の一覧を調べるための指定です。2

# ログの一覧(件数が多い順)。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

最初の一覧は、現在の件数があるログを件数の多い順に20件まで出します。RecordCount は件数、IsEnabled は記録が有効かどうかです。製品名で見つけた LogName や発生元の Name を、次章の取得条件に使います。

ここにも Where-Object がありますが、絞っているのはログの一覧です。次章で避ける「大量のイベント本文を全件取得してから絞る」処理とは分けて考えてください。

3. 絞り込みは「どこで行うか」がすべて

この章では、絞る場所 → 指定できる条件 → 結果の読み方 → エラーの扱いの順に説明します。

3.1 同じ条件でも、絞る場所で待ち時間が変わる

同じ結果を得る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側で絞り込むため、必要なものだけが返ります。1

3.2 ログ名・期間・IDをFilterHashtableへ渡す

-FilterHashtable で使う主なキーは次のとおりです。まず LogName と期間を決め、必要に応じて ID や ProviderName を足します。1

キー 指定するもの 例
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-...'

3.3 重大度は表示名ではなく数値で指定する

Level の数値は次のとおりです。4

値(Level) 日本語環境の表示 英語環境の表示
1 重大 Critical
2 エラー Error
3 警告 Warning
4 情報 Information
5 詳細 Verbose

右2列は、イベントビューアーの「レベル」列と、Get-WinEvent の結果の LevelDisplayName プロパティに出る文字列です。LevelDisplayName はOSの表示言語で変わります。出力を目で突き合わせるときはこの対応表を使い、スクリプトで絞り込むときは必ず数値の Level を指定してください(表示名で判定すると、英語環境で動かないスクリプトになります)。

3.4 件数で当たりを付けてから、個別イベントを追う

実用的な形にすると、こうなります。次の例は直近7日間の件数集計です。イベントを時系列に並べる例ではありません。

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

結果の読み方。このパイプラインの最終出力は Count と Name の2列です。ProviderName, Id のように複数のプロパティでグループ化しているので、Name には 発生元, イベントID がカンマ区切りで並びます(例: Service Control Manager, 7034)。

まず、件数の多い上位15組に見覚えのない発生元がないかを見ます。この一覧には時刻列がなく、時間帯ごとの件数の山は分かりません。当たりを付けた発生元・IDについて期間を狭め、次章のレシピで個別のイベントと障害の時間帯を照合します。

3.5 「該当なし」と「取得失敗」を区別する

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

上のように -ErrorAction Stop で受けて、FullyQualifiedErrorId が NoMatchingEventsFound のときだけ握りつぶすのが正解です。メッセージ文字列で判定すると、日本語環境では一致せず機能しません(エラー処理の考え方は「PowerShellのエラー処理と再実行設計」)。

以降にも -ErrorAction SilentlyContinue を使う短い例がありますが、それらの空の結果だけで「異常なし」と結論づけないでください。取得の失敗を区別する必要がある実務のスクリプトでは、この節の try / catch の形を使います。

4. 現場で頻出する調査レシピ

以下の5つは、調査対象に合わせて選ぶ独立したレシピです。対象ログ・期間・読み取り権限を確認して取得し、返ってきた列と並び順を見て判断する、という順番で使います。サーバー名、アカウント名、タスク名などは自分の環境に読み替えてください。

(1) 予期しない再起動・シャットダウン

取得する: 直近30日のSystemログ

再起動が起きた印だけでなく、誰かが停止を要求した記録も一緒に取得します。

# 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がない場合は、電源断やハングによる異常停止を疑います。

ここで1074から絞れるのはシャットダウンや再起動の要求元です。一覧の Message は1行目だけなので、要求したプロセスやユーザーの詳細は元のイベントの全文でも確認します。

(2) ログオン・ログオフの追跡(セキュリティログ)

取得する前に、読み取り権限を確認する

Securityログには読み取り権限が必要です。権限を用意する担当者は、この節の後半にある読み取り権限の設定を先に確認してください。設定済みなら、以下で直近1日のログオン・ログオフを取得します。

取得する: MessageではなくEventDataから列を作る

# 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 '*$' は、コンピューターアカウントなど、名前が $ で終わるものを除外するための条件です。名前の末尾だけを見ているので、残ったものがすべて人の操作とは断定できません。ログオンイベントにはサービスなどの利用も含まれます。6

そのうえで、ログオン種別 が 3(ネットワーク)や 10(RDP)の行に、見覚えのない接続元IPアドレスがないか、同じユーザーの4625(失敗)が短時間に連続していないかを見ます。この例の後段フィルターは、先にログ名・ID・期間で絞った結果への追加処理です。全イベントを取得してから絞る書き方とは異なります。

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

設定する: 管理者権限ではなく読み取り権限を渡す

調査担当者に「読み取りだけ」を与える。セキュリティログを読ませるために管理者権限を配るのは避けたいところです。ビルトインの Event Log Readers グループ(SIDは S-1-5-32-573)にアカウントを追加すれば、管理者権限なしでイベントログを読めるようになります。3

# 対象のコンピューターで実行する(要管理者権限)。
# ビルトイングループの表示名は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 で確認・変更します。3

wevtutil gl Security                    # 現在の設定を表示する(channelAccess がSDDL)
# wevtutil sl Security /ca:<SDDL>       # 変更する。/ca は既存のSDDLを「置き換える」

/ca は追記ではなく置き換えです。必ず wevtutil gl の出力を控えてから変更してください。

(3) アプリケーションの異常終了

直近14日のApplicationログを取得し、クラッシュ・マネージド例外・障害報告を並べて見ます。出力は新しい順のリスト形式です。

# 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) サービスの停止・再起動

直近30日のSystemログから、発生元を Service Control Manager に絞ります。出力するのは TimeCreated / Id / Message です。停止の記録と、新しいサービスが導入された記録を区別して読みます。

# 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を使うので、先に骨格を押さえておきます。イベントログのXPathで覚えるのは、実質2種類だけです。2

書き方 指すもの 例
*[System[ ... ]] どのイベントにも共通の項目(イベントID、時刻、レベル、プロバイダー) *[System[EventID=41]]
*[EventData[Data[@Name='項目名']='値']] そのイベント固有の項目(名前付きのイベントデータ) *[EventData[Data[@Name='TaskName']='\夜間集計']]

この2つを and / or でつなぐだけです。値の比較にはシングルクォートを使うため、PowerShell側ではヒアストリング(@" 〜 "@)に入れると、引用符の入れ子で悩まずに済みます(下のコードがその形です)。

条件式をイベントビューアーからコピーする

自分で組み立てるより、イベントビューアーに書かせてコピーするほうが速くて確実です。「イベント ビューアー > (対象のログを右クリック) > 現在のログをフィルター」でGUIから条件を指定し、ダイアログの「XML」タブに切り替えると、同じ条件のクエリがXMLで表示されます。その <Select Path="..."> と </Select> に挟まれた部分が、そのまま -FilterXPath に渡せる文字列です。2

ここで取り出すのは、対象ログに対する1つの条件式です。複数の <Select> などを含む構造化クエリ全体を使う場合は、7章の判断表にある -FilterXml と区別してください。2

直近3日とタスク名を、取得時に指定する

次の前半は避けたい書き方との比較です。前半でも期間は3日へ絞っていますが、その期間内のイベントを取得したあと、タスク名を表示用メッセージで判定しています。後半は期間とタスク名の両方をイベントログ側へ渡します。

# 【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

結果と、条件が効いているかを確認する

出力は TimeCreated / Id / LevelDisplayName / Message のリストです。対象タスクの履歴が出ているか、時刻とイベントの内容を照合します。

timediff はミリ秒単位で「現在からの経過時間」を指定するXPath関数で、259200000 は3日です。タスク名は登録時のフルパス(ルート直下なら \タスク名)で指定します。表示用の Message は言語設定に依存し、しかもクライアント側での絞り込みになるため、この記事の方針どおりイベントデータで絞ってください。

このログは既定で無効になっている場合があります。有効化の方法とタスクが動かないときの切り分けは「タスクスケジューラのタスクが実行されない・0x1で終わる」にまとめています。

5. 複数台・別マシンのログを調べる

方法は4つあります。少数のPCへ直接問い合わせる、複数台でコマンドを並列実行する、ファイルを持ち帰る、継続的に転送する、のどれが目的に合うかで選びます。4つを順に実施する手順ではありません。

方法1: リモートで直接読む

-ComputerName はWinRMに依存しません。対象機のイベントログサービスへアクセスできる権限と、ファイアウォールの許可が必要です。次の例は srv01 のSystemログからID 41を、新しいものから最大10件取得します。2

Get-WinEvent -ComputerName 'srv01' -FilterHashtable @{ LogName='System'; ID=41 } -MaxEvents 10

方法2: Invoke-Commandで並列に問い合わせる

台数が多いときはこちらです。こちらはWinRMによるPowerShellリモート処理が前提で、各サーバーの中で絞り込んだ結果を返してもらいます。

$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の並列処理」)。

最後は PSComputerName、TimeCreated の順に昇順で並べています。全サーバーを横断した新しい順の一覧ではなく、サーバーごとに時刻を追う出力です。また、この短い例の SilentlyContinue では取得失敗を見落とし得るので、結果が返らないサーバーは3.5の方針で確認してください。

方法3: evtxを採取して手元で解析する

現地でエクスポートしてもらったファイルを、そのまま読めます。ネットワーク越しに何度も問い合わせるより速く、証跡としても残ります。

下のコメント行が現地での採取操作、実行文が手元での読み取りです。採取側と解析側のパスをそれぞれ確認し、保存済みのファイルを -Path で指定します。

# 現地で: 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. ログのサイズと保持期間

6.1 現在の設定を確認してから、変更の必要性を判断する

「調べようとしたら、その時間帯のログがすでに上書きされていた」は非常によくある失敗です。既定の最大サイズは小さめで、イベントが多い環境では数日で一巡します。

次のコードの前半は確認、最後の wevtutil sl は変更です。続けて一括実行せず、現在の設定を確認してから必要な場合だけ変更してください。最大サイズを変更する例では管理者権限を使います。

# 現在のサイズ設定と保持状況を確認する
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

前半の MaxMB は最大サイズをMBで表した値、RecordCount は件数です。OldestRecordNumber は最古のレコードの番号であって日時ではないため、この一覧だけでは何日前まで残っているかは分かりません。調べたい時間帯のイベントが実際に残っているかと、サイズ設定を分けて確認します。

6.2 再現を待つ前に、残せる量を確保する

調査を前提とするサーバーや、障害が起きている端末では、先にログサイズを広げてから再現を待つのが定石です。サイズの拡張は、これからの記録を残すための準備であり、すでに上書きされたイベントを取り戻す操作ではありません。ログの世代管理と自動アーカイブについては「PowerShellスクリプト応用 ── ログ調査・アーカイブ・レポート化」も参照してください。

7. 実務の定石(判断表)

迷ったら、この表で手段を選び直します。取得できたか、どう読むか、どこへ残すかを別々に判断するのが要点です。

状況 選択 補足
これから書くスクリプト Get-WinEvent Get-EventLog は5.1限定・クラシックログのみ2
ログ名・ID・期間で絞る -FilterHashtable 最も速く、読みやすい1
イベントデータの中身で絞る -FilterXPath / -FilterXml イベントビューアーのカスタムビューからコピーできる2
件数が多く時間がかかる 期間を狭める・-MaxEvents 絞り込みをPowerShell側でやらない
メッセージから値を抜きたい ToXml() のEventData 言語設定に依存しないスクリプトになる2
複数台を単発調査 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

検証環境を選ぶ

試す場所がないという場合、業務端末で本番のログを相手に練習する必要はありません。Windowsサンドボックスなら数分でクリーンなWindowsを起動でき、閉じれば消えます(「Windows Sandboxでアプリ検証を速くする方法」)。ただしサンドボックスは起動直後の環境なのでログの蓄積がほとんどなく、再起動履歴やログオン履歴のように「時間の経過で溜まるログ」を試すなら評価用の仮想マシンのほうが向きます。まずは第3章の取得条件を、利用を許可された検証用端末のログに合わせて試し、絞り込みの速さの違いを体感するところからで十分です。

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

関連記事

関連する相談領域

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

参考リンク

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

  2. 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 ↩16 ↩17 ↩18

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

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

  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など)とログオン種別の値の意味、監査ポリシーの設定によって記録の有無が変わることについて。 ↩ ↩2

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

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

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

よくある質問

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

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

ブログ一覧に戻る