midPoint by OpenStandia Advent Calendar 2024 の20日目は、15日目の記事で構築したActive Directory(以下、AD)互換環境であるSamba4に対して、マルチアカウントをプロビジョニングします。
既に、16日目と17日目の記事でそれぞれADへのユーザーとグループプロビジョニングについて解説済みです。
- 16日目「midPoint からActive Directoryにプロビジョニングする(ユーザー編)」
- 17日目「midPoint からActive Directoryにプロビジョニングする(グループ編)」
そして18日目と19日目の記事で、midPointのアソシエーションを利用したオブジェクト間のメンバーシップのプロビジョニングについても解説済みです。
- 18日目「midPoint からActive Directoryにプロビジョニングする(グループメンバーシップ編)」
- 19日目「midPoint からActive Directoryにプロビジョニングする(ネストグループ編)」
今回はADへのプロビジョニング解説シリーズの締めとして、少し高度な機能であるmidPointの 「マルチアカウント」 を活用した、複数ユーザーや複数グループのプロビジョニングについて解説したいと思います。その前に、midPointのリソース設定などでこれまでよく目にしてきた 「種別(kind)」と「用途(intent)」 についてまずは解説します。
種別(kind)と 用途(intent)とは
リソース設定でオブジェクトタイプを追加する際に、基本情報にて 「種別」 と 「用途」 を設定する箇所がありました。
「種類(kind)」はmidPointが定義している「アカウント(Account)」「エンタイトルメント(Entitlement)」「汎用(Generic)」の3つから、追加したいリソースのオブジェクトタイプに合わせて基本的に選択していました。ユーザーやアカウントといったオブジェクトを扱うのであれば「アカウント」を選択し、グループやロールといった権限制御に使われるオブジェクトを扱うのであれば「エンタイトルメント」を選択していました。
一方、「用途(intent)」については、これまでの記事では特に設定せずデフォルトの挙動に任せていました(未設定の場合、default
という値が利用されます)。実はこの「用途(intent)」には、自由な値を設定することができます。「用途(intent)」はそのオブジェクトタイプがどのように使用されることを意図しているかを示します。例えば、連携先システムにおいて一般ユーザーだけでなく、システム管理用のアカウントもあり、それもmidPointからプロビジョニングする場合は、「用途(intent)」を system
と設定したりして表現します。
種別(kind)と 用途(intent)によるマルチアカウントの表現
これまで構築したADにマルチアカウントを設定してみます。例として、今までのユーザーと異なる種別として、システムユーザーを別OU配下で管理するとします。
-
ou=Users,ou=IDM,dc=ad,dc=example,dc=com
:デフォルトユーザーのOU(16日目の記事で作成済み) -
ou=SysUsers,ou=IDM,dc=ad,dc=example,dc=com
:システムユーザーのOU
ADの起動時の初期処理の修正
ou=SysUsers
を追加するため、ADの起動時の初期処理(./ad/ad-init.sh
)を以下のように少し修正しておきます。
dn: OU=Users,OU=IDM,$BASE_DN
objectClass: organizationalUnit
+
+dn: OU=SysUsers,OU=IDM,$BASE_DN
+objectClass: organizationalUnit
EOF
これで「ad」コンテナをdocker compose up -d --build ad
で再ビルドして起動すれば、システムユーザー用のOUが作成されます。
なお、「ad」コンテナはデータを保存するようにDocker Composeで設定していないため、再作成するとプロビジョニングしたデータがクリアされます。「HR recon Orgs」リコンシリエーションタスクと「HR import users」インポートタスクを再度実行して、AD側に再プロビジョニングしておきます。
システムユーザー用のオブジェクトタイプ追加
「AD」リソースに新たにシステムユーザー用のオブジェクトタイプを追加します。用途を system
、デフォルトを False
とします。
-
表示名:
System User
-
種類:
アカウント
-
用途:
system
-
デフォルト:
False
リソース・データの設定は、「User」オブジェクトタイプと区別して名寄せできるように「フィルター」にてOUを絞り込んでおきます。
AD側に既に存在するデータとの名寄せのシナリオ1で、「User」と「System User」のどちらの用途なのか判断が必要になります。誤って別の用途として判断されてしまうとコンフリクトエラーが発生し、名寄せに失敗します。フィルターでOUを利用して絞り込んでおくと、「User」と「System User」は異なるOU配下なので用途を正しく判定できます。
-
オブジェクトクラス:
user
-
フィルター:
attributes/dn endsWith[distinguishedName] "ou=SysUsers,ou=IDM,dc=ad,dc=example,dc=com"
MidPointデータの設定は「User」オブジェクトタイプと同じです。
デフォルトユーザー用のオブジェクトタイプの修正
既存の「User」オブジェクトタイプの基本情報の編集画面を開き、リソース・データの設定にて「System User」オブジェクトタイプと区別して名寄せできるように「フィルター」設定を追加してOUを絞り込んでおきます。
-
フィルター:
attributes/dn endsWith[distinguishedName] "ou=Users,ou=IDM,dc=ad,dc=example,dc=com"
マッピングの設定
今回は、16日目の記事で作成した「User」オブジェクトタイプのアウトバウンドマッピングを少し変えて、sys
プレフィックスを設定するようにしておきます。dn
、userPrincipalName
、sAMAccountName
の「式」はスクリプト
とし、以下のコードを設定します。
-
name → dn:
basic.composeDnWithSuffix("cn", "sys" + name.orig, "ou=SysUsers,ou=IDM,dc=ad,dc=example,dc=com")
-
name → userPrincipalName:
"sys" + name + "@ad.example.com"
-
name → sAMAccountName:
"sys" + name
以下のようになります。
同期の設定
こちらは16日目の記事と同じ設定にしておきます。
Correlationの設定
16日目の記事と同じように名寄せも設定しておきます。
名寄せ用のインバウンドマッピングの追加
cn
を使って名寄せしますが、プレフィックスを付けているので「式」をスクリプト
として調整します。
詳細設定で「Use for」を相関
にしておきます。
Correlationの設定
以下のように設定します。
システムユーザー用のロール作成
ADリソースにシステムユーザー用のオブジェクトタイプを追加しましたので、それに対するインデュースメントを設定するプロビジョニング用のロールを作成します。作成手順は、12日目の記事「midPoint におけるロールベースプロビジョニング」で解説済みです。詳細手順はそちらを適宜参照いただき、以下のロールを作成します。
-
名前:
System User
ADリソースへのインデュースメントを追加します。
追加したオブジェクトタイプ 「種類:アカウント、用途:system」 を選択できるようになっていますので、これを選択してインデュースメントを設定します。
プロビジョニングの確認
では、作成したロールをユーザーにアサインし、マルチアカウントのプロビジョニング動作を確認してみましょう。既にADにユーザーとしてプロビジョニング済みであるユーザーに対して、作成した「System User」ロールをアサインします。
「変更のプレビュー」ボタンで設定が想定通りされているか確認します。正しく設定されていれば、下記のようにou=SysUsers
配下にsys
プレフィックス付きでcn
を構成してプロビジョニングする予定であることを確認できます。
保存後、該当ユーザーのプロジェクションを確認すると、システムユーザーのプロジェクションが追加されていることが分かります。
種別(kind)と 用途(intent)の限界
用途を自由に増やしてマルチアカウントに対応することができましたが、この方式には限界があります。オブジェクトタイプの追加が事前に必要という静的な設定であり、動的には対応できません。例えば、組織に対応してプロビジョニングするADグループを、ユーザーの種別や職階、契約などで分けて複数作りたい場合に、それらの種類が増えるたびにリソース設定の変更が必要となってしまいます。また、種類が違うだけでマッピング内容が基本同じという場合に、似たようなマッピングを持つオブジェクトタイプの設定が複数並び、メンテナンスも大変です。
そのような状況に対応できるように、midPointではもう1つ タグ(tag) という情報を組み合わせることができます。タグを利用したマルチアカウントについては、以下の公式ドキュメントに記載されています。
このタグを利用する場合は、オブジェクトタイプを新たに追加する必要はありません。オブジェクトタイプの設定の中で、動的にタグを決定するための設定を行うことができます。中々イメージしづらいと思うので、具体的な使用例を紹介します。
タグ(tag)によるマルチアカウントの表現
先ほどはユーザーに関してマルチアカウントでADにプロビジョニングしましたが、今度はグループに対して設定します。便宜上「マルチアカウント」と呼んでいますが、プロビジョニングできるものであれば何でもよいので、ADグループに対しても適用できます。
現状、ADグループは組織情報を元に、1:1でプロビジョニングしています。これを、タグを使って1:Nのグループをプロビジョニングするようにします。
仕様
midPointの組織オブジェクトの詳細画面を開くと、基本情報に「メールドメイン」というマルチバリュー属性があります。本来の用途はこの組織のメールアドレスのドメイン情報を保持するための属性ですが、便宜上この属性にプロビジョニングしたい追加のADグループの種別を設定する仕様とします。例えば以下は、「employee」「staff」「intern」の3つの種別を設定しています。
この値を設定して組織オブジェクトを保存すると、ADには以下の3つのグループを新たにプロビジョニングするという仕様とします。「cn」が名前_種別
となるようにサフィックスを付けたグループを作ります。
cn=001_employee,ou=Groups,ou=IDM,dc=ad,dc=example,dc=com
cn=001_staff,ou=Groups,ou=IDM,dc=ad,dc=example,dc=com
cn=001_intern,ou=Groups,ou=IDM,dc=ad,dc=example,dc=com
オブジェクトタイプの追加
既に「Group」オブジェクトタイプが存在しているのでそちらを修正してもよいですが、サフィックスを付けたマッピングルールとなり混ぜると複雑化しますので、今回はもう一つ「Category Group」というオブジェクトタイプを追加し、そこでタグの設定を追加することとします。
ただし、タグの設定はGUIのサポートがありません。よって、ADリソースのRAW編集画面を開き、以下の<objectType>〜</objectType>
を挿入して設定します(タグに関わる部分以外は、今までの記事で解説したように画面から作成できますので、タグ部分のみを追加でも構いません)。
<objectType>
<kind>entitlement</kind>
<intent>categoryGroup</intent>
<displayName>Category Group</displayName>
<default>false</default>
<multiplicity>
<maxOccurs>unbounded</maxOccurs>
<tag>
<outbound>
<source>
<path>$focus/mailDomain</path>
</source>
</outbound>
</tag>
</multiplicity>
<delineation>
<objectClass>ri:group</objectClass>
</delineation>
<focus>
<type>c:OrgType</type>
</focus>
<attribute>
<ref>ri:dn</ref>
<outbound>
<strength>strong</strength>
<source>
<path>$focus/name</path>
</source>
<source>
<path>$projection/tag</path>
</source>
<expression>
<script>
<code>
tag == null ? "cn=$name.orig,ou=Groups,ou=IDM,dc=ad,dc=example,dc=com" : "cn=${name.orig}_${tag},ou=Groups,ou=IDM,dc=ad,dc=example,dc=com"
</code>
</script>
</expression>
</outbound>
</attribute>
<attribute>
<ref>ri:displayName</ref>
<outbound>
<strength>strong</strength>
<source>
<path>$focus/displayName</path>
</source>
</outbound>
</attribute>
<attribute>
<ref>ri:sAMAccountName</ref>
<outbound>
<strength>strong</strength>
<source>
<path>$focus/name</path>
</source>
<source>
<path>$projection/tag</path>
</source>
<expression>
<script>
<code>
tag == null ? name.orig : "${name.orig}_${tag}"
</code>
</script>
</expression>
</outbound>
</attribute>
<attribute>
<ref>ri:groupType</ref>
<outbound>
<strength>strong</strength>
<expression>
<value>-2147483646</value>
</expression>
</outbound>
</attribute>
</objectType>
ポイントは以下の箇所です。ここでタグの数を無制限(unbounded
)とし、アウトバウンドマッピングで使用するタグの取得元情報としてmailDomain
属性を指定しています。これにより、mailDomain
属性に設定した値がタグの値として利用されます。
<multiplicity>
<maxOccurs>unbounded</maxOccurs>
<tag>
<outbound>
<source>
<path>$focus/mailDomain</path>
</source>
</outbound>
</tag>
</multiplicity>
そして、そのタグの値を「dn」の値に埋め込んでプロビジョニングするように、アウトバウンドマッピングを書いています。
<attribute>
<ref>ri:dn</ref>
<outbound>
<strength>strong</strength>
<source>
<path>$focus/name</path>
</source>
<source>
<path>$projection/tag</path>
</source>
<expression>
<script>
<code>
tag == null ? "cn=$name.orig,ou=Groups,ou=IDM,dc=ad,dc=example,dc=com" : "cn=${name.orig}_${tag},ou=Groups,ou=IDM,dc=ad,dc=example,dc=com"
</code>
</script>
</expression>
</outbound>
</attribute>
同様に、「sAMAccountName」の値にもタグの値を埋め込んでいます。
<attribute>
<ref>ri:sAMAccountName</ref>
<outbound>
<strength>strong</strength>
<source>
<path>$focus/name</path>
</source>
<source>
<path>$projection/tag</path>
</source>
<expression>
<script>
<code>
tag == null ? "$name.orig" : "${name.orig}_${tag}"
</code>
</script>
</expression>
</outbound>
</attribute>
Company Orgアーキタイプの修正
追加した「Category Group」オブジェクトタイプに対するインデュースメントを設定しておきます。
プロビジョニングの確認
組織「005」の詳細画面を開き、「メールドメイン」にemployee
、staff
、intern
の3つの値を追加して「変更のプレビュー」を見てみます。
サフィックスが付与された3つのグループが追加されようとしていることが分かります。「保存」ボタンをクリックして保存します。
この組織のプロジェクションを確認しておきます。以下のように、ADに対して4つのグループがプロビジョニングされた状態となっていることを確認できます。
試しに、「intern」を削除してまた変更のプレビューを確認すると、連動してデプロビジョニング(グループの削除)が行われることも確認できます。
このように タグ(tag) を活用することで、オブジェクトタイプを事前に追加することなく動的にマルチアカウントを表現することが可能となります。
タグ(tag)利用時のメンバーシップの連携設定
タグを使ったマルチアカウントでのADグループのプロビジョニングができたので、メンバーシップのプロビジョニングも実施します。種別(kind)と 用途(intent)のみを使用している場合は、静的に決まっているため以下のようなアソシエーションのアウトバウンドマッピングを定義していました。定義済みの種別(kind)と 用途(intent)を設定するだけでした。
<inducement id="...">
<construction>
<strength>weak</strength>
<resourceRef oid="..." relation="org:default" type="c:ResourceType">
<!-- AD -->
</resourceRef>
<kind>account</kind>
<intent>default</intent>
<association id="...">
<ref>group</ref>
<outbound>
<expression>
<associationFromLink>
<projectionDiscriminator xsi:type="c:ShadowDiscriminatorType">
<kind>entitlement</kind>
<intent>default</intent>
</projectionDiscriminator>
</associationFromLink>
</expression>
</outbound>
</association>
</construction>
<order>2</order>
<focusType>c:OrgType</focusType>
</inducement>
一方、タグの場合は所属するプロジェクション(Shadowオブジェクト)が静的に決まらず動的に変わるため、同じ設定は利用できません。そこで、<associationTargetSearch>
というタグを使って、ユーザーが所属するグループ(に紐づくShadowオブジェクト)を直接検索する設定を行います。
今回の例の場合、「Company Org」アーキタイプをRAW編集で開き、以下の<inducement>〜</inducement>
を追加します。
<resourceRef oid="..."
のoid
には、自身の環境のADリソースのoid
を設定してください。
<inducement>
<construction>
<strength>weak</strength>
<resourceRef oid="..." relation="org:default" type="c:ResourceType">
<!-- AD -->
</resourceRef>
<kind>account</kind>
<intent>default</intent>
<association>
<ref>ri:group</ref>
<outbound>
<expression>
<associationTargetSearch>
<filter>
<q:text>attributes/dn =[distinguishedName] ```
// アーキタイプがアサインされていないユーザーの場合
if (basic.isEmpty(focus.archetypeRef)) {
return null
}
// ユーザーにアサインされているアーキタイプを取得
archetype = midpoint.resolveReferenceIfExists(focus.archetypeRef)
// アーキタイプが解決できない場合
if (archetype == null) {
return null
}
// アーキタイプ名をサフィックスとしてグループを探す
return "cn=${immediateRole.name.orig}_${archetype.name},ou=Groups,ou=IDM,dc=ad,dc=example,dc=com"
```
</q:text>
</filter>
</associationTargetSearch>
</expression>
</outbound>
</association>
</construction>
<order>2</order>
<focusType>c:UserType</focusType>
</inducement>
<associationTargetSearch>
の中では<filter>
で検索クエリを設定します。<q:text>
を使うと、midPoint Query Languageでクエリーを書くことができます。
midPoint Query Languageとは、midPointの至る所(GUI、スクリプト)で使用できる専用のクエリー言語です。詳細は以下の公式ドキュメントを参照してください。
https://docs.evolveum.com/midpoint/reference/support-4.8/concepts/query/midpoint-query-language/
今回の例では、ユーザーにアサインされているアーキタイプの名前(Employee)から所属させるグループ名を自動的に決めています。「Employee」アーキタイプが付与されているユーザーであれば、CN=組織名_employee
のADグループに所属させます。
このようにmidPoint Query Languageでは、中に埋め込む値をGroovyスクリプトでロジックを書いて動的に決めることができます。なお、スクリプト内で使用している以下の変数について補足しておきます。これらはアソシーションのアウトバウンドマッピングでデフォルトでバインドされている変数です。
- focus:アソシーションのアウトバウンドマッピングを評価する対象のサブジェクト(今回の例だとユーザー)
- immediateRole:このアウトバウンドマッピングを評価するに当たりサブジェクトにアサインされたオブジェクト(今回の例だと、サブジェクトであるユーザーにアサインされた組織オブジェクト)
タグ(tag)利用時のメンバーシップのプロビジョニング確認
005
組織に所属する001
ユーザーの詳細画面を開き、1件リコンサイルでプレビューを表示しで動作確認します。以下のように、CN=005_employee
のグループのみにメンバーシップがプロビジョニングされる予定であることが分かります。その他のグループ(CN=005_staff
、CN=005_intern
)には所属となりません。
保存後、該当ユーザーのプロビジョクションを確認すると、memberOf
属性に設定されていることが分かります。
まとめ
20日目では、midPointのマルチアカウント機能を活用したプロビジョニングについて解説しました。特に、タグ(tag)を使用したマルチアカウント機能は動的に対応できる点が特徴であり、柔軟に対応が可能です。
一人に対して複数アカウントの連携や、会社組織(本部や部など)単位で複数のADグループをプロビジョニングしたいケースはよく見かけると思いますので、本機能を活用していただければと思います。
-
11日目の記事「midPoint からCSVにプロビジョニングする」で紹介したプロビジョニング時の名寄せの処理です。 ↩