GASでGoogleドライブ監査ログを自動バックアップ



# 背景
Googleドライブの監査ログを管理画面から手動で定期的にスプレッドシートにバックアップしていましたが、従業員の人数が増えたことにより、バックアップするまでにかなりの時間がかかるようになってきたので、Google Apps Scriptでバックアップ作業を自動化しました。
普段はコードを書いたりしないので、コードが汚いと思いますがご容赦ください。

# 使用するもの
・　GoogleAppsScript
・　Reports API
・　スプレッドシート

# コード全体
```JavaScript 

function generateDriveActivityReport() {
  var now = new Date();
  var nowTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
  var yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, 0, 0, 0);
  var startTime = yesterday.toISOString();
  var endTime = nowTime.toISOString();
  
  var row2 = [];
  var row3 = [];
  var rows = [];
  var rows2 = [];
  var rows3 = [];
  var rows4 = [];
  var pageToken;
  var page;
  var applicationName = 'drive';
  do {
    page = AdminReports.Activities.list('all', applicationName, {
      startTime: startTime,
      endTime: endTime,
      eventName: 'change_acl_editors',
      pageToken: pageToken
    });
    var items = page.items;
    if (items) {
      for (var i = 0; i < items.length; i++) {
        var item = items[i];
        if (item.events[0].parameters[0].boolValue == true){
          var row = [
            new Date(item.id.time),
            item.actor.email,
            item.events[0].parameters[8].value,
            item.events[0].name,
          ];
        
          if (item.events[0].parameters[3].multiValue[1] != null){
            var row2 = [[item.events[0].parameters[3].multiValue[0]] + ',' + [item.events[0].parameters[3].multiValue[1]]];
          }else{
            var row2 = [item.events[0].parameters[3].multiValue[0]];
          }
        
          if (item.events[0].parameters[4].multiValue[1] != null){
            var row3 = [[item.events[0].parameters[4].multiValue[0]] + ',' + [item.events[0].parameters[4].multiValue[1]]];
          }else{
            var row3 = [item.events[0].parameters[4].multiValue[0]];
          }
        
          if (item.events[0].parameters[10].name === 'owner'){
            var row4 = [item.events[0].parameters[10].value];
          }else{
            var row4 = [item.events[0].parameters[11].value];
          }
          rows.push(row);
          rows2.push(row2);
          rows3.push(row3);
          rows4.push(row4);
        }
      }
    }
    pageToken = page.nextPageToken;
  } while (pageToken);

  if (rows.length > 0) {
    var formatDate = Utilities.formatDate(new Date(), "JST","yyyyMMdd");
    var spreadsheet = SpreadsheetApp.create('G Suite Drive Audit Report(change_acl_editors)_' + formatDate);
    var sheet = spreadsheet.getActiveSheet();

    var headers = ['Time', 'User', 'FileName', 'Event', 'old_value', 'new_value', 'Owner'];
    sheet.appendRow(headers);

    sheet.getRange(2, 1, rows.length, 4).setValues(rows);
    sheet.getRange(2, 5, rows2.length, 1).setValues(rows2);
    sheet.getRange(2, 6, rows3.length, 1).setValues(rows3);
    sheet.getRange(2, 7, rows4.length, 1).setValues(rows4);

    Logger.log('Report spreadsheet created: %s', spreadsheet.getUrl());
    
    var FOLDER_ID = 'GoogleドライブのフォルダID';
    var sheetId = spreadsheet.getId();
    var fileSS = DriveApp.getFileById(sheetId);
    var folderTarget = DriveApp.getFolderById(FOLDER_ID);
    
    folderTarget.addFile(fileSS);
    DriveApp.getRootFolder().removeFile(fileSS);
    
  } else {
    Logger.log('No results returned.');
  }
}
```
# 解説

※コードについては本家のサンプルを参考にしております。
https://developers.google.com/apps-script/advanced/admin-sdk-reports

```JavaScript
function generateDriveActivityReport() {
  var now = new Date();
  var nowTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
  var yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, 0, 0, 0);
  var startTime = yesterday.toISOString();
  var endTime = nowTime.toISOString();
```
前日分のログをバックアップするために、実行日の0:00と実行日前日の0:00をDateオブジェクトで取得しています。そしてReports APIで使用するために日付をISO8601ExtendedFormatに準じた文字列に変換しています。

```JavaScript
  do {
    page = AdminReports.Activities.list('all', applicationName, {
      startTime: startTime,
      endTime: endTime,
      eventName: 'change_acl_editors',
      pageToken: pageToken
    });
```
上記で取得した日付とイベント名を指定してReports APIに経由で監査ログを取得しています。別のイベントの監査ログを取得したい場合は、以下のリファレンスを参照してイベント名を変更してみてください。

https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive?apix_params=%7B%22userKey%22%3A%22all%22%2C%22applicationName%22%3A%22drive%22%2C%22eventName%22%3A%22change_acl_editors%22%2C%22maxResults%22%3A100%7D#change_acl_editors

