見出し画像

#138 Google グループを操作する(作成と削除はできた!)


はじめに

注意: 今回の記事は、Google Workspace 環境に特化したものなので、個人の Google アカウントでは実行できない点に注意してください。

職場で Google Workspace を運用していて、「Google グループの作成などを GAS を使ってできないか?」と考え、試行錯誤してみました。希望する処理は、GAS で以下のような処理を行う感じ…

  1. グループの作成

  2. グループの設定変更

    • グループの種別(共同トレイを有効にする / しない) ※下図参照

    • それぞれの役割での権限の設定

  3. メンバーの追加/削除

  4. グループの削除

Google グループの追加の機能で「共同トレイ」
Google グループの UI で行える設定

結論を先に書いておきますが…
上記のような 1. ~ 4. の処理を GAS で行えないかを確認しましたが、一番行いたかった 2. の処理が、現時点で提供されている API では実現できそうにありません。それ以外は、提供されている API で実現できそうなのですが、肝心な 2. ができないのです。
こういった設定部分を GAS で自動化することで、設定漏れを無くすようにしたかったのですが、断念しました。 以降では、そのように判断した経緯をまとめておきます。

提供されている API は 2種類

GAS で利用できる Google グループに関する API は、

の 2種類が提供されています。

(1) Groups Service

結局のところ、このサービスで行えることは「Class Group」の ↓ のように提供されているメソッドの通り。ざっくり要約すると、既に作成されているグループのメンバーや役割を確認できるだけで、メンバーを追加/削除は行えません。

Class Group」で提供されているメソッド

(2) Admin SDK Directory サービス

高度なサービスとして提供されている Admin SDK Directory サービスは、下図の左側のツリーで表示されているように、提供されている機能は多岐に渡っています。

Directory API で提供されている機能は多岐にわたる
(日本語に翻訳すると、不可解な翻訳になる部分があるので英語表記)

その中で、以下のように「REST Resource: groups」でグループに関する機能が提供されています。

提供されているメソッドは ↓ のような感じですが、グループを作成するための insert、更新のための updatepatch、の引数として用いられる「REST Resource: groups」のメンバーにはグループの表示名(name)や説明(description)はあるものの、グループの設定に関する内容(冒頭の 2. の処理の内容)に関係しそうなものは含まれていません。

「REST Resource: groups」に提供されているメンバー
「REST Resource: groups」に提供されているメソッド

メンバーの追加/削除については、「REST Resource: members」によって実現できそうです。

「REST Resource: members」に提供されているメソッド

作ってみたプログラム

前項の内容を確認しながら、下図のような Googleスプレッドシートにバインドされたプログラムを作ってみました。

作成したスプレッドシート

列 B ~ D に所定の内容を入力して、メニューから「現在の行の内容でグループを作成」を選択すると、その行の内容をもとにグループを作成します。
同様に、既存のグループの情報を取得したり、グループの削除が行えます。

メニューからグループの操作を行う

実際のプログラムは、以下の通りで、再利用可能なように細かく関数分割を行ってみました。ほとんど、スプレッドシートの内容をもとに API を呼び出しているだけなので、シンプルなプログラムになっています。

"use strict";                               // 変数の宣言を強制する

//  グループの情報が格納されているクラス
//    https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups?hl=ja#Group

const COL_EMAIL = 2;                        // グループのメールアドレス
const COL_NAME = 3;                         // グループの表示名
const COL_DESCRIPTION = 4;                  // ユーザーがグループの目的を判断するための詳細な説明
const COL_ID = 5;                           // グループの一意の ID
const COL_ADMINCREATED = 6;                 // このグループがユーザーではなく管理者によって作成されたか?
const COL_MEMBERCOUNT = 7;                  // グループの直接的なメンバーであるユーザーの数
const COL_MESSAGE = 8;                      // 処理結果/メッセージを出力する列番号

/**
 * スプレッドシートにメニュー項目を追加する
 */
function onOpen() {
  Logger.log("[開始] メニュー項目を追加");

  SpreadsheetApp.getUi()
    .createMenu('グループの操作')
    .addItem('現在の行のグループの情報を取得', 'getGroupInfoUI')
    .addItem('現在の行の内容でグループを作成', 'createGroupUI')
    .addItem('現在の行のグループを削除', 'deleteGroupUI')
    .addToUi();

  Logger.log("[終了] メニュー項目を追加");
}

