Get-SecurityAlertでMDEアラートだけ取得する方法|Get-MgSecurityAlertV2とserviceSourceで取りこぼし防止

Get-SecurityAlertでMicrosoft Defender for Endpoint(MDE)のアラートだけを取得したいのに、なぜか「リスクの高いサインイン」が混ざったり、件数が欠けたりすることがあります。多くの場合、原因は“旧アラートAPIの仕様”とページング(次ページ取得)の見落としです。アプリ登録を増やさず、委任されたアクセス許可(ユーザー権限)だけでMDEアラートを漏れなく取る手順をまとめます。

目次

まず結論:MDEアラートだけを「漏れなく」「混ぜずに」取るなら Alerts v2 を使う

同じ「アラート取得」でも、Microsoft Graphには大きく2系統があります。ここを混同すると、症状(取りこぼし/別プロバイダー混入)がそのまま発生します。

観点旧:/security/alerts(Legacy)新:/security/alerts_v2(Alerts v2)
PowerShellコマンド例Get-SecurityAlert / Get-MgSecurityAlertGet-MgSecurityAlertV2
主な用途複数プロバイダーの「第一世代」アラート集約Microsoft 365 Defender基盤の「最新世代」アラート
混在しやすさ混在しやすい(MDE以外も返る)serviceSource で絞り込みやすい
代表的な絞り込み軸vendorInformation/provider(ただし名称がレガシー)serviceSource(例:microsoftDefenderForEndpoint)
権限(委任)SecurityEvents.Read.AllSecurityAlert.Read.All
注意点廃止予定(期限が明記されている)/$top が“プロバイダーごとの集約”になりやすいページングは nextLink 前提だが、PowerShellの -All で追える

要件が「MDEだけ」「取りこぼしゼロ」なら、最優先で Get-MgSecurityAlertV2(alerts_v2) に寄せるのが安全です。旧API側はプロバイダー統合の都合で“混ざる”仕様が前提になりやすく、さらにレガシー名が残っていてフィルターの文字列も分かりにくくなっています。

なぜ Get-SecurityAlert で「リスクの高いサインイン」が混ざるのか

Get-SecurityAlert(または Get-MgSecurityAlert)が叩くことが多い /security/alerts(Legacy) は、Microsoft GraphのセキュリティAPIが複数プロバイダーのアラートをフェデレーション(横断集約)して返す設計です。つまり、MDEだけに限らず、Microsoft Entra ID Protection(旧Azure AD Identity Protection)など別系統のアラートも同じコレクションに入ってきます。

そのため、MDEを狙って実行しても、条件が甘い/ページングが不完全/provider名が想定と違う、といった要素が重なると、

  • MDEアラートが取りこぼされる
  • 「リスクの高いサインイン」のような別プロバイダーのアラートが混ざる

という状態になりがちです。

さらに重要なのが、Legacy alerts は 廃止予定(削除期限が明記)である点です。長期運用のスクリプトほど、早めに v2 へ寄せておく方が事故を減らせます。

アプリ登録を増やさずに実現する:Microsoft Graph PowerShellの「既定アプリ」で委任権限を使う

「アプリ登録を極力行わない」という方針の場合でも、Microsoft Graph PowerShell を使えば、Microsoftが提供する既定のマルチテナントアプリを利用して、ユーザーとしてサインイン(委任)できます。

準備:モジュールと接続

まずは必要モジュールを読み込み、委任権限(スコープ)を指定して接続します。MDEアラート取得を v2 で行うなら SecurityAlert.Read.All が基本です。

# 初回のみ(未インストールなら)
Install-Module Microsoft.Graph -Scope CurrentUser -Force

# セキュリティ系コマンドを使うため
Import-Module Microsoft.Graph.Security

# アプリ登録なし(既定アプリ)で、委任されたアクセス許可を要求してサインイン
# 管理者同意が必要なテナントでは、管理者に同意してもらう必要があります
Connect-MgGraph -Scopes "SecurityAlert.Read.All"

「ブラウザー認証がうまくいかない」「過去のトークンが影響する」場合は、PowerShellを新規セッションで開く/シークレット(InPrivate)を使う/別プロファイルにする、などで切り分けすると安定します。

委任権限でMDEを読むときの落とし穴:MDE側のロールが別途必要

Microsoft Defender for Endpoint のデータは、Graphの権限だけでは足りず、MDE側の閲覧ロールが必要になるケースがあります。GraphのセキュリティAPI概要にも「MDEは追加のユーザーロールが必要」「アプリのみ(アプリケーション権限)を推奨する」旨の注意が記載されています。

会社方針でアプリ登録を避ける場合でも、少なくとも以下は確認してください。

  • Microsoft Graph 側:SecurityAlert.Read.All(委任)が付与・同意されている
  • MDE 側:アラート閲覧が可能なロール(例:セキュリティ閲覧者/セキュリティ管理者/該当RBACグループ)が付いている

推奨:Get-MgSecurityAlertV2 で「MDE由来だけ」を取得する