```JavaScript
    var items = page.items;
    if (items) {
      for (var i = 0; i < items.length; i++) {
        var item = items[i];
        if (item.events[0].parameters[0].boolValue == true){ 
          var row = [
            new Date(item.id.time),
            item.actor.email,
            item.events[0].parameters[8].value,
            item.events[0].name,
          ];
        
          if (item.events[0].parameters[3].multiValue[1] != null){
            var row2 = [[item.events[0].parameters[3].multiValue[0]] + ',' + [item.events[0].parameters[3].multiValue[1]]];
          }else{
            var row2 = [item.events[0].parameters[3].multiValue[0]];
          }
        
          if (item.events[0].parameters[4].multiValue[1] != null){
            var row3 = [[item.events[0].parameters[4].multiValue[0]] + ',' + [item.events[0].parameters[4].multiValue[1]]];
          }else{
            var row3 = [item.events[0].parameters[4].multiValue[0]];
          }
        
          if (item.events[0].parameters[10].name === 'owner'){
            var row4 = [item.events[0].parameters[10].value];
          }else{
            var row4 = [item.events[0].parameters[11].value];
          }
          rows.push(row);
          rows2.push(row2);
          rows3.push(row3);
          rows4.push(row4);
        }
      }
    }
    pageToken = page.nextPageToken;
  } while (pageToken);
```
取得した監査ログの中から必要な値を選定しています。
今回は以下を出力します。

1. タイムスタンプ
2. ユーザー名
3. ファイルの名前
4. イベント名
5. 変更前の値
6. 変更後の値
7. ファイルのオーナー

変更前の値と変更後の値は複数の可能性があるので、if文でまわしています。
ファイルのオーナーについては、個人かチームドライブかによって指定する場所が変わるので、こちらもif文でまわしています。
最後にシートにレコードを追加するためにpushメソッドで変数の末尾にレコードを追加しています。

```JavaScript
  if (rows.length > 0) {
    var formatDate = Utilities.formatDate(new Date(), "JST","yyyyMMdd");
    var spreadsheet = SpreadsheetApp.create('G Suite Drive Audit Report(change_acl_editors)_' + formatDate);
    var sheet = spreadsheet.getActiveSheet();
```
監査ログはスプレッドシートで保存をしたいので、保存用のスプレッドシートを作成します。
スプレッドシートの名前は適宜修正してください。

```JavaScript
 var headers = ['Time', 'User', 'FileName', 'Event', 'old_value', 'new_value', 'Owner'];
    sheet.appendRow(headers);

    sheet.getRange(2, 1, rows.length, 4).setValues(rows);
    sheet.getRange(2, 5, rows2.length, 1).setValues(rows2);
    sheet.getRange(2, 6, rows3.length, 1).setValues(rows3);
    sheet.getRange(2, 7, rows4.length, 1).setValues(rows4);
```
スプレッドシートに項目名を追加します。
項目名が1行目になるので、2行目から出力したデータを入力していきます。

```JavaScript
    var FOLDER_ID = 'GoogleドライブのフォルダID';
    var sheetId = spreadsheet.getId();
    var fileSS = DriveApp.getFileById(sheetId);
    var folderTarget = DriveApp.getFolderById(FOLDER_ID);
    
    folderTarget.addFile(fileSS);
    DriveApp.getRootFolder().removeFile(fileSS);
    
  } else {
    Logger.log('No results returned.');
  }
}
```
監査ログを保存したいGoogleドライブのフォルダIDを入力します。
フォルダIDの確認方法はURLの

```
https://drive.google.com/drive/u/1/folders/{フォルダID}
```

{フォルダID}の部分です。

# おわりに
こちらのスクリプトをトリガーで毎日動かせば、定期的に手動でバックアップする手間が省け格段に楽になります。

~~ただ1点問題があります。~~
~~API経由と管理コンソール経由とでレポート結果の数が違うことが確認されています。これはGoogleのサポートから「管理コンソール上の数値は正しく認識しておりましたが、APIとの概念に相違があったため、APIでは管理コンソールと異なるファイル数を表示している」との回答がありました。~~

~~確認したところ、API＞管理コンソールとなっており、足りないわけではないので、監査自体には問題はありません。ただ余計なログが残ることにより、結果のデータが肥大するので、引き続きGoogleに問い合わせをしていきます。~~

上記ですが、Googleより
『例としまして、「フォルダに追加」イベントでは、「アップロード」のイベントを副次的要素として含みます。これは、データを Google ドライブにアップロードした場合、マイドライブを含むいずれかのフォルダにデータを追加するイベントも副次的に行われているためです。
回避策としまして、コマンド内容に、 “ primary_event ” を “ true ” とする記述をフィルタとして加えていただきますと、管理コンソールのレポート結果と同じ結果が API でも得られます。』
との回答があり、“ primary_event ” を “ true ” とするフィルターを設定したところ、管理コンソールのレポート結果と同じ結果が API でも得られました。