Google グループの全メンバー一覧取得

# 概要

Google Workspace 管理者向けの、全グループのメンバーアドレス一覧を GAS で取得する話です。過去に見つけた手順[^1]に対して主に下記を直したものを記しておきます。

- メンバーが多くても取得可能
- 複数ドメインでも一括で取得

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/1deb6152-e84c-2787-811b-e8bce1803daf.png)


# 1. はじめに

# 1.1. 背景
成り行き上、Google Workspace の管理をしていたりする Kirurobo です。
管理コンソールからは選んだ1つのグループのメンバー一覧をダウンロードすることはできますが、グループが多くなってくると面倒です。そこででまとめて取得できる方法がないか探しました。

## 1.1. 既存手法

Google Apps Script を使ってスプレッドシートに書き出すものとしてて2015年のひささんのスクリプト[^1]が見つかりました。

## 1.2. 提案手法
基本的には上記の内容で良さそうだったのですが、下記について対応すべくスクリプトを修正しました。

- ページングを考慮する
    - 多くのメンバーが居る場合や多くのグループがある場合取得しきれなかったため
- ドメイン名は固定で書かない（ハードコーディングしない）
    - スクリプトをそのままコピペで動くようにする
    - ドメインが複数ある場合にも対応させる
- 確認画面が出た方が安心


# 2. 前提条件

- Google Workspace の管理者権限があること
- Google Workspace で API を有効にしていること

<!--

[Google Cloud Platform](https://console.cloud.google.com/) で Admin SDK API を有効にしていること


<details><summary>Admin SDK API 有効化</summary>

自分でプロジェクトを作成する場合は、プロジェクトを作成する。
プロジェクトが無い場合は自動で「My First Project」という名前のプロジェクトができる模様。

[Google Cloud Platform](https://console.cloud.google.com/) を開く。

ライブラリ を開き、「Admin」などで検索して Admin SDK API を探す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/e05b1817-1f29-c7b3-e6d6-07d490ed6821.png)

有効にする を押し、有効化する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/4f06bd02-4c6e-312c-542e-897ec87bbfdd.png)

</details>
-->

APIについては、管理で [セキュリティ] → [APIの制御] を開き「ドメインで所有する内部アプリを信頼する」にチェックを入れてあればそれで良いと思われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/ac81c7cf-218b-dbd3-f364-8a195bab91ba.png)


# 3. スプレッドシート作成

Googleドライブでスプレッドシートを新規作成します。
「全グループメンバー一覧」など適当に名前を付けておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/b4a3e435-9017-8ff9-ec4b-11042fbd280f.png)


# 4. スクリプトの作成

## 4.1. スクリプトエディタを開く

作成したスプレッドシートのメニューで [ツール] → [スクリプトエディタ] を選択。
※ 2022/06/22追記 現在は [拡張機能] → [App Script] のようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/cb518121-9509-b53c-61ba-12791bb3b7aa.png)



## 4.2. スクリプトを名前を付けて保存

スクリプトエディタが開いたら、適当な名前で保存しておきます。スプレッドシートと同じでも違っていてもOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/6ae443dc-aa51-e2f4-84eb-ab078583b8cc.png)

## 4.3. サービスを追加

左に見える サービス 欄の「+」を押します。
Admin SDK API を選択すると、ID「AdminDirectory」が現れます。これを追加してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/9d7e8711-6a29-11f6-2311-f5f8a28d8e8f.png)


## 4.4. コードを記述
デフォルトの コード.gs の内容は消去して、こちらをコピペしてください。
ドメイン名は自動取得するため、特にこの中を直していただかなくても動くはずです。

