Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

組織改編に強いグループ運用を作る:人事データ起点のOkta×Googleグループ自動化

この記事は「EXをシンプルにする」に向き合う 情シスブログリレーの第2回です。前回の記事は、こちらです。

はじめに

組織改編のたびに、各組織の担当者がGoogleグループのメンバーを手作業で入れ替える。おそらく多くの会社で見られるこの運用を、SansanのIdPであるOktaを中心とした、Okta User Profileベースの自動化に置き換えました。結果として、各組織で対応していたこの運用作業をなくすことができています。

こんにちは。Sansan技術本部 コーポレートシステム部の平野圭人です。

私が所属するコーポレートシステム部は、情報システム部門(いわゆる情シス)にあたります。部のミッションとして掲げているのが「EXをシンプルにする」というものです。EXとはEmployee Experience(従業員体験)のことです。
私たちは「EXをシンプルにする」というミッションのもと、従業員が日々利用する社内システム、SaaS、クラウド環境の設計・開発・運用を担当しています。

過去にこちらの記事で、OktaをTerraformでIaC化する取り組みについて書きましたが、今回はその延長線上のテーマとして、OktaとGoogleグループの運用自動化についてご紹介します。IdP(Okta)とGoogle Workspaceを運用している方や、人事データとグループ管理の連携に悩んでいる方の参考になればうれしいです。

なぜグループ運用の自動化に取り組んだのか?

Sansanでは社員数も利用するサービス数も年々増えており、各サービスに存在するグループの設計・運用は複雑になっていました。手動を前提とした運用ではもうスケールしない、というのが正直なところです。特に大きかった課題は次の2つでした。

1. 社員登録からグループアサインまでの作業が人手依存

組織改編・入退社・異動のたびに、グループの追加・削除やメンバー更新が発生します。組織改編が重なる月初には作業が集中し、漏れやミスのリスクも常につきまといます。

2. 運用経路と命名規則の混在

グループの作成・メンバー更新経路として、OktaのGroup RuleAWS LambdaGoogle Apps Script(GAS)を利用した自動化処理、依頼ベースの手作業、個別運用などが併存しており、同じような運用でも作業方法などがバラバラでした。命名規則も用途ごとに揺れていたため、「このグループは何のために存在するのか」を調べるだけでも一苦労、という状態です。

このまま放置すれば、権限ガバナンス(最小権限・監査性)と運用品質がじわじわ崩れていき、説明コストや修正コストが積み上がっていくのは目に見えています。そこで、グループ運用そのものを設計から見直すことにしました。

ゴールと非ゴール

取り組みにあたって、今回のゴールを次の2つに整理しました。

  • 人事データ(Workday/CDB)由来の組織や人の属性情報に基づき、Oktaグループの作成からGoogleグループへのメンバーアサインまでを自動化し、組織改編や人事イベントが起きてもスケールするグループ運用を実現する
  • 権限ガバナンスの強化(最小権限・監査性)と運用効率化(工数削減・ヒューマンリスク排除)を同時に達成する

一方で、Okta/Google Workspace外の他サービスにおけるサービス内グループ設定までの統合管理は、今回のスコープからは外しました。ここまで一度に狙うと取り組みが発散してしまうため、まずはOktaとGoogleグループに絞っています。

※CDB(Corporate DataBridge)は、Corporate領域の各種データをREST API経由で一元的に扱えるSansanの内製データ連携基盤です。詳細はこちらの記事を参照してください。

現状整理(As-Is)

設計に入る前に、既存グループの用途分類、命名の揺れ、運用経路(誰が・何で更新しているか)、実態との乖離が起きるパターンを棚卸ししました。関係者へのヒアリングや設定の確認、既存の作成履歴の調査を通じて、「どこで何が決まり、どこに反映されるのか」というデータフローと責務を一つずつ可視化していきました。

地道な作業ですが、この整理のおかげで「組織にひも付くグループは標準化して自動化する」「組織にひも付かないグループは従来経路で管理する」という境界を引けるようになりました。ここを曖昧にしたまま進めていたら、後の設計はもっと迷走していたと思います。

