からめもぶろぐ。

俺たちは雰囲気で OAuth をやっている

Office 365 管理 API を使って SharePoint Online の監査ログを取得する

いままでずっと SharePoint Online の監査ログの生データは取得できないと思っていたのですが、実は Office 365 管理 API というものを使えば取得できるのだそうです。Office 365 管理 API は SharePoint、Exchange、Teams、Power Platform などのさまざまな監査ログを取得できるのですが、特に利用状況の監視目的で SharePoint Online の監査ログを取りたいという要望は非常に多いため、この方法を使えば解決できるのはないかと思います。

なお今回は以下の記事を参考にしています。

qiita.com

サンプル コード

github.com

実行手順

Office 365 管理 API でログを取得するには以下の手順が必要になります。

  • アクセス トークンを取得する
  • サブスクリプションを作成する
  • コンテンツの URL を取得する
  • コンテンツを取得する

アクセス トークンを取得する

Office 365 管理 API を使用するためには、はじめに Azure Active Directory にアプリを登録する必要があります。[API のアクセス許可] では [Office 365 Management APIs] - [アプリケーションのアクセス許可] - [ActivityFeed.Read] を追加します。

f:id:karamem0:20200902135316p:plain

またアプリケーション シークレットも取得しておきます。

Office 365 管理 API は Azure Active Directory の v2.0 エンドポイントに対応していないので MSAL を使うことができません。ADAL を使ってもいいのですが、面倒なので、今回は直接 HttpClient で取りに行くようにします。といっても Client Credentials Grant なのでそれほど難しくはありません。

private static void AcquireToken()
{
    var httpRequestUrl = $"https://login.microsoftonline.com/{TenantId}/oauth2/token";
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, httpRequestUrl);
    var httpRequestContent = new FormUrlEncodedContent(new Dictionary<string, string>()
    {
        { "grant_type", "client_credentials" },
        { "resource", Resource },
        { "client_id", ClientId },
        { "client_secret", ClientSecret }
    });
    httpRequestMessage.Content = httpRequestContent;
    var httpResponseMessage = HttpClient.SendAsync(httpRequestMessage).GetAwaiter().GetResult();
    var httpResponseContent = httpResponseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
    var httpResponseJson = JsonConvert.DeserializeObject<JToken>(httpResponseContent);
    AccessToken = httpResponseJson.Value<string>("access_token");
}

サブスクリプションを作成する

Office 365 管理 API のコンテンツ タイプ (Audit.SharePoint) に対してサブスクリプションを作成します。Webhook も登録できるようなのですが、今回は省略します。PublisherIdentifier は任意の GUID を指定してください。

private static void CreateSubscription()
{
    var httpRequestUrl = $"https://manage.office.com/api/v1.0/{TenantId}/activity/feed/subscriptions/start?contentType=Audit.SharePoint&PublisherIdentifier={PublisherIdentifier}";
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, httpRequestUrl);
    httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
    var httpResponseMessage = HttpClient.SendAsync(httpRequestMessage).GetAwaiter().GetResult();
    var httpResponseContent = httpResponseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}

コンテンツの URL を取得する

ログを取得する時間を指定してコンテンツ データのダウンロード先の URL を取得します。時間は 24 時間以内で過去 7 日以内である必要があります。今回は 1 日前のデータを取得するように指定します。

private static void GetContentUri()
{
    var startTime = DateTime.Today.AddDays(-1).ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss");
    var endTime = DateTime.Today.AddSeconds(-1).ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss");
    var httpRequestUrl = $"https://manage.office.com/api/v1.0/{TenantId}/activity/feed/subscriptions/content" +
        $"?contentType=Audit.SharePoint" +
        $"&PublisherIdentifier={PublisherIdentifier}" +
        $"&startTime={startTime}" +
        $"&endTime={endTime}";
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, httpRequestUrl);
    httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
    var httpResponseMessage = HttpClient.SendAsync(httpRequestMessage).GetAwaiter().GetResult();
    var httpResponseContent = httpResponseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
    var httpResponseJson = JsonConvert.DeserializeObject<JArray>(httpResponseContent);
    ContentUri = httpResponseJson[0].Value<string>("contentUri");
}

コンテンツを取得する

取得した URL から JSON 形式のコンテンツを取得します。

private static void GetContents()
{
    var httpRequestUrl = ContentUri;
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, httpRequestUrl);
    httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
    var httpResponseMessage = HttpClient.SendAsync(httpRequestMessage).GetAwaiter().GetResult();
    var httpResponseContent = httpResponseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
    var httpResponseJson = JsonConvert.DeserializeObject<JArray>(httpResponseContent);
    Console.WriteLine(JsonConvert.SerializeObject(httpResponseJson, Formatting.Indented));
}

実行結果

以下のような JSON が出力されます。サンプルはファイルのダウンロードですが、それだけではなく、いろいろな種類のログが取得できます。

[
  {
    "CreationTime": "2020-09-01T14:00:03",
    "Id": "bea6430c-9db6-400c-ebbf-08d84e7f532c",
    "Operation": "FileDownloaded",
    "OrganizationId": "92dbed3f-d37a-4f19-a392-f6970505cc6a",
    "RecordType": 6,
    "UserKey": "i:0h.f|membership|10033fffac7f4b34@live.com",
    "UserType": 0,
    "Version": 1,
    "Workload": "OneDrive",
    "ClientIP": "52.185.144.178",
    "ObjectId": "https://karamem0jp-my.sharepoint.com/personal/takashi_shinohara_karamem0_jp/Documents/fitbit.json",
    "UserId": "takashi.shinohara@karamem0.jp",
    "CorrelationId": "1aba0151-abda-4b69-a102-a9319a2a9e15",
    "EventSource": "SharePoint",
    "ItemType": "File",
    "ListId": "ccf377fa-0605-41b5-925a-224e62839884",
    "ListItemUniqueId": "3afd549b-4c58-4026-a7e2-d4fcc1a2ebe0",
    "Site": "0815189e-2f76-44f2-89fe-f2dd7260e20d",
    "WebId": "15cdf073-9e78-4667-94ea-96abe6aa860f",
    "HighPriorityMediaProcessing": false,
    "SourceFileExtension": "json",
    "SiteUrl": "https://karamem0jp-my.sharepoint.com/personal/takashi_shinohara_karamem0_jp/",
    "SourceFileName": "fitbit.json",
    "SourceRelativeUrl": "Documents"
  },
...
]

何が取得できるかについては以下が参考になります。

docs.microsoft.com

まとめ

Office 365 管理 API に関する情報は以下にまとまっています。

docs.microsoft.com

オンプレの SharePoint で監査ログを取っていろいろやっていたことを、SharePoint Online に移行しても同じことをやりたい、というときにぜひ使っていただきたいと思います。