/**
 * 現在のアクティブなセルの行と列を取得する
 * 
 * @param {object} sheet - セルの位置を確認するシート(Class Sheet)
 * @return {object} - アクティブなセルの行(row)と列(column)
 */
function getRowColumn(sheet) {
  let range = sheet.getActiveRange();

  return {
    "row": range.getRow(),
    "column": range.getColumn(),
  };
}

/**
 * 当該行のグループ情報を取得する(UI からの呼び出し)
 */
function getGroupInfoUI() {
  let sheet = SpreadsheetApp.getActiveSheet();
  let res = getRowColumn(sheet);

  let gMail = sheet.getRange(res.row, COL_EMAIL).getValue();
  console.log(`Active : ( ${res.row}, ${res.column} ) ${gMail}`);

  try {
    let gInfo = getGroupInfo(gMail);
    console.log(gInfo);

    sheet.getRange(res.row, COL_NAME).setValue(gInfo.name);
    sheet.getRange(res.row, COL_DESCRIPTION).setValue(gInfo.description);
    sheet.getRange(res.row, COL_ID).setValue(gInfo.id);
    sheet.getRange(res.row, COL_ADMINCREATED).setValue(gInfo.adminCreated);
    sheet.getRange(res.row, COL_MEMBERCOUNT).setValue(gInfo.directMembersCount);
    sheet.getRange(res.row, COL_MESSAGE).setValue('OK');
  }
  catch (e) {
    console.error(e.message);
    sheet.getRange(res.row, COL_MESSAGE).setValue(e.message);
  }
}

/**
 * 当該行のグループ情報を取得する
 * 
 * @param {String} gMail - グループのメールアドレス
 * @return {object} - 取得したグループの情報(Resource: groups)
 */
function getGroupInfo(gMail) {
  // https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups/get?hl=ja
  return AdminDirectory.Groups.get(gMail);
}

/**
 * 当該行の内容からグループを作成する(UI からの呼び出し)
 */
function createGroupUI() {
  let sheet = SpreadsheetApp.getActiveSheet();
  let res = getRowColumn(sheet);

  let gMail = sheet.getRange(res.row, COL_EMAIL).getValue();
  let gName = sheet.getRange(res.row, COL_NAME).getValue();
  let gDesc = sheet.getRange(res.row, COL_DESCRIPTION).getValue();
  console.log(`Active : ( ${res.row}, ${res.column} ) ${gMail}`);

  try {
    //  https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups/insert?hl=ja
    let gInfo = createGroup(gMail, gName, gDesc);
    Logger.log(gInfo);

    sheet.getRange(res.row, COL_ID).setValue(gInfo.id);
    sheet.getRange(res.row, COL_ADMINCREATED).setValue(gInfo.adminCreated);
    sheet.getRange(res.row, COL_MEMBERCOUNT).setValue(gInfo.directMembersCount);
    sheet.getRange(res.row, COL_MESSAGE).setValue('OK');
  }
  catch (e) {
    console.error(e.message);
    sheet.getRange(res.row, COL_MESSAGE).setValue(e.message);
  }
}

/**
 * 当該行の内容からグループを作成する
 * 
 * @param {String} gMail - グループのメールアドレス
 * @param {String} gName - グループの表示名
 * @param {String} gDesc - ユーザーがグループの目的を判断するための詳細な説明
 * @return {object} - 作成したグループの情報(Resource: groups)
 */
function createGroup(gMail, gName, gDesc = '') {
  //  https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups/insert?hl=ja
  return AdminDirectory.Groups.insert(
    {
      "email": gMail,
      "name": gName,
      "description": gDesc,
    }
  );
}

/**
 * 当該行の内容からグループを削除する(UI からの呼び出し)
 */