設計(To-Be)

設計原則

判断に迷ったときに立ち返れるよう、最初に5つの設計原則を決めました。

  • 変更耐性:組織改編・入退社・異動など「人と組織が動く」前提で破綻しないこと
  • 再現性:IaCを前提に、誰が適用しても同じ状態を作れ、差分が追えること
  • 監査可能性:誰にいつ何が付与されたかを追跡できること
  • 最小権限:グループの意味が崩れないよう過剰付与を防ぐこと
  • 運用可能性:例外対応・障害対応・棚卸しが現実的なコストで回ること

全体データフロー

Workday/CDB(組織階層)→ Okta User Profile(ユーザー属性として保持)→ Oktaグループ(Group Ruleで属性ベース自動アサイン)→ Group PushでGoogleグループと1:1同期、という一方向の流れに統一しました。データの流れを一方向にそろえたことで、不整合が起きたときに「どこを疑えばよいか」が分かりやすくなっています。

※Group Pushは、Oktaグループに対応するグループを連携先アプリ(今回はGoogle Workspace)側に作成し、メンバーの変更をOktaから一方向で同期し続けるOktaの機能です。

Okta User Profile設計

CDBからユーザーの所属部署コードを管理する departmentCode(主務部署コード), subDepartmentCode(兼務部署コード)の属性をOkta User Profileへマッピングします。この属性の源泉はWorkday上の人事データです。人事側で組織や所属が変わるとCDB経由でOkta User Profileに反映されるため、ここが今回の自動化におけるすべての起点です。

グループ標準パターン

  • 組織グループ:all_employeediv_<div>hq_<div>_<hq>dept_<div>_<dept>grp_<div>_<dept>_<group>
    • 全社員、事業部単位、統括部単位、部単位、グループ単位のように各階層ごとに固定のプレフィックスを付与

命名は小文字英数字と _- のみ・64文字以内など、Google側の制約に合わせて統一しています。

自動アサイン設計(Okta Group Rule)

departmentCode でどの組織グループに所属するかを判定します。ポイントは、部署名ではなく部署コードを判定キーにしたことです。これにより、名称変更だけならグループの再作成が不要になり、組織変更に強い構造になりました。実際には、次のような条件式でユーザーを組織グループに振り分けています。

// 実際の振り分けルール
// departmentCode(string型):主務部署コード
// subDepartmentCode(string型の配列):兼務部署コードのリスト
// ${departmentCode} には振り分け対象の部署コードが入る

String.startsWith(user.departmentCode,"${departmentCode}")||String.stringContains(Arrays.toCsvString(user.subDepartmentCode),"${departmentCode}")

== ではなく String.startsWith で判定しているのは、部署コードが階層構造になっているためです。前方一致にすることで、所属組織のグループだけでなく、上位部署のグループへのアサインもこの1本の式で実現しています。例えば、部署コード・部署と組織グループの対応は次の通りです。

部署コード 部署 組織グループ
A1_1111_0000_0000_0000 技術本部 div_eng
A1_1111_0000_2222_0000 技術本部 コーポレートシステム部 dept_eng_corps
A1_1111_0000_2222_3333 技術本部 コーポレートシステム部 ITSMグループ grp_eng_corps_itsm

なお、Rule生成時に式へ渡すのは、末尾の _0000 ブロックを削ったプレフィックス(dept_eng_corps なら A1_1111_0000_2222)です。これにより、配下組織の部署コードとも正しく前方一致します。

OktaのGroup Ruleとグループ定義はTerraformで管理します。また、本仕組みで作成された同期グループ(Group PushでGoogleグループと同期しているグループ)への手動アサインは一律禁止する運用としました。

実装

全体像

日次では、Okta User Profile更新機能がユーザーの属性を更新すると、Group Ruleがその属性に基づいてユーザーを各グループに自動アサインし、Group PushがGoogleグループへ同期します。
月次では、月次更新処理がCDBに連携された組織改編に合わせてOktaグループとGroup RuleをTerraform経由で更新し、Googleグループの作成・設定までを自動で追従させます。

