Microsoft 365の監査ログCSVから情報持ち出しの疑いをClaude Codeで調べる
Microsoft 365の統合監査ログを最小範囲でCSVに出し、Claude Codeで大量ダウンロード・外部共有・転送ルール作成を時系列にまとめる手順です。
Microsoft 365の統合監査ログは、Microsoft Purviewの監査画面かExchange Online PowerShellのSearch-UnifiedAuditLogでCSVにできます。そのCSVをClaude Codeで集計すれば、退職予定者の大量ダウンロード、外部への共有、メール転送ルールの作成を時系列に並べられます。
ただしCSVには、ユーザー名・IPアドレス・ファイル名が入ります。全件をそのまま読ませるのではなく、対象者と操作を絞って出し、集計はスクリプトに任せる形にします。この記事はその順序を扱います。
調べる前に決めておくこと
最初に決めるのは技術ではなく範囲です。次の3点を紙に書いてから、ログに触れます。
- 誰を調べるか(原則1人。複数なら人ごとに分けてエクスポート)
- いつからいつまでか(退職予定日から逆算した数週間など)
- どの操作を見るか(ダウンロード、共有、転送ルール)
監査ログの検索には権限がいります。Microsoft Purviewポータルで「Audit Logs」または「View-Only Audit Logs」ロールが必要で、PowerShellのcmdletを使うならExchange管理センター側でも同じロールを割り当てます。
管理者の権限範囲にも注意が要ります。管理単位(administrative unit)を割り当てられた制限付き管理者は、その単位に属するユーザーのログしか検索・エクスポートできません。調べたい相手が別の管理単位にいると、検索結果が空になります。
調査の目的と対象者の範囲は、社内の規程や人事・法務の判断に沿って決めてください。この記事は手順の説明であり、調査の可否を判断するものではありません。
最小範囲でCSVを出す
エクスポートの入口は2つあります。コネクタ経由でPurviewにClaudeの利用ログを流す方法はClaudeとMicrosoft Purviewの連携手順にあり、そちらはClaude側の利用を追う入口です。ここで扱うのは、Microsoft 365側の操作ログをCSVで取り出す入口です。
ポータルから出す
Microsoft Purviewポータルで「Audit」を開き、検索画面で次のように絞ります。
| 項目 | 設定の例 |
|---|---|
| Date and time range (UTC) | 設定の例退職予定日から逆算した期間 |
| Users | 設定の例対象者のアカウント1つ |
| Activities - operations names | 設定の例FileDownloaded,FileSyncDownloadedFull,AnonymousLinkCreated,SecureLinkCreated,SharingInvitationCreated,New-InboxRule,Set-InboxRule |
操作名は正確に入力しないと結果が返りません。公式の監査アクティビティ一覧から名前をコピーして貼る運用が確実です。日付と時刻はUTCで表示され、指定できる期間は最大180日です。
検索が終わったら結果画面で「Export」を押します。広い範囲の検索は完了まで時間がかかり、大規模なテナントでは最大48時間かかることがあります。検索ジョブは、ブラウザーを閉じても動き続けます。
エクスポートされたCSVには、CreationDate、UserIds、Operations、AuditDataの4列が入ります。詳細はAuditData列にJSONで入っています。
PowerShellから出す
操作を繰り返し出すならExchange Online PowerShellが向きます。RecordTypeは1回の実行で1つしか指定できないため、ファイル操作・共有操作・転送ルールの3回に分けて実行し、同じCSVに追記します。
$s = "2026-09-01"; $e = "2026-10-15"; $u = "taro@contoso.com"
$f = Search-UnifiedAuditLog -StartDate $s -EndDate $e -UserIds $u `
-RecordType SharePointFileOperation -Operations FileDownloaded,FileSyncDownloadedFull `
-SessionId "case01-file" -SessionCommand ReturnLargeSet
$f | Select-Object CreationDate,UserIds,RecordType,AuditData |
Export-Csv .\case01.csv -NoTypeInformation
$h = Search-UnifiedAuditLog -StartDate $s -EndDate $e -UserIds $u `
-RecordType SharePointSharingOperation `
-SessionId "case01-share" -SessionCommand ReturnLargeSet
$h | Select-Object CreationDate,UserIds,RecordType,AuditData |
Export-Csv .\case01.csv -NoTypeInformation -Append
$r = Search-UnifiedAuditLog -StartDate $s -EndDate $e -UserIds $u `
-Operations New-InboxRule,Set-InboxRule `
-SessionId "case01-rule" -SessionCommand ReturnLargeSet
$r | Select-Object CreationDate,UserIds,RecordType,AuditData |
Export-Csv .\case01.csv -NoTypeInformation -AppendUserIdsと日付は例です。StartDateとEndDateにタイムゾーンを付けない値はUTCとして扱われます。同じ日付を両方に指定するときは時刻も付けないと、結果が返りません。
転送ルールは3本目の検索で取ります。RecordTypeを付けず、OperationsにNew-InboxRuleとSet-InboxRuleを渡しています。Operationsに使える値はRecordTypeに依存するため、PowerShellで空振りしたときはポータルの「Activities - operations names」に同じ名前を入れて試してください。
件数の上限と取りこぼし
CSVに出ない行があると、「ダウンロードが少ない」という誤った結論につながります。上限は先に確認します。
- ポータルのエクスポートは、Audit (Standard)で最大50,000行、Audit (Premium)で最大100万行です。超えた分はCSVに含まれません。
Search-UnifiedAuditLogは既定で100件しか返しません。-SessionCommand ReturnLargeSetを付けると最大50,000件まで取れますが、並び順は保証されません。- 日付順に並べたいときの
ReturnNextPreviewPageは、最大5,000件です。同じSessionIdで2つの値を切り替えると、10,000件に制限されます。
上限に当たったら、期間を分けて複数回検索します。この記事の集計スクリプトはCreationTimeで並べ直すので、ReturnLargeSetの順不同でも問題ありません。
保持期間も確認します。Audit (Standard)の記録は180日、E5ライセンスのユーザーはExchange・SharePoint・Microsoft Entra IDの記録が既定で1年残ります。180日より前の操作は、そもそも検索できません。
また、操作から記録が検索に現れるまでには時間差があります。Exchange・SharePoint・OneDrive・Teamsなどの主要サービスで、おおむね60〜90分です。退職日当日の操作を調べるなら、翌日以降に出すのが安全です。
CSVを丸ごと読ませない
ここからがClaude Codeの出番ですが、CSVの中身をそのままチャットへ貼る必要はありません。AuditData列には、ユーザー、IPアドレス、ファイルのURLが入っているからです。
進め方は次のとおりです。
- 作業フォルダーを作り、対象者1人分のCSVだけを置く
- 先頭の数行を、ユーザー名やURLを伏せた状態でClaude Codeに見せる
- 列の構造だけを根拠に、集計スクリプトを書かせる
- スクリプトはローカルで実行し、出力された集計表だけを次の分析に使う
CSVの構造は公式に説明があります。AuditData列はJSONで、Operation(操作名)、CreationTime(UTCの時刻)、UserId、ClientIP、ObjectId(SharePointならファイルのURL)、SourceFileNameといったプロパティを持ちます。Claude Codeには、この構造を伝えてスクリプトを書かせます。
添付のサンプル3行はユーザー名とURLを伏せてあります。
CSVのAuditData列はJSONで、Operation / CreationTime / ClientIP /
ObjectId / SourceFileName を持ちます。
次の集計をするPython(標準ライブラリのみ)を書いてください。
- 対象のOperationだけを抽出
- 時刻はUTCからJSTに変換
- 日別・操作別の件数
- 1時間あたりのFileDownloaded件数が閾値以上の時間帯
- New-InboxRule / Set-InboxRuleの発生時刻
- 出力にユーザー名は含めない「ユーザー名を出力に含めない」と指定しておくと、集計結果を共有しても対象者が特定されにくくなります。ファイルのURLは出力に残るため、共有範囲は別に決めてください。
時系列を作る集計スクリプト
次のスクリプトは、上のプロンプトで生成させる想定の例です。Claude Codeが書くコードは毎回少しずつ違うので、そのまま実行するのではなく、中身を読んでから走らせてください。
import csv, json, sys
from collections import Counter
from datetime import datetime, timedelta, timezone
TARGET = {"FileDownloaded", "FileSyncDownloadedFull",
"AnonymousLinkCreated", "SecureLinkCreated",
"SharingInvitationCreated", "New-InboxRule", "Set-InboxRule"}
THRESHOLD = 50 # 1時間あたりのダウンロード件数。例なので実態に合わせる
JST = timezone(timedelta(hours=9))
csv.field_size_limit(sys.maxsize)
events = []
with open(sys.argv[1], newline="", encoding="utf-8-sig") as f:
for row in csv.DictReader(f):
try:
d = json.loads(row["AuditData"])
op = d["Operation"]
t = datetime.fromisoformat(d["CreationTime"])
except (KeyError, ValueError):
continue
if op not in TARGET:
continue
t = t.replace(tzinfo=timezone.utc).astimezone(JST)
obj = d.get("ObjectId") or d.get("SourceFileName") or ""
events.append((t, op, obj[:80]))
events.sort()
print(f"対象イベント: {len(events)} 件")
by_day = Counter((t.date(), op) for t, op, _ in events)
for (day, op), n in sorted(by_day.items()):
print(f"{day} {op}: {n}")
per_hour = Counter(t.strftime("%Y-%m-%d %H時") for t, op, _ in events
if op in ("FileDownloaded", "FileSyncDownloadedFull"))
print("--- ダウンロードが閾値以上の時間帯 ---")
for hour, n in sorted(per_hour.items()):
if n >= THRESHOLD:
print(hour, n)
print("--- 共有・転送ルール(時系列) ---")
for t, op, obj in events:
if op not in ("FileDownloaded", "FileSyncDownloadedFull"):
print(t.strftime("%Y-%m-%d %H:%M"), op, obj)python3 summarize.py case01.csv > summary.txt
wc -l summary.txt出力はsummary.txtに落とします。対象イベントの総件数、日別・操作別の件数、閾値を超えた時間帯、共有と転送ルールの時系列が並びます。件数は公式の画面の件数と照合してください。ポータルの詳細画面の件数は重複を除くため、CSVの行数と食い違うことがあります。
FileSyncDownloadedFullは、OneDriveの同期アプリでライブラリのファイルをPCに落とした操作です。ブラウザーからのダウンロードはFileDownloadedなので、両方を集計に入れています。
AnonymousLinkCreatedは認証なしで誰でも開けるリンク、SecureLinkCreatedは安全な共有リンクの作成、SharingInvitationCreatedは組織のディレクトリにいない相手への共有です。外部への持ち出し経路としては、この3つを分けて見ます。
集計結果をClaudeに時系列へまとめさせる
summary.txtは個人名を含まず、件数と時刻が中心です。これならClaude Codeに読ませて、時系列の文章にまとめさせられます。
summary.txt は対象者Aの2026-09-01〜10-15の監査ログ集計です。
- 事実として言えることだけを時系列の箇条書きにしてください
- 「持ち出した」「不正」などの断定は書かないでください
- 通常業務でも起こり得る説明を、各項目に1つ添えてください
- ログだけでは分からないこと(転送先、ファイルの内容)を最後に列挙してください最後の2点が重要です。ダウンロードが多い日があっても、引き継ぎや報告書作成といった業務でも起こります。転送ルールは作成の事実が分かるだけで、ログの要約だけでは転送先や意図までは断定できません。
まとめを出させたら、人がsummary.txtと1行ずつ照合します。Claude Codeの文章が集計にない件数や時刻を書いていないかを見るためです。このステップは省かない前提で運用します。
ログから言えること・言えないこと
監査ログは「その操作が記録された」ことしか示しません。次の限界を、結論に書く前に確認します。
| 見たいこと | ログから分かるか |
|---|---|
| 大量のダウンロードがあったか | ログから分かるか分かる(件数・時刻・ファイルのURL) |
| 外部ユーザーに共有したか | ログから分かるか共有招待・リンク作成の事実は分かる |
| 持ち出したファイルの中身 | ログから分かるか分からない |
| 私物端末のUSBや画面撮影 | ログから分かるか監査ログの対象外 |
| 転送ルールの作成 | ログから分かるか作成の事実は分かる。転送先などの設定値はAuditDataを人が読んで確かめる |
ClientIPにも注意が必要です。サービスによってはOffice on the webなどの信頼されたアプリのIPが入り、操作した端末のIPではない場合があります。Microsoft Entra IDの管理者操作やシステムアカウントの操作では、IPが記録されません。
さらに、メールボックスの監査イベントが一部のユーザーで検索結果に出ない場合もあります。「ログに無い」ことは「操作が無い」ことの証明になりません。
つまずきやすい点
CSVを開くと列が少ない
ポータルのCSVは4列で、詳細はAuditDataのJSONに入ります。Excelで見るなら、Power Queryでこの列をJSONとして展開します。ただしPower Queryは先頭1,000行から列を決めるため、後ろの行にだけあるプロパティが落ちることがあります。スクリプトで読めば、この問題は起きません。
検索結果が空になる
操作名の綴りの誤り、管理単位の権限不足、保持期間外の日付、検索の開始日と終了日の取り違えが典型です。統合監査ログの取り込みが有効かは、Exchange Online PowerShellでGet-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabledを実行して確かめます。Security & Compliance PowerShellで実行すると、有効でもFalseと表示される点に注意してください。
ライセンス管理など別の用途にも使いたい
ユーザーごとのライセンスと最終サインインをCSVに出して洗い出す手順は、Microsoft 365の未使用ライセンスをClaude Codeで洗い出す手順にあります。
結果の扱い
集計結果は、疑いを示す材料にとどまります。人事・法務への報告では、ログの事実、集計の方法、確認できていないことを分けて書くと、後から再現できます。
調査に使ったCSVとスクリプトは、案件が終わったら保管期間を決めて削除します。エクスポートしたCSVにはユーザー名とファイルのURLが入っているため、共有フォルダーに置きっぱなしにしないでください。
Claude側の監査ログ(組織設定の変更履歴)を確認したいときは、Claude監査ログの見方が入口になります。退職者対応の全体像はCoworkの退職者オフボーディングで扱っています。
まとめ
監査ログのCSVは、範囲を絞って出し、集計はスクリプト、要約はClaude Codeという分担にすると、個人データの露出を抑えながら時系列を作れます。結論は「ログの事実」にとどめ、意図の判断は人が行います。