Google Apps Scriptでグループアドレスの作成・削除を行う【GAS】

Google Apps Scriptで地味な存在ながら非常に重要な機能を担ってるのが「Googleグループ」。単なるメーリングリストとしての機能だけではなく、Google Driveのアクセス権をまとめて付与する場合にも、このグループのアドレスで管理したりします。

そんなグループアドレスを作成するにはちょっと手間が必要です。

今回使用するスプレッドシート

グループの作成で利用するメソッドは、Google Workspaceの管理者権限が必要です。よって通常のユーザ権限では実行ができないので要注意。今回のコードでは紹介していませんが、Enterprise Standard以上で使える動的グループについては、Admin SDKではなくCloud Identity APIを使って作成が出来るようです。

Google Workspaceの動的グループで楽をしよう【GAS】

事前準備

グループの作成は1つのメソッドではなく、3つのメソッドを使って作成する必要があります。また、その際に利用するクラスは

スクリプトエディタを開き、左サイドバーのサービスの+をクリックして以下の追加を行います。

  1. Admin SDK APIを追加する
  2. GroupSettings APIを追加する

図:2つのサービスが必要です

ソースコード

グループを作成 => グループ設定を変更 => メンバーを追加という3ターンで1グループが作成できます。

グループの作成

//作成
try{
      //グループの作成
      var group = AdminDirectory.Groups.insert({
        email: groupaddress,
        name : groupname,
      });
}catch(e){

}
  • AdminDirectory.Groups.insertで追加します
  • emailがグループアドレスの指定場所
  • nameがグループ名を指定する場所です

グループ設定変更

このグループ作成で一番配慮しなければいけないのが、グループのアクセス設定。一歩間違えると社内のメールが外部の第三者にだだ漏れという事件が嘗てありましたが、それが起こりかねません。また勝手に外部のメンバーがリクエストしてグループに入れるというのもマズイ。

また、内部の人間にむやみに権限を与えて、外部メンバーを追加できてしまうというのもまたマズイということでこれらのアクセスタイプ。これをよく考えてコードで設定する必要があります。

アクセスタイプについて

次のコードのoptionの中身がグループに対する様々なアクセス権を設定するものになります。主によく使うパターンは以下内容になります。

  • whoCanViewMembership(メンバーを表示できるユーザ)
    ALL_IN_DOMAIN_CAN_VIEW = 組織全体
    ALL_MEMBERS_CAN_VIEW = グループメンバー
  • whoCanContactOwner(グループのオーナーに連絡出来るユーザ)
    ANYONE_CAN_CONTACT = 外部含め誰でも
    ALL_MEMBERS_CAN_CONTACT = グループメンバー
  • whoCanViewGroup(会話を閲覧できるユーザ)
    これが一番重要な設定で誤った設定をすると第三者にグループメールの中身が世界中に漏洩することになります。
    ALL_IN_DOMAIN_CAN_VIEW = 組織内なら誰でも
    ALL_MEMBERS_CAN_VIEW = グループメンバー
  • whoCanPostMessage(投稿出来るユーザ)
    こちらも通常はメンバー以外の投稿を認めない設定が良いでしょう
    ANYONE_CAN_POST = 外部含め誰でも
    ALL_MEMBERS_CAN_POST = グループメンバー

    ALL_IN_DOMAIN_CAN_POST = ドメイン内のメンバーならだれでも
  • whoCanModerateMembers(メンバーの管理)
    通常メンバーにメンバーの管理権限は与えません
    OWNERS_ONLY = オーナーのみ
    OWNERS_AND_MANAGERS = オーナーとマネージャのみ
  • whoCanJoin(グループに参加できるユーザ)
    CAN_REQUEST_TO_JOIN = 組織内全員がリクエスト出来る
  • allowExternalMembers(組織外のメンバーの許可)
    false = 外部メンバー追加はさせない
    true = 外部メンバーを追加を許可する
    ※但し、trueでなくとも管理コンソールからは追加できる。しかしGASからはtrueにしておかないと外部メンバーは追加できない

ソースコード

//アクセスタイプを設定
var option = {
  whoCanViewMembership: 'ALL_IN_DOMAIN_CAN_VIEW', 
  whoCanContactOwner: 'ANYONE_CAN_CONTACT',
  whoCanViewGroup: 'ALL_MEMBERS_CAN_VIEW', 
  whoCanPostMessage: 'ANYONE_CAN_POST', 
  archiveOnly: 'false',
  whoCanModerateMembers: 'OWNERS_AND_MANAGERS', 
  whoCanJoin: 'CAN_REQUEST_TO_JOIN', 
  allowExternalMembers: 'true' 
};

try{
  //アクセスタイプ設定
  var accessType = AdminGroupsSettings.Groups.patch(option, groupaddress);
}catch(e){

}
  • optionにて、アクセスタイプを定義します
  • 今回のオプションは社内で利用する場合に推奨されるパターンのオプションとなります。
  • AdminGroupsSettings.Groups.patchにてアクセスタイプを反映させます
  • groupaddressにはグループアドレスを入れます。

