Slack AtlasをAPIで組織図に反映させた話
好きなゲームメーカーはATLUS!
メガテンやペルソナ、葛葉ライドウが楽しみなケンシロウです。
みんなライドウ買ったらアバチュもあるかもしれないからみんな買ってくれ頼む←
と言うわけで今回はSlack AtlasをAPIで連携させた話です。
SCIMの場合はこの記事を参考にしてもらった方がいいかもです。
目次
- 1.背景
- 2.設計
- 3.実装
- 4.結果
- 5.まとめ
1.背景
弊社では社員数がどんどん増えてきたこともあり、
誰がどの部署で指示系統がどうで〜と言う情報の精査が難しくなっていました。
Slack Atlasを利用した組織図が無い頃はタレントマネジメント的なサービスも無く、
PDFの組織図で管理していたため顔もわからない状態でした。
元々活用しよう!と言う話は内部では出ていたのですが、
人事部から相談があったため実際にやりましょうという話になり現在は運用をしております。
2.設計
設計するに辺り、弊社ではOktaを採用しております。
SCIMで実施しても良かったのですが、組織全体を考えた際にまだIDPやグループウェアが導入されていないような企業もあったりするため、スプレットシート内に記載をして反映させる方向にしました。
基本方針としてコミュニケーションはSlackで統一しましょうという動きを取っていることもあり、
様々なグループ関連会社のワークスペースが存在します。
ちなみにこの記事を書いているタイミングで弊社のオーガナイゼーションに
ワークスペースを移行していて待ち状態なので記事書いてます。
これは今後の課題ではあるのですが、弊社は正としている人事DBが無く、現状PDFの組織図がスタートとなっていることもあり色んなところでデータが連携できない状態になっているため見直しが必要だったのですが、人事DB自体の見直しや準備は結構時間がかかるため一旦後回しにしました。
なので現状の運用する前提として
- グループ全体に展開するためにスプレットシートからのAPI連携
- 人事DBが存在しないため一旦は変更がある度に人事部が追加、変更(ここは今後改修予定)
- SlackのメンバーIDのデータはスケジュールで払い出しをし自動連携
- Slack Atlasへの取り込みも1日の17:00 and 2日の10:00に自動流し込み
このような内容を踏まえて設計をし実装を進めました。
3.実装
Slack側とスプレットシート側に分けてどのような設定をしたか説明していきます。
Slack
Slack APIの設定
Slack APIでアプリを作ります。
Slack Atlas用アプリ
users.profile:wrireを設定します。
OAuth & PermissionsはSlackメンバーID払い出し用アプリ
メンバーIDの払い出しもするのでそのアプリも作りましょう。
OAuth & Permissionsは
admin.users:read
同じ人物で別会社のドメインで登録しているアカウントが存在するため念の為メールも拾います。
この3つを追加したアプリを作ってトークン(xoxp-から始まる)をそれぞれメモっておきます。
Atlas側の設定
ではSlack Atlas側の設定ですが、今回流し込む情報はこのメンバーディレクトリと所属部分です。
Scriptを書くときにカスタムフィールドIDを情報として利用するのでどのカスタムフィールドIDが何の情報と連携しているか確認しておきましょう。
デフォルトの表示名からは変更しております。
右側の編集で変更可能です。
APIになっているのですがこれをSCIMの対象に変更することも可能です。
Google Apps Script
スプレットシートの準備
会社名|所属部署|所属課|役職|名前|SlackメンバーID|直の上長名|上長のメンバーIDという表をスプレットシートを作ります
こことは別にSlackメンバーID参照用のシートを作っておきましょう。
シートを増やして空白で大丈夫です。
SlackのメンバーID払い出しのスクリプト
function getSlackUser() {
const slack_app_token = "xoxp-SlackメンバーID払い出し用アプリのトークン";// SlackアプリのOAuthトークン
const sheetName = 'SlackメンバーIDのシート名'; // シート名を指定
const options = {
"method" : "get",
"contentType": "application/x-www-form-urlencoded",
"payload" : {
"token": slack_app_token,
"include_profile": true
}
};
const url = "https://slack.com/api/users.list";
const response = UrlFetchApp.fetch(url, options);
const members = JSON.parse(response).members;
let arr =[];
for (const member of members) {
//削除済、botユーザー、Slackbotを除く
if (!member.deleted && !member.is_bot && member.id !== "USLACKBOT") {
let id = member.id;
let realName = member.real_name; // 名前を取得
let displayName = member.profile.display_name; //表示名を取得
let email = member.profile.email; // メールアドレスを取得
arr.push([realName, displayName, email, id]); // 名前、表示名、メールアドレス、IDの順で配列に追加
}
}
//スプレッドシートに書き込み
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
sheet.clearContents(); // シートの内容をクリア
// ヘッダー行を追加
sheet.appendRow(['名前', '表示名', 'メールアドレス' ,'メンバーID']);
// データ行を書き込み
if (arr.length > 0) {
sheet.getRange(2, 1, arr.length, arr[0].length).setValues(arr); // 2行目から書き込み開始
}
}
メインの情報を記載して流し込む用のスクリプト
function updateSlackAtlasFromSheet() {
// 設定
const SHEET_NAME = 'Atlasに情報流し込み用シート名'; // データが記載されたシート名
const SLACK_TOKEN = 'xoxp-Atlas反映用に作成したアプリのトークン'; // SlackアプリのOAuthトークン
const COMPANY_FIELD_ID = 'xxxxxxxxxxxxxx'; // 例:会社名のカスタムフィールドID
const DEPARTMENT_FIELD_ID = 'xxxxxxxxxxxxxx'; // 例:部署のカスタムフィールドID
const DIVISION_FIELD_ID = 'xxxxxxxxxxxxxx'; // 例:部門のカスタムフィールドID
const TITLE_FIELD_ID = 'xxxxxxxxxxxxxx'; // 例:役職のカスタムフィールドID
const MANAGER_FIELD_ID = 'xxxxxxxxxxxxxx'; // 例:ダイレクトポートtoのカスタムフィールドID
// スプレッドシートの読み込み
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
const dataRange = sheet.getDataRange();
const values = dataRange.getValues();
// ヘッダー行をスキップ
for (let i = 1; i < values.length; i++) {
const row = values[i];
const company = row[0]; // A列: 会社名
const department = row[1]; // B列: 部署
const division = row[2]; // C列: 部門
const title = row[3]; // D列: 役職
const name = row[4]; // E列: 名前 (必要に応じて)
const slackMemberId = row[5]; // F列: SlackのメンバーID
const managerName = row[6]; // G列: 上長の名前 (必要に応じて)
const managerSlackMemberId = row[7]; // H列: 上長のメンバーID
// Slack APIに送信するデータを作成
const payload = {
user: slackMemberId,
profile: {
fields: {
[COMPANY_FIELD_ID]: { value: company },
[DEPARTMENT_FIELD_ID]: { value: department },
[DIVISION_FIELD_ID]: { value: division },
[TITLE_FIELD_ID]: { value: title },
[MANAGER_FIELD_ID]: { value: managerSlackMemberId },
// 名前は標準のプロフィールフィールドなので、必要に応じて設定
// "real_name": name,
},
},
};
const options = {
method: 'post',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Authorization': 'Bearer ' + SLACK_TOKEN,
},
payload: JSON.stringify(payload),
};
const response = UrlFetchApp.fetch('https://slack.com/api/users.profile.set', options);
const responseJson = JSON.parse(response.getContentText());
if (!responseJson.ok) {
Logger.log(`Error updating profile for user ${slackMemberId}: ${responseJson.error}`);
} else {
Logger.log(`Successfully updated profile for user ${name}`);
}
// APIリクエストのレート制限に配慮して、適度な間隔を置く
Utilities.sleep(1000); // 1秒待機
}
Logger.log('Slack Atlasへの登録処理が完了しました。');
}
あとはメインのAtlasに情報流し込む用のシートに必要な情報を入れていきましょう。
社長、CTO、私の順番で入れてみます。
SlackメンバーIDは払い出ししたシートからVLOOKUPで取得してますが、
記事用にhogehogeとしています。
これで準備完了です。
4.結果
これを流し込むとこのような組織図になります。
人型のアイコンが写真のところにあると思うのですが、この人型アイコンのカウント方法が
例えばBさんがAさんをマネージャーとし、Bさんにダイレクトレポートが2人存在している場合、
Aさんの人型アイコンの人数は1、Bさんの人型アイコンの人数は2という表示になります。
私の場合だとCTOが上司に当たるのでプロフィールを開くとこのような形になります。
共通してスプレットシートの表にある項目がで空白で、
自分で言うところの"役職"ですがそもそも項目としても表示されません。
なので何か値を入れないとエラーになるということはありませんでした。
5.まとめ
じゃあ実際に展開してどうだったかというと、まだ1ヶ月も経っておらずGENDA本社のみの展開ですが、
現場から概ね好評のお声をいただいております。
Slackの運用上、顔写真は登録してもらっているので誰がどの部署、部門でどの人が上司で関わっているのかなどの情報が詳細にわかったことによって、誰に聞けばいいかわからない問題がおおよそ解消されたとの現場からフィードバックをいただきました!
弊社はタレントマネジメント的なサービスはそもそも無かったためSlackでできるのは結構便利かつ、
今後はGENDA全体で展開していきたいと思っております。
とは言え途中お話しした人事DB問題や、関連会社の運用フローどうするかなどの課題は残っているため、ここはどう解消させたか、どういう方向性に舵を切ったかなどはまたいつか…!
ちなみにこれを記事にしながら組織図表示したときに役職も表示されている人たちは
"タイトル"の欄に役職入れてたのでここもAPIであとで流し込んでおこうと思いました!
Discussion