function deleteGroupUI() {
  let sheet = SpreadsheetApp.getActiveSheet();
  let res = getRowColumn(sheet);

  let gMail = sheet.getRange(res.row, COL_EMAIL).getValue();
  let gName = sheet.getRange(res.row, COL_NAME).getValue();
  let gDesc = sheet.getRange(res.row, COL_DESCRIPTION).getValue();
  console.log(`Active : ( ${res.row}, ${res.column} ) ${gMail}`);

  let dlg = Browser.msgBox(`グループ「${gName} (${gMail})」の削除を実行してもよいですか?`, Browser.Buttons.YES_NO);
  if (dlg != 'yes') {
    SpreadsheetApp.getActiveSpreadsheet().toast('グループの削除はキャンセルされました。');
    return;
  }

  try {
    let gInfo = deleteGroup(gMail);
    Logger.log(gInfo);
    sheet.getRange(res.row, COL_ID).clearContent();
    sheet.getRange(res.row, COL_ADMINCREATED).clearContent();
    sheet.getRange(res.row, COL_MEMBERCOUNT).clearContent();
    sheet.getRange(res.row, COL_MESSAGE).setValue('OK');
  }
  catch (e) {
    console.error(e.message);
    sheet.getRange(res.row, COL_MESSAGE).setValue(e.message);
  }
}

/**
 * 当該行の内容からグループを削除する
 * 
 * @param {String} gMail - グループのメールアドレス
 * @return {object} - 作成したグループの情報(Resource: groups)
 */
function deleteGroup(gMail) {
  // https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups/delete?hl=ja
  return AdminDirectory.Groups.remove(gMail);
}

冒頭の 1. と 4. の処理を組み込んだところで、前述のように 2. の処理に対応した API が提供されていないことが確認できたこともあって、3. の処理は実装していません。

気付いたこと

前述のプログラムを作ってみて、気付いたことを書き連ねておきます。

  • Google グループの API は 2種類に分けて提供されているが、Google グループの設定に関する部分を設定/変更するための API は提供されていない。

  • リファレンスでは「REST Resource: groups」のグループを削除するためのメソッドとして、delete が提供されているように説明されているが、実際に提供されているのは remove となっている。

delete ではなく remove ?
  • REST Resource: groups」の insert メソッドでグループを作成した場合、スクリプトを実行した人がオーナー(所有者)になるものの、メンバーにオーナーも含めて誰も所属していない状態を作れる。 ※オーナーがメンバーには含まれていない状態

最後に

今回の記事では、当初の想定していた処理のすべてを行うことができませんでしたが、Googleグループ の Web UI でグループを作成したときには、↓ のようにオーナーには自身が設定された状態になり、オーナー不在にはできません。 ※管理コンソールで操作する分には、メンバー不在のグループは作成できます。

Web UI でグループを作成する際の画面

しかしながら、GAS のプログラムでグループを作成した場合には、オーナーに自身が設定されるものの、メンバーには含まれていない状態を作り出せます。このような状態になることで、

  • オーナーでなければ行えない操作は Web UI から行える

  • オーナーがメンバーには含まれていないので、メールが配信されない

といった状態を作れます。このようなグループであれば、オーナーに設定された管理者に大量のメールが配信されることを防げます。ひとまず、このような運用が行えることが分かったことは有益でした。👍

最後に、お決まりのフレーズなどを書いておきます。

  • 一応の動作確認は行っているものの、不慮のトラブルによって損害等が生じても、責任はとれませんので予めご了承ください。

  • コメントを含めても短いスクリプトであり、実行に際して目的外の場所への書き出しや収集などは行っていません。

  • 特別なエラー処理は行っていないので、意図しないケースでエラーが発生してしまうかもしれません。どうにもならない場合には、ご連絡ください。

わたし自身にしてみると、このような「スクリプトを作ること」が目的になっているような感じですが、このスクリプトが何かの役に立てば幸いです。
これらの記事への感謝の意は、記事に対する「スキ ♡」や、以下のような Amazon のアフィリエイトリンク経由での商品の購入、note の「チップ」機能を利用してもらえると、このようなプログラム作成の励みになります。😍

  • Amazon のアフィリエイトリンクを開くと、それ以降 24時間以内に商品を購入した場合に、紹介者(この場合は、わたし)に紹介料が発生します。

記事の下部にある note の「チップ」機能

いいなと思ったら応援しよう!