はじめに こんにちは、 ネクストモード株式会社 のSaaSおじさん久住です...
【Okta】入退社手続きをOkta Workflowsでエレガントにしたい!
はじめに
こんにちは、 ネクストモード株式会社 のSaaSおじさん久住です
ネクストモードではID統合管理(IDaaS)のOktaを利用して各SaaSへのシングル・サインオンやプロビジョニングの一元管理をしています
今回は従業員の入退社に伴う一連の情シス業務をスマートかつエレガントにすべく、Okta Workflowsの活用を考えてみたのでご紹介します
弊社のオフボーディング(情シス観点)
ネクストモードではOktaでIDを一元管理し、各SaaSにプロビジョニングしているので、社員の入退社があった際も基本的には(一部のSCIMに対応していない or API開放していないSaaSを除いて)SaaS側のユーザーも自動で作成・削除されます
すごく楽な仕組みになっているんですが、定期的な情シス業務改善ミーティングの中で気になることがありました
「プロビジョニング/デプロビジョニングが成功しているかどうかを目視確認(ヨシッ!)している」ということです
エレガントじゃない・・・プロビジョニングの素晴らしさを半減させちゃってる・・・!
そこでこの課題を解決できないか、Okta Workflowsを駆使してみました
やったこと
今回はユーザーが無効(Deactivated)になったことをトリガーに、対象ユーザーのDeprovisioningがSuccessとなったアプリケーション名をOktaのSystem Logsから拾ってきて全部まとめてSlackに通知します
フローは親フローと子フロー(ヘルパーフロー)に分かれていて、親フローで拾ったSystem Logsを子フローに渡して整形しています
親フロー
子フロー
テストしてみるとこんな形でSlackに通知されます
フローの紹介
イベント
Oktaユーザーの無効化をトリガーとしたいため、イベントはDeactivate Userとしました
イベントフックを利用することも可能ですが、Deactivate Userカードの方が楽なのでこちらにしました
アクション
【親】Wait For
Oktaでユーザーを無効化してから各種SaaSのデプロビジョニングが始まり、System Logに吐き出されるのでフローを一時停止するWait For関数をはさみます
ログの傾向から5分あれば十分でしたので5分のWait timeをはさみました(ここはプロビジョニング連携してるSaaS数によるかと思います)
【親】Search System Logs
次にOktaのシステムログから特定のログイベントを検索します
Filterの設定ができるのでEvent TypeとTarget IDをフィルター条件とし、SaaSの数は200個以下なのでResult SetはFirst 200 Matching Recordsとしました
- Event Type:application.provision.user.deactivate
- Target ID:Okta UserのID
ちなみに入社(オンボーディング)時のEvent Typeはapplication.provision.user.pushを利用します
【親】Map
Map関数を利用して検索したログイベントをリストのアイテム毎にHelperフローに渡して新しいリストに格納していきます
アイテムの処理の順序性については重要ではないため、数が多い場合はConcurrencyを1より大きくして並列処理するのもいいでしょう
【子】Helper Flow
ここから子フローを作成していきます
まずHelper Flowにより、親フローから渡されたアイテムを受け取ります
わかりやすく、アイテムの中も書いてますがObject型のSystem Logs(名前は任意)だけあれば大丈夫です
【子】Object Get
次にObject型のログイベントのアイテムから成功かどうかの情報を取得します
Objectにはヘルパーフローで受け取ったSystem Logsをドラッグアンドドロップで挿入し、pathはRaw Output.outcome.resultとします
【子】IF/Else
次が少しややこしいかも知れないですが、IF/Else関数を利用して下記の通り実施しています
- Object Get関数で取得した値がSUCCESSだった場合はdisplaynameを取得し、outputに格納
- Object Get関数で取得した値がSUCCESSではなかった場合はSlackにエラーメッセージを通知し、Return Error関数でError終了
※pathについては後述に詳細書いてますのでご参照ください
【子】Flow Control Return
最後にSUCCESSだった場合に取得したdisplaynameを返り値として親フローに戻す
【親】Map(戻り)、List to Text
ログイベントの数だけ子フローを呼び出し、取得した値をnew listというlist型に格納します
List型を通知するメッセージで見やすいように / で区切るテキスト型に変換します
【親】Now + Date to Text
Now関数で現在日時を取得し、Date to Text関数で指定フォーマットの日本時間に変換してtext型にします
【親】Compose + Slackメッセージ通知
最後にCompose関数を利用してメッセージを整形してSlack通知します
メッセージ内容は下記の通り設定しました
(例)
オフボーディングが完了しました。
■ 作業日時
2024/02/01 10:00:01■ ユーザー情報
Yosuke Kusumi■ SaaS情報
Slack / Notion / Asana
ユーザー情報は最初のDeactivate User関数のOkta UserのDisplay Nameから引っ張ってきます
詰まったところ
For Each・Map・Reduce
ヘルパーフローの呼び出しを最初For Each関数を使おうとしていたのですが、それだと返り値を設定できず、悪戦苦闘していました
結果的にMap関数でうまくいったのですが、今回For Each関数、Map関数、Reduce関数の違いを正しく知るところから大変でした
データの型
以前も大いに詰まって、今回もちょっと躓いたのは型の問題です
Object型なのか、List型なのか、ここを意識して繋げていかないとエラーになります
今回は下記のようなList型のSystem LogsからObject型の下記アイテムを取得していきました
システムログ(List型)
[ログアイテム①(Object型), ログ②(Object型), ・・・]
各ログアイテムについては子フローにて必要な値(今回はターゲットのSaaS名)のみ取り出したのですが、これも癖がありました
ログアイテム(Object型)
各ログアイテムは下記のようにネストされた(=オブジェクトの中にオブジェクトが内包されている)オブジェクトから成り立っており、必要な値を取り出すためにはpathの指定に工夫が必要になります
Targetの3番目のdisplayNameのSlackを取得したい場合は配列Target[2]に格納された情報を取得することになるため、pathにTarget.2.displayNameと指定します
Search System Logsカード使用時の403エラー
出来上がって実際に動かしてみたところ、Search System Logsで403エラーが起こってしまいました
Okta WorkflowsにLogを読み取る権限を付与できていなかったことが原因のようでしたので下記の通り対処しました
Okta管理画面より「アプリケーション」→「Okta Workflows OAuth」→「Okta APIのスコープ」をクリック
okta.logs.readの権限を付与します
以上が躓いたところです、奥が深いですが使えば使うほど慣れてきました!
最後に
今回はOktaでDeactivateしたユーザーがSaaS側で自動削除されていることをWorkflowsを使ってSlack通知する方法をご紹介しました
入社時の通知についても別の親フローを作成し、システムログのフィルター条件を変えるだけで子フローは同じものを活用できます
各SaaSに入ってユーザーが作られている/削除されていることを目視で確認したりログをみたりしていた稼働を減らすことで、エレガントになったのではないでしょうか!
Workflowsは今回のケースのように「自動化したい」「エレガントにしたい」と普段思っているところにハマりやすい機能ですので是非活用してみてはいかがでしょうか