Alerts v2 の最大のメリットは、serviceSource でアラートの出どころ(サービス)を絞り込みやすい点です。MDEのアラートだけに限定するなら、まずはこれが最短ルートです。

MDEアラートだけを全件取得(ページング込み)

# MDE のアラートだけを全件取得
Get-MgSecurityAlertV2 -All `
  -Filter "serviceSource eq 'microsoftDefenderForEndpoint'"

-All を付けると、内部的に @odata.nextLink を追って全ページを取得します。「一部しか取れない」症状の多くは、この -All 抜けで説明できます。

まずは“値の確認”から始めると失敗しない

環境によっては、プロパティの値や利用可能なcmdletが想定と違うことがあります。最初に少数件だけ取って、serviceSource が想定どおりか確認すると安全です。

# まずは10件だけ見て、serviceSource が何になっているか確認
Get-MgSecurityAlertV2 -Top 10 |
  Select-Object Id, CreatedDateTime, Severity, Status, ServiceSource, Title

日付で絞り込んで“取りこぼし”と“負荷”を同時に減らす

アラートが多いテナントで毎回全件取得すると、時間もAPI負荷も大きくなります。運用では、createdDateTime を使って期間を区切るのが定番です。

# 例:2025-08-01以降に作成されたMDEアラートだけ取得(UTCのZ)
Get-MgSecurityAlertV2 -All `
  -Filter "serviceSource eq 'microsoftDefenderForEndpoint' and createdDateTime ge 2025-08-01T00:00:00Z"

createdDateTime は UTC で扱うのが基本です。日本時間で「今日0:00以降」を取りたいなら、事前にUTCへ変換してからフィルター文字列を組み立てるとズレが起きません。

よく使うフィルター例(v2)

目的Filter例ポイント
MDEだけserviceSource eq 'microsoftDefenderForEndpoint'混入を最短で防ぐ
新規(未対応)だけserviceSource eq 'microsoftDefenderForEndpoint' and status eq 'new'アラートキュー運用に近い形
高・重大だけserviceSource eq 'microsoftDefenderForEndpoint' and severity in ('high','critical')環境により critical の扱いに差が出る場合があるため、まず実データ確認推奨
最終更新が最近serviceSource eq 'microsoftDefenderForEndpoint' and lastUpdateDateTime ge 2025-12-01T00:00:00Z“更新が入ったもの”の追跡に便利

Alerts v2 の $filter で利用できるプロパティには、serviceSource や createdDateTime などが含まれます。フィルター条件を増やすときは、まず“サポートされているプロパティ”を前提に設計してください。

Get-MgSecurityAlertV2 が使えない場合:モジュール更新と、どうしても無理なら Legacy での回避策

まずは「コマンドが存在するか」を確認する

環境によっては Microsoft.Graph.Security の更新が遅れていて、Get-MgSecurityAlertV2 が見当たらないことがあります。最初にコマンド有無を確認し、なければモジュール更新を試します。

# v2コマンドの有無を確認
Get-Command Get-MgSecurityAlertV2 -ErrorAction SilentlyContinue

# 更新(必要に応じて)
Update-Module Microsoft.Graph -Force
Update-Module Microsoft.Graph.Security -Force

更新後は PowerShell を開き直してから再確認すると、読み込み中の古いDLLの影響を避けられます。

どうしても Legacy を使うなら:vendorInformation/provider の“レガシー名”で絞る

/security/alerts(Legacy)を使う場合は、まず前提として SecurityEvents.Read.All が必要になります。

Import-Module Microsoft.Graph.Security
Connect-MgGraph -Scopes "SecurityEvents.Read.All"

ここでハマりやすいのが、Legacy は製品名がリブランディングされても、フィルターで使うキーワードが古いまま残っている点です。Microsoft Learn の例でも、MDEは “Microsoft Defender for Endpoint” ではなく、フィルターキーワードが “Microsoft Defender ATP” として扱われます。

ベンダー(表示上の製品)Legacy側の $filter キーワード例補足
Microsoft Defender for Endpoint(MDE)Microsoft Defender ATP名称が古いまま残っていることがある
Microsoft Entra ID Protection(リスク サインイン等)IPC“Risky sign-in” 混入の原因になりやすい
Microsoft Defender for Cloud AppsMCASCloud App Securityのレガシー名

実運用では、まず少量取得して provider 名の実値を確認し、その文字列でフィルターするのが確実です。

# まずは少数取得して provider 名を確認(Legacy)
$sample = Get-MgSecurityAlert -Top 20
$sample | Select-Object -First 10 `
  -Property Id, Title, Severity, Category, CreatedDateTime, EventDateTime, `
  @{Name="Provider";Expression={$_.VendorInformation.Provider}}, `
  @{Name="Vendor";Expression={$_.VendorInformation.Vendor}}

provider の実値が確認できたら、その値で MDE のみへ絞ります。MDE相当が “Microsoft Defender ATP” になっているケースが多い想定です。

# 例:MDE相当(レガシー名)だけ取得
Get-MgSecurityAlert -All `
  -Filter "vendorInformation/provider eq 'Microsoft Defender ATP'"

