【要検証】範囲を絞り共有ドライブフォルダ一括移動権限をユーザに付与する(Google Apps Script)

共有ドライブのフォルダ整理の際、ユーザーにフォルダを移動してもらいたいと思い、権限を付与しようとすると、共有ドライブ全体の管理者にする必要が出てきます。
そこまでの権限はユーザーに与えられず、結局情シスで作業するケースありませんか?

ということで、ユーザーがGoogleフォーム経由で、フォルダ範囲を絞り共有ドライブフォルダ一括移動する権限を、ユーザーに与えられるGoogle Apps ScriptをChatGPTと相談しながら作ってみました。
ただし、個人契約の試用期間テナントでの動作検証しか行っていないため、検証してくださる方絶賛募集中です。

機能概要

こちらの公式ヘルプページの「ほとんどのファイルやフォルダを共有ドライブに移動できるようにする」権限を付与したアカウントで、Googleフォーム送信時に実行されるトリガーを設定しておくことで、実質的に権限をユーザーに付与します。
フォームに移動元フォルダIDと、移動先親フォルダIDを入力して送信すると、フォルダが移動できます。

フォームは組織内誰でもアクセスでますが、フォーム送信者が以下両方の条件を満たさなければ、動作しないようにすることで、範囲を限定しています。

  • 指定のGoogleグループに所属

  • 移動元と、移動先の両方のフォルダのコンテンツ管理者以上(管理者、コンテンツ管理者、オーナー)の権限を保持

その他特徴

  • moveToメソッドで移動しているため、フォルダのID、リンクは変更されない

  • エラーや移動結果はメール(フォーム送信者と、トリガー設定アカウント宛)、Webhookで通知

  • 共有ドライブ→マイドライブの移動も一応可能※1回試しにやったら、同じURLで開けました。マイドライブ→共有ドライブの移動に使えるツールも作ったので後程公開しようと思います。

使い方

詳細は割愛しますが、以下の手順で使えるようになると思います。影響範囲の広い権限を付与するので、セキュリティなどはよく確認の上、設定してください。

  • 「ほとんどのファイルやフォルダを共有ドライブに移動できるようにする」権限をアカウントに付与(特権管理者に両共有ドライブの管理者権限を付与するなど)

  • 移動するフォルダのID、移動先の親フォルダIDの2項目のフォームを作成

  • フォーム設定で「メールアドレスを収集する」を確認済みにし、その他設定も行う。

  • フォームの編集メニューのスクリプトエディタにコードをペースト

  • スクリプトエディタのサービスで、Admin SDK APIと、Drive APIを追加

  • WebhookURL、実行権限をつけるGoogleグループを定義

  • 権限をつけたアカウントでmoveSharedDriveFolder(e)関数のトリガーをフォーム送信時で設定。(※3/31 関数名をonFormSubmitから変更)

Google Apps Scriptのコードこちらになります。