図:これがアクセスタイプ

メンバーの追加

AdminDirectoryを使う場合

//メールアドレスを取得
let member = {email: "hoge@tomato.com"};

try{
  //グループにユーザーを追加
  var res = AdminDirectory.Members.insert(member,groupaddress);
}catch(e){

}
  • member変数でJSON形式でメンバーのアドレスを構築
  • AdminDirectory.Members.insertにてメンバーを追加する
  • groupaddressにてグループアドレスを指定する

Cloud Identity Groups APIを使う場合

2020年10月に追加されたCloud Identity Groups APIというものがあるのですが、こちらのAPIを使ってメンバーの追加や更新を行う場合は、対象者のグループ参加有効期限を設定可能です。GUI上や他のメソッドからは出来ない動作になります。このAPIはREST APIとなっているため、GASからはUrlfetchAppで叩いてリクエストしますが、Cloud Identity Premium / Enterprise Standard以上でなければ利用出来ません。

このAPIはGCP側のAPIとなるので、事前準備が必要です。以下の手順で構築します。

※但し、1時間後にexpireと指示してみましたが、削除されず・・・日数単位での指定が必要のようです。

※セットしてる最中は、対象者のロールの変更などは手動で出来ません。

APIを有効にする

Google Cloud Console側でAPIを有効化する必要性があります。

  1. GCPのプロジェクトを開く
  2. 左サイドバーからAPIとサービスにて、「APIとサービスの有効化」をクリックする
  3. Cloud Identityと検索すると出てくるので、クリックします。
  4. 有効化をクリックします。
  5. Admin SDK APIも検索して追加しておきます。
  6. 認証情報の作成は不要です

GASのスクリプトエディタ左側のサービスにて、Admin ADKも追加しておきましょう。

図:有効化をしておくだけでOK

図:サービスとしても追加

GASのプロジェクトを紐付けする

Google Apps ScriptとCloud Consoleのプロジェクトを紐付けする作業が必要です。以下の手順でプロジェクトの変更を行います。

  1. Cloud Console側のプロジェクトのホームを開き、「プロジェクト番号」を控えておく
  2. Google Apps Scriptのスクリプトエディタを開く
  3. サイドバーからプロジェクト設定を開く
  4. GCPプロジェクトの「プロジェクトを変更」をクリック
  5. GCPのプロジェクト番号に、1.の番号を入力して、プロジェクトを設定をクリック
  6. これで紐付けが完了しました。

図:プロジェクトの移動も必須の作業です

appscript.jsonの編集

appscript.jsonに以下のような感じで、oauthScopesを追加して起きます。appscript.jsonの表示は、サイドバーのプロジェクト設定より、「appsscript.json」マニフェスト ファイルをエディタで表示するにチェックを入れると表示されるようになります。GCPのAPIを使う為に必要です。

{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/cloud-identity.groups",
    "https://www.googleapis.com/auth/admin.directory.user"
  ]
}
セットするソースコード

このコードは対象のグループに対して、期限付きでユーザを追加するコードです。すでに追加済のユーザに対しての変更は、こちらのメソッドを利用して更新します。

//GWS顧客IDを取得する
function getCustomerId(){
  var customer = AdminDirectory.Users.get(Session.getEffectiveUser().getEmail())
  return customer.customerId
}

//グループのリソース名を取得する
//groups/xxxxxxといったリソース名を取得するための関数
function getGroupName(groupman){ 
  var url = "https://cloudidentity.googleapis.com/v1beta1/groups?parent=customers/ "+ getCustomerId(); 
  var param = { 
    method : "get", 
    headers : {
      "Authorization": "Bearer " + ScriptApp.getOAuthToken()
    }, 
  }; 

  var pageToken ; 
  do{ 
    var page = JSON.parse(UrlFetchApp.fetch(url,param).getContentText()); 
    if(page.groups && page.groups.length > 0){ 
      for(var i = 0; i< page.groups.length;i++){ 

        if(page.groups[i].groupKey.id === groupman){  
          return page.groups[i].name;
        } 
      } 
    } 
    pageToken = page.nextPageToken 
  }while(pageToken) 
}

