Claude Media
Microsoft 365の監査ログCSVから情報持ち出しの疑いをClaude Codeで調べる

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 -Append

UserIdsと日付は例です。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. 作業フォルダーを作り、対象者1人分のCSVだけを置く
  2. 先頭の数行を、ユーザー名やURLを伏せた状態でClaude Codeに見せる
  3. 列の構造だけを根拠に、集計スクリプトを書かせる
  4. スクリプトはローカルで実行し、出力された集計表だけを次の分析に使う

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という分担にすると、個人データの露出を抑えながら時系列を作れます。結論は「ログの事実」にとどめ、意図の判断は人が行います。

この記事を共有:XはてブLinkedIn