こんにちは!たつみんです!
ちょうど1週間前にOkta City Tour Tokyoで発表がありましたが、Okta WorkflowsにSmartHR Connectorがリリースされましたので検証してみました。
なお、OktaとSmartHRは2022年3月にもSCIM連携についてもリリースしています。その際の検証は以下の記事でご紹介しています。比較のためにこちらもぜひご参照ください。
はじめに
SCIM連携と今回のOkta Workflowsでの連携とはどのような違いがあるかを簡単にご紹介いたします。
まず、プレスリリースにあるように扱える属性が増えています。またSCIM連携では難しかったImportするアカウントを絞り込んだり、タイミングを調整することがOkta Workflowsでは処理を行う対象を絞ったり入社日や退職日をキーにn日前/後に処理を行うということをしやすくなるという点があります。
この検証ブログでは以下を実現できるようにOkta Workflowsを作成し、SmartHR Connectorに関する部分を中心に解説します。
- SmartHRに登録されたユーザーの入社日の7日前になったらOktaユーザーアカウントの作成(日時処理)
- SmartHRに登録されたユーザーの退職日の翌日になったらOktaユーザーアカウントの無効化(日時処理)
- SmartHRに登録されたユーザーの部署情報が更新されたらOktaユーザーアカウントの部署情報を更新(即時処理)
事前準備
SmartHR側の部署情報の定義
今回の検証では以下のような本部,部,チームというイメージで最大3階層となる部署情報をあらかじめ定義し、検証環境のユーザーに設定をしました。
SmartHRでは以下のように1ユーザーに対して最大3個の部署情報を付与することができます。
Okta側の部署情報
Okta側で標準で用意されている部署情報を格納する属性はOrganization,Division,Departmentです。
今回はOrganizationにSmartHRで定義した本部を、Divisionに部を、Departmentにチームを割り当てることとしますが、SmartHRで表現できる部署情報はあと2セット分が必要です。そこで以下のようにカスタム属性として追加しました。
カスタム従業員項目の追加
SmartHRの基本情報は以下のようにヨミガナは存在しますが、アルファベット表記がありません。また、メールアドレスも入社前に招待された場合はプライベートなメールが設定さている場合が多いと思います。
Oktaのアカウントを作成するためには、First name,Last name,Username,Primary emailが必須ですが、First name,Last nameは非ASCII文字は利用したくないためアルファベット表記が必要です。
Username,Primary emailは共にEmail形式となり基本的に同一の値を登録しますが、これらもSmartHRの基本情報のメールアドレスは前述のとおり利用できないことを想定して、カスタム従業員項目として作成しておきます。
これらの項目を以下のように、First name,Last name,Company Emailとして設定しました。
入社前で会社として発行したメールアドレス(Oktaアカウント)が存在しない場合は以下のようにFirs nameおよびLast nameを登録しておく必要があります。入社済みの場合は発行済みのメールアドレス(Oktaアカウント)を登録しておきます。
Okta Workflowsで入社済みのユーザーに対する処理はこのCompany EmailをキーにOktaユーザーと紐づけて処理を行います。
SmartHR側のアクセストークンの発行
Okta WorkflowsでSmartHR Connectorを設定する際に必要となるアクセストークンを発行します。
SmartHRでは口座情報や雇用保険の被保険者番号など様々な項目が用意されていますが、これらは情シスとしては読み取りすら不要な項目なので、今回の検証では部署情報など必要最小限の項目にのみに読み取りの権限を付与します。
また、上記カスタム従業員項目で作成したCompany EmailについてはOktaユーザーアカウントを作成した際にSmartHRに書き込みをする想定なのでこれにだけは更新権限を付与しておきます。
これらを考慮するとおそらく必要となるのは以下のような項目になるかと思います。user_id,last_name,first_name,emp_status,entered_at,resigned_at,position,employment_type,departments
そしてカスタム従業員項目として追加したfor Okta.First name,for Okta.Last name,for Okta.Company Emailです。
SmartHR側のWebhookの登録について
以下のように設定をします。この時、入力すべきURLとシークレットはOkta WorkflowsでAPI Endpointを追加した際に生成されるInvoke URLとClient Tokenを利用します。
またこの際に変更されたデータのIDのみ送信するにチェックを入れておきます。これでWebhookが発動した時に本来閲覧してはいけない情報を含まずに送信されます。必要な情報は処理の中でアクセストークンを通じて取得します。
Okta Workflowsの設定
概要
今回作成したのは1個のTable1と4個のFlowで構成される少し複雑な内容となっています。
- データ蓄積用テーブル
- Flow1:1日に1回実行し、SmartHRの登録情報を検索
- Flow2:Flow 1の情報を受け取って入退社の処理を実施
- Flow3:SmartHR上の更新情報を受け取ってOktaの属性をアップデート
- Flow4:Flow 2,Flow3から呼び出されれ、SmartHRの部署情報の階層情報を再構築し、呼び出し元に返す
テーブル作成
データを蓄積するためにテーブルを作成しますが、これは絶対に必要というわけではありません。主にデバッグ用だと考えてください。
カラムが多いため、一部のみをスクリーンショットとして掲載します。
0_Organization,0_Division,0_Departmentに部署情報の1つ目が格納されます。さらに2つ目と3つ目の部署情報を格納するための1_Organization,1_Division,1_Departmentと2_Organization,2_Division,2_Departmentもカラムとして用意しておきます。
Flow1:1日に1回実行し、SmartHRの登録情報を検索
Flow1は3つのカードで構成されています。
- Scheduled Flowカードにて、1日1回深夜1:00に実行されるように設定
- TablesのClear Tableカードにて、テーブルを初期化
- SmartHR Search Employeesカードにて、登録情報を検索
SmartHR Connectorの初回設定について
Okta WorkflowsでSmartHR関連のカードを利用する場合に初回のみ以下のように設定を行います。今回はSmartHRが検証環境であるため、Account TypeでSandboxを選択していますが、本番環境の場合はProductionを選択しましょう。
Tenant IDはSmartHRのサブドメイン部分を入力し、Access Tokenは事前準備のSmartHR側のアクセストークンの発行で発行した値を入力します。
詳細は以下のOktaのドキュメントをご参照ください。
SmartHR Search Employeesカードの詳細解説
今回の検証では特に条件を絞らずにすべての情報を取得するようにしています。
そのため、検索結果をどのように扱うかのオプションでStream Matching Recordsとしています。他のオプションとして、First Matching RecordやFirst 100 Matching Recordが用意されていますが検索結果として複数ヒットする場合や100件以上が想定される場合はStream Matching Recordsが現実的な選択肢となります。
ここでは取得した情報を別のFlowに渡すためにFlow2を指定しています。そのため実は最低限Flow2のトリガーカードであるHelper Flowを先に作っておく必要があります。
Flow2:Flow 1の情報を受け取って入退社の処理を実施
以下が全体像です。このFlow2は処理が多いため画像としては3枚ですがこれらが繋がっています。
フローチャート機能で出力される図としては以下のようになります。
If/Elseifで条件分岐しており、さらにその内部でIf/Elseにて条件分岐していることが表されています。
トリガーからSmartHRで登録情報の取得
- トリガーとしてはHelper Flowカードを設定
- Flow1からデータを受け取るためにオブジェクト型の変数名Recordを設定
- Get Multipleにて、先ほどのオブジェクトを指定し、Record.Employee IDにてSmartHRのIDを取得
- SmartHR Read Employeeカードにて、取得したSmartHRのIDを利用し、必要な項目を取得
SmartHR Read Employeeカードの詳細解説
Read Employeeで取得できる部署情報とカスタム従業員項目はそれぞれリスト型オブジェクトです。後段の処理で工夫してうまく扱う必要があります。
入社日に関する処理
以下の4つのカードでは日付に関する処理を行っています。
- Date & TimeのNowカードにて、現在の日時情報を取得
- 次のDate & TimeのDate To Textカードにて、タイムゾーンをJSTに変換
- Date & TimeのSubtractカードにて、SmartHRから取得した入社日に対して7日を引いた値を作成
- これも次のDate & TimeのDate To Textカードにて、タイムゾーンをJSTに変換
カスタム従業員項目の取り出し
- List Index Byカードにて、項目名と値のペアに再構築
- カスタム従業員項目として項目を3個作成していますが、リスト内の順序が順不同で出力される場合があり、0,1,2などの添字を利用できない事への対処するための処理
- Get Multipleカードにて、項目名を指定し値を取得
部署情報を再構築とテーブルへの反映
- Mapカードにて、部署情報をFlow4へ渡し、再構築されたリストを受け取る
- Flow4で行っている処理については後段で詳細を記載します。
- Get Multipleカードにて、部署情報を取得
- Create Rowカードにて、これまでに取得した項目を利用し行を作成
入社日と退職日による条件分岐
- If/Elseifカードにて、ひとつ目の条件判定として入社日の7日前である場合を指定
- 次の条件判定として退職日の入力がある場合を指定
入社日の7日前である場合
- Composeカードにて、First nameとLast nameからメールアドレスを作成
- To Lower Caseカードにて、すべて小文字に変換
- Randomカードにて、8文字のパスワードを生成
- OktaのCreate Userカードにて、これまで作成した情報を利用しユーザーを作成
- SmartHR Update Employeeカードにて、Company Emailに対して作成したメールアドレスを反映
- Update Rowカードにて、テーブルに対しても作成したメールアドレスを反映
- Composeカードにて、作成したアカウント情報をSlackに投稿するためのメッセージを作成
退職日の入力がある場合
- Date & TimeのAddカードにて、退職日に1日追加
- Date & TimeのDate to Textカードにて、タイムゾーンをJSTに変換
- If/Elseカードにて、退職日の翌日が当日であるかを判定
- 当日である場合にOkta Deactive Userカードにて、OktaユーザーをDeacitvateとする処理
- Composeカードにて、Deactivateしたアカウント情報をSlackに投稿するためのメッセージを作成
- 退職日の翌日が当日でない場合はRetunカードにて、呼び出し元のFlow1に戻る
Slackへメッセージの送信
- Continue Ifカードにて、Slackへ投稿するメッセージが空でないことを確認
- Slack Send Message to Channelカードにて、チャンネルIDを指定しメッセージを送信
Flow3:SmartHR上の更新情報を受け取ってOktaの属性をアップデート
以下が全体像です。このFlow3も処理が多いため画像としては2枚ですがこれらが繋がっています。
フローチャート機能で出力される図としては以下のようになります。
要所要所でContinue Ifで判定をしていますが分岐処理自体は存在しないシンプルな構成です。
トリガーからSmartHRのRead Employeeで登録情報を取得するまでの処理
- トリガーとしてはAPI Endpointカードを設定
- </>からInvoke URLとClient Tokenを取得し、SmartHRのWebhookの登録画面で設定
- bodyに含まれるcrew idやeventを指定
- Continue Ifにてeventがcrew_updatedを指定し、従業員情報が更新された場合のみ後続の処理を実行
- SmartHR Read Employeeカードにて、取得したSmartHRのIDを利用し、必要な項目を取得
カスタム従業員項目の取り出しと部署情報のリストの加工
この処理はFlow2で行った処理とほぼ同等です。
- List Index Byカードにて、項目名と値のペアに再構築
- カスタム従業員項目として項目を3個作成していますが、リスト内の順序が順不同で出力される場合があり、0,1,2などの添字を利用できない事への対処
- Get Multipleカードにて、項目名を指定し値を取得
- Okta Read Userカードにて、メールアドレスからOktaユーザーIDとステータスを取得
- Continue Ifカードにて、OktaのユーザーステータスがDEPROVISIONED以外である場合のみ後続の処理を実行
- Mapカードにて、部署情報をFlow4へ渡し、再構築されたリストを受け取る
- Get Multipleカードにて、Mapカードで加工したリストから部署情報を取得
Oktaユーザーのアップデートからテーブルの更新
- Okta Update Userカードにて、OktaユーザーIDを指定し部署情報を更新
- この検証ではOktaに反映する項目として部署情報のみを想定
- Search Rowsカードにて、Emailカラムを検索し、Row IDを取得
- Update Rowカードにて、Row IDをキーに情報を更新
Flow4:Flow 2,Flow3から呼び出されれ、SmartHRの部署情報の階層情報を再構築し、呼び出し元に返す
以下が全体像です。
以下がフローチャートです。このFlowでは複数の条件分岐が設定されています。
トリガーから部署情報の取得するまでの処理
- トリガーとしてはHelper Flowカードを設定
- Flow2およびFlow3からデータを受け取るためにオブジェクト型の変数名Recordを設定しておきます。
- Get Multipleカードにて、Name,Parent.Name,Parent.Parent.nameを指定し値を取得
実際の部署情報と取得される値について
部署情報が3階層すべて満たされている場合
例えば、情報システム部/情報システム部/ヘルプデスクチームのような場合は以下のように取得できます。
- Name=ヘルプデスクチーム
- Parent.Name=情報システム部
- Parent.Parent.name=情報システム部
経営管理本部/人事部のような部付けの場合は以下のように取得できます。
- Name=人事部
- Parent.Name=経営管理本部
経営管理本部のような本部付けの場合は以下のように取得できます。
- Name=経営管理本部
つまり本部,部,チームが必ずParent.Parent.name,Parent.Name,Nameの順番とはならずに、値が設定されている部署情報の最下層がNameに割り当てられ、そこから遡るような構造となります。
値の有無による部署情報の階層構造の判定
- If/Elseifカードにて、Parent.Parent.nameに値が入っている場合は部署情報がチームまで存在すると判定
- Contractカードにて項目名OrganizationにParent.Parent.nameを、DivisionにParent.Nameを、DepartmentにNameを指定しオブジェクトを再構築
- デバック用にGet Multipleカードにて、それぞれの値を取得
- Returnカードにて、呼び出し元Flowに再構築した部署情報を戻す
- If/Elseifカードにて、Parent.Parent.nameに値が入っていない場合にさらにParent.Nameが値が入っている場合は部署情報が部まで存在すると判定
- Contractカードにて項目名OrganizationにParent.Nameを、DivisonにNameを指定しオブジェクトを再構築
- If/Elseifカードにて、Parent.Parent.nameに値が入っていない場合にさらにParent.Nameが値が入っていない場合は部署情報が本部まで存在すると判定
- Contractカードにて項目名OrganizationにNameを指定しオブジェクトを再構築
実行結果
テーブルには以下のように入力されます。
Slackへの通知は以下のスクリーンショットのうち、01:01の部分です。また01:10の部分は以前執筆したブログのFlowによって通知しています。
今回Oktaで新たにアカウントが作成されたユーザーのSmartHRのカスタム従業員項目のCompany Emailにも値が反映されています。
作成されたOktaユーザーには部署情報も反映されいます。
Slack通知の01:10でユーザーが作成と同時にOktaグループ Helpdesk team Groupに追加されていることも確認できます。
これは以下のようなGropu Rulesによってどのdapartmentに値が入っていてもアサインするように設定しているため、自動的に処理が行われます。
Flow3が実行された場合はFlow内部の処理ではSlackにメッセージを送信していませんが、部署情報が変化し、Group Ruleによってグループのアサインが変更になった場合は以前執筆したブログのFlowによって通知される仕組みとなっています。
これは部署情報を更新したという情報よりそれによってGroup Ruleが適用され所属するOktaグループが変化した結果の通知のほうが大事であるという考えからです。
最後に
SmartHR Connectorの検証のつもりが入退社や部署異動を想定した実践的なユースケースに基づき作り込んでいく作業にいつのまにか没頭していました。データ構造の把握やAPIの範囲、Webhookの使い所など多くの要素が必要なため勉強になりました。
今回、入退社処理から部署異動の反映まで一通りの検証ができましたが、実務で利用するには発行するメールアドレスの重複チェックや部署情報以外の属性を活用の検討などが必要かと思います。
もちろん入社前に誰がカスタム従業員項目を入力するのかであったり、どのタイミングでSmartHRで退職日時を更新するのか?などの運用ルールを決めておく必要が必ず発生します。
ちなみに部署情報はSmartHRでは予約管理機能により、従業員情報の更新予約が可能です。
SCIM連携よりできることが増えた分、作り込んでいくのが大変ではありますがチャレンジする価値はあると思います!
それではまた別の記事でお会いしましょう?