🦆

Googleの監査ログで外部共有操作を監視したい(下準備)

2025/03/11に公開

なぜ作成したのか

  • いい加減見ないふりをしてきたGoogleドライブの外部共有を可視化していきたい

当面の目標

  • Googleドライブの監査ログで外部共有アクションを一日ごとに集計し、その日に組織で外部共有されたアイテム、実施者を可視化できるようにしたい

以下はサンプルコードになります。ポイントは以下のとおりです。

  • 管理者アカウント で実行し、対象のドメインの監査ログへアクセスする権限が必要です。
  • スクリプト内では、Admin SDK レポートの拡張サービス(AdminReports)を使用します。スクリプトエディタの「拡張機能」→「サービス」メニューから「Admin SDK」を有効にしてください。
  • 指定した期間(例:前日~当日)の間に発生した Googleドライブの共有変更(外部共有)イベントを取得し、スプレッドシートにまとめる例です。
  • 実行後、トリガーを設定して毎日自動実行させることで、日次集計の運用が可能です(Apps Scriptの編集画面 → 時計アイコン「トリガー」から設定)。
/**************************************
 * 外部共有アクションを日次集計し、
 * 対象スプレッドシートに書き出す例
 * (共有先メールアドレス、共有ドライブ名付き)
 **************************************/

function exportDailyExternalSharingLog() {
  // --- ①取得期間を設定(前日0時~当日0時などに調整)---
  // ここでは例として「前日の00:00:00 〜 今日の00:00:00」
  var today = new Date();
  var start = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1, 0, 0, 0);
  var end   = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0);

  // レポートAPI用の時間形式に変換 (ISO8601: yyyy-MM-ddTHH:mm:ssZ)
  var startTime = Utilities.formatDate(start, "UTC", "yyyy-MM-dd'T'HH:mm:ss'Z'");
  var endTime   = Utilities.formatDate(end,   "UTC", "yyyy-MM-dd'T'HH:mm:ss'Z'");

  // --- ②Admin SDK レポートからDriveログを取得 ---
  var activities = [];
  var pageToken;
  do {
    var response = AdminReports.Activities.list("all", "drive", {
      startTime: startTime,
      endTime: endTime,
      pageToken: pageToken
    });
    if (response.items && response.items.length > 0) {
      activities = activities.concat(response.items);
    }
    pageToken = response.nextPageToken;
  } while (pageToken);

  // --- ③外部共有イベントと思しきものをフィルタリング ---
  // "acl_change" or "change_user_access" イベント かつ newValue = "outsideDomain" を対象
  var externalSharingActivities = activities.filter(function(activity) {
    if (!activity.events) return false;
    return activity.events.some(function(evt) {
      if (evt.name !== "acl_change" && evt.name !== "change_user_access") return false;
      // パラメータの中に newValue: "outsideDomain" があるかチェック
      var hasOutsideParam = (evt.parameters || []).some(function(param) {
        return param.name === "newValue" && param.value === "outsideDomain";
      });
      return hasOutsideParam;
    });
  });

  // --- ④結果をスプレッドシートに書き出す ---
  // 例として、スクリプトが紐づいたスプレッドシートに「YYYY-MM-DD_外部共有」シートを作成して書き込み
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetName = Utilities.formatDate(start, "Asia/Tokyo", "yyyy-MM-dd") + "_外部共有";
  var sheet = ss.getSheetByName(sheetName) || ss.insertSheet(sheetName);
  sheet.clearContents();

  // 見出し行
  var headers = [
    "イベント日時 (UTC)",
    "実施者(メール)",
    "ドキュメントタイトル",
    "ドキュメントID",
    "イベント名",
    "newValue",
    "oldValue",
    "共有先メールアドレス",
    "共有ドライブ名"
  ];
  sheet.appendRow(headers);

  externalSharingActivities.forEach(function(activity) {
    var actorEmail = activity.actor.email || "";
    var eventTime  = activity.id.time; // ISO8601形式のタイムスタンプ

    (activity.events || []).forEach(function(evt) {
      // 外部共有に該当するパラメータを探す
      var docTitle = "";
      var docId = "";
      var newValue = "";
      var oldValue = "";

      // 追加パラメータ
      var targetUser = "";     // 共有先メールアドレス
      var driveName = "";      // 共有ドライブ名(該当する場合)

      (evt.parameters || []).forEach(function(param) {
        switch (param.name) {
          case "doc_title":
            docTitle = param.value;
            break;
          case "doc_id":
            docId = param.value;
            break;
          case "newValue":
            newValue = param.value;
            break;
          case "oldValue":
            oldValue = param.value;
            break;
          case "target_user":
            // 共有先メールアドレス
            // param.value に共有先のメールアドレスが入っている場合が多い
            targetUser = param.value;
            break;
          case "drive_name":
            // 共有ドライブ名(共有ドライブ上のファイルの場合に含まれる)
            driveName = param.value;
            break;
          default:
            // 他に必要なパラメータがあれば追加
            break;
        }
      });

      // フィルタリング済みなので newValue: outsideDomain を含むものをここで出力
      if (newValue === "outsideDomain") {
        sheet.appendRow([
          eventTime,
          actorEmail,
          docTitle,
          docId,
          evt.name,
          newValue,
          oldValue,
          targetUser,
          driveName
        ]);
      }
    });
  });

  Logger.log("処理が完了し、" + sheetName + " シートに " + externalSharingActivities.length + " 件のイベントを出力しました。");
}

コードの概要

  1. 日付の設定

    • 前日の0時から当日の0時までの間に行われたイベントを対象としています。
    • startTimeendTime を ISO8601形式 (yyyy-MM-dd'T'HH:mm:ss'Z') に変換。
  2. レポートAPI呼び出し

    • AdminReports.Activities.list("all", "drive", {...}) でドライブの監査ログを取得します。
    • pageToken を利用して繰り返し呼び出し、全ページ分のログを格納。
  3. 外部共有イベントのフィルタリング

    • events の中の evt.name が "acl_change" または "change_user_access" で、かつパラメータ newValue が "outsideDomain" になっているものを抽出。
  4. スプレッドシートへの書き出し

    • 前日の日付をシート名にしたシートを作り、既にあれば内容を初期化。
    • ヘッダ行を追加し、外部共有イベント1件ごとに行を追加。
  5. トリガー設定(任意)

    • Apps Scriptのトリガーを使い、毎日午前0時や早朝などに自動実行させると日次集計が自動化可能。

所感

  • 明日会社の環境で実装してみる
  • シートが作成できればSlack連携するなり、Appsheetで各従業員に自分の操作した外部共有をリスト配信させるなりの対応ができそう
  • ただしログはフロー情報なので、通知には向いてるけど管理には向かない
  • 共有ドライブ名と共有先ドメインが取得できれば、ドライブ管理者で妥当性判断する情報は提供できるようになるかも
GitHubで編集を提案

Discussion