こんにちは、 ネクストモード株式会社 の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カードの方が楽なのでこちらにしました
Oktaでユーザーを無効化してから各種SaaSのデプロビジョニングが始まり、System Logに吐き出されるのでフローを一時停止するWait For関数をはさみます
ログの傾向から5分あれば十分でしたので5分のWait timeをはさみました(ここはプロビジョニング連携してるSaaS数によるかと思います)
次にOktaのシステムログから特定のログイベントを検索します
Filterの設定ができるのでEvent TypeとTarget IDをフィルター条件とし、SaaSの数は200個以下なのでResult SetはFirst 200 Matching Recordsとしました
ちなみに入社(オンボーディング)時のEvent Typeはapplication.provision.user.pushを利用します
Map関数を利用して検索したログイベントをリストのアイテム毎にHelperフローに渡して新しいリストに格納していきます
アイテムの処理の順序性については重要ではないため、数が多い場合はConcurrencyを1より大きくして並列処理するのもいいでしょう
ここから子フローを作成していきます
まずHelper Flowにより、親フローから渡されたアイテムを受け取ります
わかりやすく、アイテムの中も書いてますがObject型のSystem Logs(名前は任意)だけあれば大丈夫です
次にObject型のログイベントのアイテムから成功かどうかの情報を取得します
Objectにはヘルパーフローで受け取ったSystem Logsをドラッグアンドドロップで挿入し、pathはRaw Output.outcome.resultとします
次が少しややこしいかも知れないですが、IF/Else関数を利用して下記の通り実施しています
最後にSUCCESSだった場合に取得したdisplaynameを返り値として親フローに戻す
ログイベントの数だけ子フローを呼び出し、取得した値をnew listというlist型に格納します
List型を通知するメッセージで見やすいように / で区切るテキスト型に変換します
Now関数で現在日時を取得し、Date to Text関数で指定フォーマットの日本時間に変換してtext型にします
最後にCompose関数を利用してメッセージを整形してSlack通知します
メッセージ内容は下記の通り設定しました
(例)オフボーディングが完了しました。■ 作業日時
2024/02/01 10:00:01■ ユーザー情報
Yosuke Kusumi■ SaaS情報
Slack / Notion / Asana
ユーザー情報は最初のDeactivate User関数のOkta UserのDisplay Nameから引っ張ってきます
ヘルパーフローの呼び出しを最初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エラーが起こってしまいました
Okta WorkflowsにLogを読み取る権限を付与できていなかったことが原因のようでしたので下記の通り対処しました
Okta管理画面より「アプリケーション」→「Okta Workflows OAuth」→「Okta APIのスコープ」をクリック
okta.logs.readの権限を付与します
以上が躓いたところです、奥が深いですが使えば使うほど慣れてきました!
今回はOktaでDeactivateしたユーザーがSaaS側で自動削除されていることをWorkflowsを使ってSlack通知する方法をご紹介しました
入社時の通知についても別の親フローを作成し、システムログのフィルター条件を変えるだけで子フローは同じものを活用できます
各SaaSに入ってユーザーが作られている/削除されていることを目視で確認したりログをみたりしていた稼働を減らすことで、エレガントになったのではないでしょうか!
Workflowsは今回のケースのように「自動化したい」「エレガントにしたい」と普段思っているところにハマりやすい機能ですので是非活用してみてはいかがでしょうか