//有効期限をセットしたユーザをグループに追加する
function makeGroupUserlimit() {
  //対象のグループアドレス
  let groupaddr = "ここに追加先のグループアドレスを入力"

  //グループ名を取得する
  let groupman = getGroupName(groupaddr)

  //作成エンドポイント
  let endpoint = 'https://cloudidentity.googleapis.com/v1beta1/'+groupman+'/memberships'; 

  //OAuth Tokenを取得
  let token = ScriptApp.getOAuthToken();

  //リミット日付
  const expire = 1;       //指定日数後にExpireする
  const now = new Date();
  
  //指定日数後の日時を算出(日本時間)
  let expiration = new Date(now.getTime() + (expire * 24 * 60 * 60 * 1000));
  let expiretime = Utilities.formatDate(expiration, "JST", "yyyy-MM-dd'T'HH:mm:ss'Z'")

  //リクエストボディ
  let body = {
    "preferredMemberKey": {
      "id": "ここに対象のユーザのメールアドレスを入力"    //対象ユーザメールアドレス
    },
    "roles": [
      {
        "expiryDetail": {
          "expireTime": expiretime
        },
        "name": "MEMBER"
      }
    ]
  }

  //ヘッダ情報
  let header = {
    Authorization: "Bearer " + token
  }
 
  //リクエストオプション
  let options = {
    headers: header,
    method: "POST",
    contentType: "application/json",
    payload: JSON.stringify(body),
    muteHttpExceptions: true
  }
 
  //URLリクエスト
  let response = UrlFetchApp.fetch(endpoint,options);
 
  //レスポンス内容を取得
  let retcode = response.getResponseCode();

  //作成実行の結果
  if(retcode == 200){
    let ret = response.getContentText();
    console.log(ret)
    return ret;
  }else{
    //失敗
    console.log("失敗")
    return false;
  }
}
  • getGroupName関数で、groups/xxxxという形式のグループのリソース名を取得しておきます。
  • getCustomerId関数で、Google Workspaceの顧客IDを取得します(管理コンソール上でも表示されてる)
  • makeGroupUserlimit関数にて、グループリソース名をもってしてURLを構築し、対象ユーザに期限を設定して追加します。
  • 利用するエンドポイントは、「https://cloudidentity.googleapis.com/v1beta1/groups/xxxx/memberships」という形式になります。
  • リクエストボディにn日後のリミットとしてexpireTimeを指定し、このときの日付時刻はJSTで指定すること
  • 成功するとレスポンスコードは200で返ってくる

管理コンソール上のグループにはこれでメンバーが新規追加されますが、表面上リミットを確認する為のUIは存在しません。成功すると以下のような内容がレスポンスとして取得可能です。

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.apps.cloudidentity.groups.v1beta1.Membership",
    "name": "groups/xxxxx/memberships/1111111",
    "memberKey": {
      "id": "対象のユーザのメールアドレス"
    },
    "roles": [
      {
        "name": "MEMBER",
        "expiryDetail": {
          "expireTime": "2024-08-06T19:33:20Z"
        }
      }
    ],
    "preferredMemberKey": {
      "id": "対象のユーザのメールアドレス"
    }
  }
}
expireする日を確認するコード

expireする日を指定することは出来ましたが、管理コンソール上などで失効する日は確認することができません。よって、これはコードでのみ確認することが出来ます。以下のコードの中でグループアドレスと対象者メアドを指定することで、対象者にセットされてる失効日確認することが可能です。

//対象ユーザのexpireする日時を取得
function viewUserExpiration(){ 
  //対象のグループアドレス
  let groupaddr = "ここにグループアドレスを指定する"

  //対象のユーザ
  let email_user = "ここに対象ユーザのメアドを指定する"

//グループ名を取得する
  let groupman = getGroupName(groupaddr)

  //グループリソース
  const resource = 'https://cloudidentity.googleapis.com/v1beta1/'+groupman+'/memberships';
    const param = { 
    method : "get", 
    headers : {
      "Authorization": "Bearer " + ScriptApp.getOAuthToken()
    }, 
  }; 

  //グループメンバーシップを取得
  let pageToken ;
  let membership;
  
  do{ 
      let page = JSON.parse(UrlFetchApp.fetch(resource,param).getContentText()); 
      let tempret = page.memberships;

      if(tempret && tempret.length > 0){ 
        for(let i = 0; i< tempret.length;i++){ 
          let user = tempret[i]; 

          //ユーザのメアドと一致するものを取得
          if(user.preferredMemberKey.id == email_user){ 
            membership = user.name; 
            break; 
          } 
        } 
      } 
      pageToken = page.nextPageToken 
  }while(pageToken)

  //対象ユーザの情報をリクエスト
  const expireurl = 'https://cloudidentity.googleapis.com/v1beta1/'+membership ; 
  const param2 = { 
    method : "GET", 
    contentType : "application/json", 
    headers : {
      "Authorization": "Bearer " + ScriptApp.getOAuthToken()
    }, 
    muteHttpExceptions:true, 
  };

  //expireする日時を取得
  const ret = UrlFetchApp.fetch(expireurl,param2); 

  //レスポンスを取得
  let result = ret.getContentText();

  //レスポンスコードを取得
  let rescode = ret.getResponseCode();

  //ステータスコードで分岐
  if(rescode == 200){ 
    const user = JSON.parse(result);
    console.log('有効期限: '+ user.roles[0].expiryDetail.expireTime); 
  }else{ 
    console.log('エラー: ' + rescode); 
    console.log('詳細: ' + result.split('title')[1]); 
  }
}

グループの削除

作成済みのグループアドレスの削除は非常に簡単です。AdminDirectory.Groups.removeに対してメアドを渡して実行するだけです。

//グループアドレスの削除実行
try{
	let ret = AdminDirectory.Groups.remove(mail);
}catch(e){
	console.log(e.message);
}

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)