それでも「Risky sign-in を明示的に除外したい」場合は、カテゴリや provider で否定条件を追加します。

# 例:MDE相当だけ&Risky sign-inカテゴリを除外(必要なら)
Get-MgSecurityAlert -All `
  -Filter "vendorInformation/provider eq 'Microsoft Defender ATP' and category ne 'Risky sign-in'"

ただし、Legacy 側はプロバイダーごとの実装差もあり、フィルターが思った通りに効かない事例が報告されることがあります。だからこそ、可能なら v2 へ寄せるのが無難です。

「全部取れていない」の原因はページングだけじゃない:現場で効くチェックリスト

-All を付けたか(次ページを追っているか)

Graph の一覧取得は、基本的に 1回の応答で全件が返りません。PowerShellなら、v1/v2 どちらも -All が用意されているので、まずはここを疑います。

$top の仕様で“思ったより少なく見える”ことがある(Legacy)

Legacy の /security/alerts は、$top が「プロバイダーごとの集約トップ」として働く説明があり、さらに $top の上限や $skip の制限もあります。件数が多いテナントで “少ない” と感じるときは、この仕様に当たっていないか確認してください。

Retention(保持期間)に引っかかっていないか(v2)

Alerts v2 は、環境の保持ポリシーで指定された期間内のアラートが対象になります。過去分を掘りたい場合、単にAPIの取りこぼしではなく、保持期間により “そもそも返らない” 可能性があります。

権限は合っているか(SecurityEvents と SecurityAlert を取り違えていないか)

Legacy と v2 では、必要な委任権限が異なります。

  • Legacy(Get-MgSecurityAlert):SecurityEvents.Read.All
  • v2(Get-MgSecurityAlertV2):SecurityAlert.Read.All

「接続できたのに結果が薄い」「空になる」場合、まずスコープの取り違えを疑うと復旧が早いです。

レスポンスヘッダーの警告で“部分応答”になっていないか

Legacy の一覧取得では、プロバイダーがタイムアウトしたりエラーを返すと、応答が部分的になるケースがあります。Graph PowerShellではレスポンスヘッダーを変数に受け取れるので、警告が出ていないか確認しておくと「取りこぼしの正体」が分かりやすくなります。

$rh = $null
$alerts = Get-MgSecurityAlert -All `
  -Filter "vendorInformation/provider eq 'Microsoft Defender ATP'" `
  -ResponseHeadersVariable rh

# 取得できた件数確認
$alerts.Count

# ヘッダー(警告など)を確認
$rh

実務向け:MDEアラートを“差分”で取り続ける最小スクリプト例

全件取得を毎回やるより、「前回実行以降に作成・更新されたものだけ取る」ほうが現実的です。ここでは v2 を前提に、createdDateTime で差分取得する最小例を載せます。

Import-Module Microsoft.Graph.Security
Connect-MgGraph -Scopes "SecurityAlert.Read.All"

# 前回取得時刻(UTC)をファイルで保持する例
$statePath = "$PSScriptRoot\lastRunUtc.txt"
if (Test-Path $statePath) {
  $sinceUtc = Get-Content $statePath | Select-Object -First 1
} else {
  # 初回は直近24hなどにしてもOK
  $sinceUtc = (Get-Date).ToUniversalTime().AddDays(-1).ToString("yyyy-MM-ddTHH:mm:ssZ")
}

$filter = "serviceSource eq 'microsoftDefenderForEndpoint' and createdDateTime ge $sinceUtc"

$alerts = Get-MgSecurityAlertV2 -All -PageSize 500 -Filter $filter |
  Select-Object Id, CreatedDateTime, Severity, Status, Category, Title, DetectionSource, ServiceSource, AlertWebUrl

# 出力
$outDir = "$PSScriptRoot\out"
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
$timestamp = (Get-Date).ToString("yyyyMMdd_HHmmss")
$alerts | Export-Csv -Path "$outDir\mde_alerts_$timestamp.csv" -NoTypeInformation -Encoding UTF8

# 次回用に「今回実行時刻(UTC)」を保存
(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") | Set-Content $statePath

この形にしておくと、アラート件数が増えた環境でも「必要な分だけ」取り続けられます。さらに lastUpdateDateTime を使って“更新差分”に寄せると、担当者割当やステータス変更の追跡にも応用できます。

まとめ:MDEだけを取りたいなら「V2 + serviceSource + -All」が最短で堅い

  • Get-SecurityAlert / Get-MgSecurityAlert(Legacy)は複数プロバイダーの集約で、MDE以外(例:リスクの高いサインイン)が混ざりやすい
  • MDEアラートだけに絞るなら、まず Get-MgSecurityAlertV2 を使い、serviceSource eq ‘microsoftDefenderForEndpoint’ でフィルターする
  • 取りこぼし対策は -All が基本。件数が多いなら createdDateTime / lastUpdateDateTime で差分取得にする
  • 委任権限のみで実現する場合でも、Graph権限に加えて MDE側ロール が必要な点は見落としやすい
  • Legacy は廃止予定のため、長期運用するなら v2 へ寄せておくのが安全

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次