```JS:コード.gs
// 全グループの、全メンバーアドレス取得

// スプレッドシートを開いた際に、メニューに「スクリプト実行」を追加する
function onOpen() {
  SpreadsheetApp.getUi()
  .createMenu("スクリプト実行")
  .addItem("メンバーアドレス一覧取得", "confirmLoadGroupMembersAddress")
  .addToUi();
}

// 確認を表示してOKなら一覧読み込み
function confirmLoadGroupMembersAddress() {
  // ドメイン一覧を取得
  const domains = getDomains();

  // 開始確認
  const ui = SpreadsheetApp.getUi();

  // 読み込み処理
  var res = ui.alert(
    "確認",
    "このシートを消去して全グループの情報を読み込みます。\nよろしいですか？",
    ui.ButtonSet.OK_CANCEL);

  if (res == "OK") { 
    loadGroupMembersAddress(domains);

    ui.alert(
      "終了",
      "完了しました。OKを押すと表示されます。",
      ui.ButtonSet.OK);
  } else {
    ui.alert(
      "キャンセル",
      "キャンセルしました",
      ui.ButtonSet.OK);
  }
}

// 対象とするドメイン一覧を取得
function getDomains() {
  // 現在ログインしているユーザー情報を元に、全ドメイン一覧を取得
  var mail = Session.getActiveUser().getEmail();
  var user = AdminDirectory.Users.get(mail);
  var domains = AdminDirectory.Domains.list(user.customerId).domains;

  // ドメイン名のリストを作成して返す
  var list = [];
  for (var i = 0; i < domains.length; i++) {
    var domain = domains[i];
    list.push(domain.domainName);
  }
  return list;
}

// 全グループについて取得してシートに出力
function loadGroupMembersAddress(domains)
{
  // ヘッダーの定義
  const header = ["GroupAddress", "Name", "DirectMembersCount", "UserAddress"];

  // 追加される行を保持する配列
  var rows = [];

  // ヘッダ行を出力
  rows.push(header);

  // ドメインごとにループ
  for (var i = 0; i < domains.length; i++) {
    var domain = domains[i];

    // 取得数制限にかかる際のページング
    var groupNextPageToken = null;

    // グループごとのループ
    do {
      var grouplist = AdminDirectory.Groups.list({
        domain: domain,
        nextPageToken: groupNextPageToken
      });

      // グループが存在しなければ終了
      if (!grouplist.groups) break;

      // 次ページがあるときはトークンが入る
      groupNextPageToken = grouplist.nextPageToken;

      // 各グループについての処理
      for(var j = 0; j < grouplist.groups.length; j++){           
        var group = grouplist.groups[j];
        var memberNextPageToken = null;

        // グループに所属するメンバーの取得
        do {
          var memberlist = AdminDirectory.Members.list(group.email, {
            pageToken: memberNextPageToken
          });

          // memberlistが無ければ終了
          if (!memberlist || !memberlist.members) break;

          // 次ページがあるときはトークンが入る
          memberNextPageToken = memberlist.nextPageToken;

          // 今回（このページ）で取得できた分を一覧に追加
          for (var k = 0; k < memberlist.members.length; k++){
            var member = memberlist.members[k];

            var cols = [];         
            cols.push(group.email);
            cols.push(group.name);
            cols.push(group.directMembersCount);
            cols.push(member.email);

            rows.push(cols);
          }
        } while (memberNextPageToken);
      }
    } while (groupNextPageToken);
  }

  // 以降、スプレッドシートへの出力
  // アクティブなシート取得
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();

  // 左上セルを基準に、最終行まで削除（書式は残す）
  sheet.getRange(1, 1, sheet.getMaxRows(), header.length).clearContent();

  // 書き込み
  sheet.getRange(1, 1, rows.length , header.length).setValues(rows);
}
```


# 5. 実行

スクリプトの準備ができたら、スプレッドシートを再読み込みすると動きます。
スプレッドシートを開いてしばらく待つと [スクリプト実行] というメニューが現れます。
この中の [メンバーアドレス一覧取得] を選ぶと実行されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/8431d88b-a0b4-318d-a5ed-21c5ebe0c41e.png)


## 5.1. （初回のみ）許可

初回は承認が必要となります。
許可を求められたら、続行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/2ff8be82-0337-7bb6-3804-68e84bb2f90b.png)

アカウントへのアクセスに許可を出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/e2ef25f7-20c8-71a5-2874-a085a9c78a55.png)

下記のものに許可が必要となっています。

- 顧客に関連するドメインの情報 （グループ表示対象となるドメイン一覧取得のため）
- ドメインのグループの表示 （グループ一覧を取得するため）
- ドメインのユーザーに関する情報の参照 （グループメンバー取得のため）
- Googleドライブのスプレッドシートの表示、編集、作成、削除 （シートへの出力のため）

初回、許可をした後は実行自体はされないので、もう一度 [スクリプト実行] から実行します。


## 5.2. 取得確認

実行するとシートを削除してよいか聞かれます。
[OK] を押すと、今開かれているシートの A:D 列の内容が消去され、そこに一覧が現れます。
（書式は消去しません。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/427bcfcc-b3b0-5297-1be4-1a41f809b28d.png)

## 5.3. 結果

動作すれば一覧が表示されます。
DirectMembersCount はグループ内のメンバー数が入ります。
その行数分のメンバーアドレスが取得されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58511/41fc62f0-10a9-cec7-3de5-8bcd80772b82.png)



[^1]: ひさ 氏. "Google Apps Scriptを用いてグループアドレスメンバーを取得". Sanwa Systems Tech Blog. https://tech.sanwasystem.com/entry/2015/04/13/201535 , 2015-04-13.