function moveSharedDriveFolder(e) {
  // 変数の宣言
  const groupEmails = ''; // フォルダ移動権限を持つグループアドレスをカンマ区切りで指定。空欄の場合チェックなし。
  const WebhookURL = ''; // SlackかGoogleChatのWebhook URLを設定。空欄の場合送信なし
  const responsesLength = FormApp.getActiveForm().getResponses().length;
  const formResponse = (e !== undefined) ? e.response : FormApp.getActiveForm().getResponses()[responsesLength - 1]; //テスト実行時eがundefinedの時に直前の送信内容を読み込む
  const itemResponses = formResponse.getItemResponses();
  const executingUserEmail = Session.getActiveUser().getEmail(); // GAS実行ユーザーを取得★
  
  // フォームの回答から送信者のメールアドレスを取得
  const senderEmail = formResponse.getRespondentEmail();

  // 移動するフォルダIDと移動先の親フォルダIDをフォームから取得
  const sourceFolderId = itemResponses[0].getResponse();
  const destinationFolderId = itemResponses[1].getResponse();

  // 移動先の親フォルダと移動するフォルダを取得
  const destinationFolder = DriveApp.getFolderById(destinationFolderId);
  const sourceFolder = DriveApp.getFolderById(sourceFolderId);

  // 移動するフォルダのアクセス権を取得
  const sourceFolderAccess = sourceFolder.getAccess(senderEmail);
  // 移動先の親フォルダのアクセス権を取得
  const destinationFolderAccess = destinationFolder.getAccess(senderEmail);

  // コンソールログに変数内容を出力
  console.log("Sender Email:", senderEmail);
  console.log("Source Folder Access:", sourceFolderAccess.name());
  console.log("Destination Folder Access:", destinationFolderAccess.name());

  // フォルダの移動を許可するアクセス権のリスト
  const allowedAccessLevels = [
    DriveApp.Permission.OWNER,
    DriveApp.Permission.FILE_ORGANIZER,
    DriveApp.Permission.ORGANIZER
  ];

  // 送信者のメールアドレスに対する移動するフォルダと移動先の親フォルダのアクセス権を確認
  if (allowedAccessLevels.includes(sourceFolderAccess) && allowedAccessLevels.includes(destinationFolderAccess) && checkMembershipInGroups(groupEmails, senderEmail)) {
    try {
      // フォルダを移動
      const movedFolder = sourceFolder.moveTo(destinationFolder);
      
      // 移動の結果を送信者に通知
      const sourceFolderName = sourceFolder.getName();
      const destinationFolderName = destinationFolder.getName();
      const message = `フォルダの移動が正常に完了しました。\n\n移動元\nフォルダ名: ${sourceFolderName}\nID: ${sourceFolderId}\n\n移動後フォルダURL\n${movedFolder.getUrl()}`;
      sendEmailAndWebhook(senderEmail, "フォルダの移動完了", message, WebhookURL);
    } catch (error) {
      // エラーが発生した場合、エラー内容を送信者に通知
      const errorMessage = `※このフォームでマイドライブから共有ドライブへの移動はできません。\nエラー内容: ${error}`;
      sendEmailAndWebhook(senderEmail, "フォルダの移動エラー。", errorMessage, WebhookURL);
      
    }
  } else {
    // 権限がない場合、送信者にエラーを通知
    let errorMessage = "以下権限不足によりフォルダの移動に失敗しました。";
    if (!allowedAccessLevels.includes(sourceFolderAccess)) {
      errorMessage += "\n移動するフォルダのコンテンツ管理者以上の権限";
    }
    if (!allowedAccessLevels.includes(destinationFolderAccess)) {
      errorMessage += "\n移動先の親フォルダのコンテンツ管理者以上の権限";
    }
    if (!checkMembershipInGroups(groupEmails, senderEmail)) {
      errorMessage += "\nフォルダ移動権限のあるグループへの参加";
    }
    sendEmailAndWebhook(senderEmail, "フォルダの移動エラー", errorMessage, WebhookURL);
  }
}


function checkMembershipInGroups(groupEmails, targetEmail) {
  // groupEmailsが空の場合は真を返す
  if (!groupEmails || groupEmails.trim() === "") {
    return true;
  }

  // カンマで区切られたテキストを配列に変換
  let groupEmailList = groupEmails.split(",");

  for (let i = 0; i < groupEmailList.length; i++) {
    let groupEmail = groupEmailList[i].trim();
    let members = AdminDirectory.Members.list(groupEmail).members;

    if (members) {
      for (let j = 0; j < members.length; j++) {
        let member = members[j];
        if (member.email == targetEmail) {
          return true; // メールアドレスが見つかったら真を返す
        }
      }
    }
  }

  return false; // メールアドレスが見つからなかったら偽を返す
}


function sendEmailAndWebhook(mailAddress, subject, body, WebhookURL) {
    try {
        const executingUserEmail = Session.getActiveUser().getEmail(); // GAS実行ユーザーを取得★
        
        // メール送信(mailAddress に送信)
        MailApp.sendEmail(mailAddress, subject, body);
        
        // メール送信(executingUserEmail に送信)
        MailApp.sendEmail(executingUserEmail, subject, body); // GAS実行ユーザーに送信★
        
        // Webhookにメール送信先を含む内容を送信
        if (WebhookURL !== null) {
            const webhookMessage = `メール送信先: ${mailAddress}\n\nメール内容: ${subject}\n\n${body}`;
            sendWebhookMessage(WebhookURL, webhookMessage);
        }
    } catch (e) {
        // エラーがあればコンソールに出力
        console.error(e);
        // Webhookにエラーを送信
        if (WebhookURL !== null) {
            sendWebhookMessage(WebhookURL, `エラーが発生しました: ${e}`);
        }
    }
}

function sendWebhookMessage(webhookURL, message) {
    const payload = {
        text: message
    };
    const options = {
        method: "post",
        contentType: "application/json",
        payload: JSON.stringify(payload)
    };
    UrlFetchApp.fetch(webhookURL, options);
}

サンプルフォーム

こちらにサンプルのフォームのリンクを置いておきます。こちらをコピーして使っていただいてもOKです。
https://drive.google.com/drive/folders/15gp8c8TEkGyOwFuR06zgp3VcgcWSE-BH?usp=sharing

使用上の注意

個人的にはだいぶ便利なものができた気がするのですが、実務では使用していないため、使う際は検証しながら使ってください。追加の注意点、改善点もあれば知らせていただけると嬉しいです。

  • フォームの公開範囲、権限付与状況によっては社外ユーザーも動作させることができてしまいます。

  • groupEmailsは空欄にすると、チェックをせずに実行するので、必要な場合は定義を忘れないで下さい。

この記事が気に入ったらサポートをしてみませんか?