人事データからGoogleグループまでの連携フロー

前提

  • OktaとGoogle WorkspaceはSAML連携済みかつProvisioning設定済み
  • Okta Google WorkspaceアプリのPush Group Ruleで特定のグループ名プレフィックスでの自動Pushを設定済み

月次処理フロー

  1. 月末月初の前後5営業日中、毎日実行してCDBから組織改編後の組織一覧を取得する
  2. 前月分と比較し、追加・変更・削除対象の組織を抽出する
  3. 対応するOktaグループ/Group RuleのTerraform定義(JSON)を自動生成してPRを作成
  4. 上記のPRマージをトリガーにTerraform Applyが実行されOktaに設定を反映
  5. Okta Group PushによりGoogle Workspace上にGoogleグループが自動作成される
  6. Okta Event HookでGroup Pushイベントを検知し、それをトリガーに起動する処理(AWS Lambda)が、作成されたGoogleグループの設定を標準値(外部メンバー不可・脱退不可・組織外メール利用不可など)へ更新する
  7. 月中の入社・異動は、日次のOkta User Profile更新と既存Ruleで自動追従する

実行期間に「月末月初の前後5営業日」と幅を持たせているのは、人事の組織Fixが月初にずれ込む場合があるためです。また現状では、ステップ3・4でリソースの削除・変更を伴う場合、人のレビューを挟む運用にしています(背景は後述します)。

このほか、依頼ベースのグループ作成には all_div_ などの予約語を使わせないガードも追加しています。

参考として、月次処理で自動生成している定義のイメージも載せておきます。下記のJSONを自動生成し、Terraformの okta_groupokta_group_rule リソースに渡しています。

# Okta Group
"groups": [
    {
      "resource_name": "A1_1111_0000_0000_0000",
      "name": "div_eng",
      "description": "技術本部",
      "department_code": "A1_1111_0000_0000_0000"
    },
    {
      "resource_name": "A1_1111_0000_2222_0000",
      "name": "dept_eng_corps",
      "description": "技術本部 コーポレートシステム部",
      "department_code": "A1_1111_0000_2222_0000"
    }, ...
]

# Okta Group Rule
"group_rules": [
    {
      "resource_name": "A1_1111_0000_0000_0000",
      "name": "org_rule_A1_1111_0000_0000_0000",
      "status": "ACTIVE",
      "assignment_groups": [
        "div_eng"
      ],
      "expression_value": "String.startsWith(user.departmentCode,\"A1_1111\")||String.stringContains(Arrays.toCsvString(user.subDepartmentCode),\"A1_1111\")"
    },
    {
      "resource_name": "A1_1111_0000_2222_0000",
      "name": "org_rule_A1_1111_0000_2222_0000",
      "status": "ACTIVE",
      "assignment_groups": [
        "dept_eng_corps"
      ],
      "expression_value": "String.startsWith(user.departmentCode,\"A1_1111_0000_2222\")||String.stringContains(Arrays.toCsvString(user.subDepartmentCode),\"A1_1111_0000_2222\")"
    }, ...
]

運用でこだわったこと

組織にひも付くグループはTerraformで生成・更新されるため、通常の組織改編で人手はほぼかかりません。また、同期グループは特定個人をOwnerにしない方針とし、退職時の作業や個人への依存を減らしました。メール利用についてもルールを決めています。同期グループ自体は組織外メール利用不可の設定とし、組織外とのメールのやりとりに使いたい場合は、メールアドレス用のグループを別に作成し、そこに同期グループを入れ子にして使ってもらう運用です。この組織外メール利用不可をはじめとするポリシーの適用は、構成図のGoogleGroup設定更新処理(月次処理フローの手順6)が担っています。組織改編で同期グループが削除・再作成されても、メールアドレス用のグループは影響を受けないため、メールアドレスの消失リスクを避けられます。

障害や不整合が起きたときは、データ起因(人事データ)→ ルール起因(Group Rule)→ 同期起因(Group Push)の順で切り分けるようにしています。

得られた効果

