Slackゲストアカウント管理を効率化するbotをGASで作ってみた。
最終更新日 2022/8/16
- ソース及びマニュアル一部修正(不要なトークン削除)
- 参考サイト追加
概要
業務でSlackを使用している会社はユーザの棚卸しや削除漏れを防ぐためゲストに対して有効期限を設定している人は多いと思います。ただ有効期限を設定した場合、延長するのがめんどうだったり期限切れの通知を見逃すことがあったため、Slackの指定チャンネルに有効期限切れをアナウンスするBotを作ってみました。
課題
公式にも期限切れをお知らせする機能はありますが、有効期限を設定した管理者(と対象者)にしか通知されなかったり、2日前(固定)だったりであまり使い勝手が良いとは言えません。(3連休だと休暇明けには解除されてしまいます)
また1人で管理していれば問題ないんですが複数の管理者(情シス)がいる場合、全員に通知がとばないためフォローが出来ないといった状況でした。
解決方法
公式ではSlackbotで対象者にしか通知されないため、指定したチャンネルに通知&指定した日数がせまると自動通知されるbotを作りました。
こんな感じで通知されます。
手順
実際の作成方法を以下に記載します。
1.Slackアプリ作成
1)Slackメニューにて「設定と管理」>「アプリを管理する」クリック
2)ページ右上の「ビルド」クリック
3)Your Appsにて「Create New App」>「From scratch」クリック
4)”Name app & ~”画面にて以下設定後「CreateApp」クリック
5) 左メニュー「Basic Information」のDisplay Infomationnにてアイコンを設定(任意)
6)左メニュー「OAuth & Permissions」をクリック、Scopesにて以下を追加
7)左メニュー「Incoming Webhooks」をクリック`
8)「Add New Webhook to Workspace」をクリックして通知したいチャンネルを選択して「許可する」クリック
9) 画面上部にアプリ再インストールの通知が表示されるので「reinstall your .app」クリック
10)以下画面にて投稿先を選択して「許可する」クリック
10)生成された”Bot User OAuth Token”と”WebhookURL”をコピー
2.GAS登録
1)スプレットシートを新規作成して開く(ファイル名はお好み)
2)メニューから「拡張機能」>「Apps Script」クリック
3)プロジェクトタイトルを設定(お好み)して以下ソースを貼り付け
function getSlackUser(cursor) {
const slack_bot_token = "xoxb-xxxx"; //☆botトークン
const limit =600;
const options = {
"method" : "get",
"contentType": "application/x-www-form-urlencoded",
"payload" : {
"token": slack_bot_token,
"cursor": cursor,
"limit":limit
}
};
const userlist_url = "https://slack.com/api/users.list";
const response = UrlFetchApp.fetch(userlist_url, options);
const members = JSON.parse(response).members;
const guest_members = members.filter((value)=>value["is_restricted"]==true); //ゲストのみにフィルターする
let activeArr = [];
for (const guest_member of guest_members) {
let id = guest_member.id;
let deleted = guest_member.deleted;
let email = guest_member.profile.email;
let real_name = guest_member.profile.real_name; //氏名
let name = guest_member.name; //mei
let is_primary_owner = guest_member.is_primary_owner; //プライマリオーナー
let is_owner = guest_member.is_owner; //オーナー
let is_admin = guest_member.is_admin; //管理者アカウント
let is_restricted = guest_member.is_restricted; //Trueならばゲスト
let is_ultra_restricted = guest_member.is_ultra_restricted; //true ならばシングルゲストチャンネル
let is_bot = guest_member.is_bot; //botユーザー
let is_app_user = guest_member.is_app_user; // アプリユーザー
let guest_expiration_ts = guest_member.profile.guest_expiration_ts;// アカウント有効期限
let guest_expiration_ts_jstTime = Utilities.formatDate(new Date( guest_member.profile.guest_expiration_ts * 1000 ), "JST", "yyyy/MM/dd"); //アカウント有効期限をunix時間からJSTに変換)
//配列に挿入
activeArr.push([ id, deleted,email,real_name, name, is_primary_owner, is_owner, is_admin , is_restricted, is_ultra_restricted, is_bot, is_app_user,guest_expiration_ts_jstTime]);
}
//スプレッドシートに書き込み
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
if( cursor == null){
sheet.clear();
sheet.appendRow(['ID','削除済','メールアドレス','氏名','ユーザー名','プライマリオーナー','オーナー','管理者アカウント','ゲストアカウント','シングルorマルチ','botユーザー','アプリユーザー','アカウント有効期限']);
}
try{
sheet.getRange( sheet.getLastRow()+1,1, activeArr.length, activeArr[0].length).setValues(activeArr);
}
catch(e){
}
if( responseMeta = JSON.parse(response).response_metadata.next_cursor != ''){
getSlackUser(JSON.parse(response).response_metadata.next_cursor);
}
Expiration_Check(sheet); //次のステップへ移動
}
function Expiration_Check(sheet){
const webhookurl = "https://hooks.slack.com/services/hogehoge"; //☆投稿したいチャンネルのWebHook
const lastRow = sheet.getLastRow()
const date = new Date();
const limit = 4 //☆アラートを出す期限(○日前)を指定
const checkday = new Date(date.setDate(date.getDate() + limit));
for( let i = 2 ; i <= lastRow; i++) {
let email2 = sheet.getRange(i, 3).getValue();
let real_name2 = sheet.getRange(i, 4).getValue();
let user_name = sheet.getRange(i, 5).getValue();
let Expiration_date = sheet.getRange(i, 13).getValue();
if(Expiration_date === ''){
Logger.log('有効期限未設定のためスキップ')
}else if(Expiration_date.getTime() >= checkday.getTime()) {
Logger.log('期限内')
}else{
Logger.log('期限切れ')
let Expiration_date_JST = Utilities.formatDate(Expiration_date, "JST", "yyyy/MM/dd"); //unix時間からJSTに変換)
//Slackに投稿
let blocks = [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "以下ゲストユーザの有効期限がまもなく切れます。\n社内IT担当は必要に応じて延長の手続きを行って下さい。"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*メールアドレス:*\n" + email2
},
{
"type": "mrkdwn",
"text": "*氏名:*\n" + real_name2
},
{
"type": "mrkdwn",
"text": "*ユーザー名:*\n" + user_name
},
{
"type": "mrkdwn",
"text": "*有効期限:*\n" + Expiration_date_JST
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "延長する場合は <https://hogehoge.slack.com/admin|ユーザ管理>にて変更を行って下さい" + "\n" + //☆対象ワークスペースのリンクを設定
" 最終ログイン日を確認する場合は<https://hogehoge.slack.com/admin/stats|アナリティクス>から確認出来ます", //☆対象ワークスペースのリンクを設定
}
}
];
let payload = {'blocks': blocks};
let options = {'method' : 'POST', 'payload': JSON.stringify(payload)};
//Slack投稿
UrlFetchApp.fetch(webhookurl, options);
}
}
}
4)ソースの以下変数を自身の環境に置き換えて保存(ソースのコメント欄に☆マークがある部分です)
5)"getSlackUser"にて「▷実行」クリックして動作確認
6) 認証画面が表示されるので権限の認証を実施
※ 以下アラートが出た場合は[詳細]>[安全でないページに移動]クリック
3.タイマー設定
定期的に実行するようにタイマー設定を行います。
1. Apps Script画面の左メニュー「⏰」>[+トリガーを追加]クリック
2. 以下の通り設定して[保存]クリック
妥協した機能
1.最終ログイン日の通知
アナリティクスに表示される「最後にアクティブだった日」を通知して延長有無の判断材料にしたかったんですが、取得できるAPIがログが膨大で、すべてのログを1度に取得出来なかったので、アナリティクスのリンクを置くことで対処しました。(ユーザ単位でフィルター出来るようになったら嬉しいな・・)
2.Slack上で有効期限を延長(無効化)させる
SlackBotにアクション付きのボタンを配置して延長する仕組みを実装予定でしたが、対象のSlackAPIがEnterprise Gridのみでしか使用できないため諦めました(プランあげたい・・・)
3.ユーザ管理で該当ユーザのみ表示させる
Slack上で延長処理が出来ないため、せめてユーザ管理のリンクを設置してクリックすると該当ユーザのみが表示(フィルター)された状態で表示されるようにしたいな〜と思って調べましたが、Google検索のように検索欄に該当ユーザの情報を入れてもURLが変化しない(パラメーター付URLで対処できない)ためこれも妥協しました…
参考にしたサイト
こちらのサイトを参考にさせていただきました。
あと同時期に同じくゲストテーマでふみふみさんがブログを書いていたのでこちらもあわせてご参照下さい。
最後に
現在、副業情シスとして主にスタートアップの支援(ITコンサル等)を行っております。
こちらのサイトに詳細記載しておりますので、もしご興味がある方いればお気軽にお問い合わせ(またはDM等)ご連絡お待ちしております。