まず数字の面から。各組織で、組織改編やメンバー異動のたびに実施していたメンバー入れ替え作業が不要になりました。約500組織 × 1組織あたり約5分(メンバーの入れ替え操作と反映確認にかかるおおよその時間)と見積もると、約2500分(約40時間)/月の削減になります。

数字に表れにくい部分では、属性とルールに基づく自動制御に移行したことで、運用負荷の削減と権限ガバナンスの強化を両立できたのが大きな収穫でした。IaC管理により、誰が作成しても同一の設定・管理状態になる再現性も確保できています。

選ばなかった技術と、その理由

今回の取り組みを振り返ると、何を作ったかと同じくらい「何を選ばなかったか」に学びが詰まっていました。代表的な3つと、進めてみて難しかったポイントを紹介します。

Push Group APIの直接制御

Googleグループとの同期は、Push Group APIを直接制御する方式も検討しました。ただ、APIの直接制御では「どのOktaグループをどのGoogleグループへ同期させるか」というマッピングの状態を自前で管理し続ける必要があります。事前検証の結果、グループ名プレフィックスの宣言だけでこの管理自体が不要になるPush Group Rule方式のほうが、運用もシンプルになると判断してこちらを採用しました。

Okta Workflows

一番迷ったのはここです。Group Pushイベントを受けてGoogleグループの設定を更新する処理は、Okta Workflowsでも実現できます。ノーコードで完結する手軽さは魅力だったのですが、次の3点から今回は見送り、Okta Event Hook + AWS Lambdaで実装しました。

  • Integrationは豊富だが、各Integration内で細かい設定ができない
  • 修正がUIベースになり、属人化が発生するリスクがある
  • 設定をコード管理できないため、リリース時の作業ミスリスクがある

IaCで再現性を担保するという設計原則に照らして、コード管理できないことが決め手になりました。

完全自動化

グループの削除・変更まで含めた完全自動化も、技術的には可能でした。ただ、グループが消えるような操作は影響が大きく、リスクに見合わないと判断して「人のレビューを挟む」境界を意図的に残しています。

進めてみて難しかったこと(ハマりポイント)

全体としては狙いどおりに機能した一方で、実際に進めてみるとつまずいたところもありました。

  • 兼務の扱い:部署コードを判定キーにする設計の前提として、兼務など当時CDBで取得できなかった属性は、人事部門と連携して整備する必要がありました
  • プラットフォームの制約:Okta User Profileのサイズ制約やGoogleグループの命名要件(64文字以内など)が、属性・命名設計の自由度を狭めました
  • 同期グループのメール設定:前述のメールアドレス用のグループに同期グループを入れ子にする構成では、当初、同期グループのメール利用を完全に無効化していたため、メールアドレス用のグループから同期グループへの転送が失敗しました。同期グループには組織内のメール利用のみ許可することで解決しています。

今後の展望

今後は大きく2つの方向を考えています。

1つ目は、自動アサイン種別の拡充です。部署だけでなく、役職(マネジャー)や雇用形態(社員/業務委託)、職種といった他の人事データを使った自動アサインにも広げていく予定です。

2つ目は、Okta連携済みの他サービスへの横展開です。Okta上の組織グループを認可単位として、NotionAWS IAM Identity CenterをはじめとするOkta連携先サービスに展開していきます。将来的には、グループを抽象的な権限単位として扱い、権限管理をOkta内で完結させる未来を目指しています。

おわりに

今回の取り組みは、工数削減にとどまらず、「人と組織が動いても壊れない仕組みをどう作るか」を考える良い題材になりました。各組織の担当者がグループ管理の作業を意識せずに済むようになったことは、部のミッションである「EXをシンプルにする」の実践そのものだと感じています。もしこの仕組みを再現するなら、前提として必要になるのは、部署コードを含む信頼できる人事データ源、属性マッピングの設計、IaC/CIの基盤の3点だと考えています。これからもIaCと自動化を武器に、より技術的に面白い情シス運用を追求していきたいと思います。最後までお読みくださり、ありがとうございました。

Sansan技術本部ではカジュアル面談を実施しています

Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。

© Sansan, Inc.