active directory のスキーマを拡張してみた。

active directoryスキーマを拡張する方法について説明します。
ちなみに何かあって責任は取りませんので自己責任でどうぞ。

はじめに、まずは愚痴。

activedirectoryはldapという聳え立つクソの上に、GUIをつけたものなので非常に難解で大変です。
データベースだったら、 alter 一発なのに、、なんでこんなに簡単なことをクソめんどくさくできるか、それが知りたい。

マジわけわかんないんですけどー。

武器の調達。

一度作ったスキーマは2度と消すことができませんww そのため絶対に本番機でやってはいけません。
テスト機でも失敗してやり直すためには、、、OSの再インストールものだと思います。
ストレスで死にたくない人は、VMWare のスナップショットを利用して常にもとの環境に戻せるようにしておきましょう。
そうしないと発狂します。
こんなアホな仕様は、、、ActiveDirectoryっていうか、LDAPの仕様?信じられぬ。

  • VMWare Server(個人的にversion1 の ネイティブアプリベースが好きです。)

http://www.vmware.com/jp/download/server/

  • Windows 2003 Server (180 日限定評価版)

http://www.microsoft.com/japan/windowsserver2003/evaluation/trial/default.mspx
何か非常にわかりづらいんですが、適当に入れて登録しましょうwww
0円のものを購入するってなんてわかりづらいんだww

  • この記事の元になった資料

ここの不思議な機械翻訳
http://technet.microsoft.com/ja-jp/library/bb727064.aspx

すべてが終わった後に発見したwordの資料ww
http://download.microsoft.com/download/6/e/0/6e0c44fc-20c7-4eb8-aaa7-cb2f784803bb/A2.doc

ldapsearchをインストールしたlinux機が一台あれば非常に便利です。

私はlinux機からこんな感じでテストしてます。

ldapsearch -V 3 -h 192.168.1.99 -x -b 'CN=abc,cn=Users,dc=rtitest,dc=local' -D 'binduser@rtitest.local' -w binduser "*"

192.168.1.99 っていうのが、ActiveDirectoryマシンです。
rtitest.local が私のActiveDirectoryのドメイン名です。
rtitest.local の binduser のパスワードは binduser です。
この環境で、 ユーザ abc の情報を問い合わせています。

さぁ、クレージーな世界に行きますよ!!

ActiveDirectoryを拡張する。

まず、スキーマを拡張する機能を有効にします。

windows2003 server では、以下のディフォルトでは ActiveDirectory スキーマの拡張ができないようです。
cmd なコマンドラインから以下を入力し、ActiveDirectory スキーマを拡張できるようにします。

 regsvr32 schmmgmt.dll
ここら辺でVMのスナップショットを取りますw

ここら辺でVMのスナップショットを取っておくと幸せになりますww

mmc から ActiveDirectory スキーマ まで

OK。次は mmc から ActiveDirectory スキーマを呼び出します。
cmdなコマンドラインから入力

 mmc

メニューの「ファイル」→「スナップインの追加と削除」


「ActiveDirectory スキーマ」を選択して「追加」
ここで「ActiveDirectory スキーマ」が表示されないあなた。最初の 「regsvr32 schmmgmt.dll」を忘れてます。zap!


ここでもう一回 「OK」

お断り。

注意! 最初にも書きましたが、「一度作った値やクラスは削除できません」そのため絶対に本番機では試さないでください。死にます。本番気以外のマシンがないなら、すべてを忘れてブラウザを閉じてこれ以上進むのをやめましょう。

何か

編集許可とか、ドメインマスターがどうこうのというのがありますが、一台でやっている限りやらなくても問題ないみたいです。
というか、ここの画面が windows 2000server と windows 2003 server では違っていて、「このドメインコントローラーでスキーマの修正が可能」というチェックがありません。やるだけ時間の無駄です。

属性の登録

ldapでは属性を作成してから、クラスに登録します。
ActiveDirectory スキーマところの + ボタンで開くと、「クラス」と「属性」があります。

それでは、属性を登録します。
この辺は資料に書いてあるとおりです。
属性から「新規登録」をクリックして、属性を追加します。

最初は保険番号。

次に給料のレベルですか。

入力内容は、資料を参考にしてください。wordの方が日本語なのでお勧めです。

間違って入れてた場合は、二度と修正できません。ご愁傷様です。

次に、クラスを登録します。

こんな感じで登録します。


クラスの種類は「補助型」にしてください。
何で補助型なのか、それは「構造型」と動違うのか、どういう意味なのかはさっぱりわかりませんwww
入力したら「次へ」で次の画面に。

オプションにさっき作った 2つの属性を指定します。

「完了」でクラスが確定します。これも一度作ったら二と度削除できません。

userクラスに関連付ける。

先ほど作った HumanResource を user クラスに関連付けます。
クラスの中から、user クラスを選択して、右クリックのプロパティを出します。

「関係」のタブをクリックして、
補助クラスのところにある、「クラスの追加」をクリック。

さっき作った HumanResource を追加。
もし、ここに HumanResource が現れない場合、、、あなたは、 HumanResource を 補助型で作成しませんでしたね。
もう取り返しが付きませんので、 VMを巻き戻してください。zap!

HumanResource を追加したら、「OK」で確定します。

ここで、エラーが発生してうまくいかないことがあります。
英語だと、「The Change was rejected by the directory service」 っていうらしいです。
#この記事を書いている前は散々発生したのに、文章を書いているとまったく再発しなくなったので日本語のエラーは不明になりました。。。メモには英語のヤツしか取ってなかったんだ。
いろいろ試しているんですが、 クラス名を HumanResource ではなく HumanResource1 にしたらうまく動作したりします。
また、HumanResource のままでもうまくいくときもあります。
うまくいくときとうまくいかないときの再現方法がまだよくわかりません。

どういうことなの?

うまくいった場合、これで設定はおしまいです。
だめだったら、VMを巻き戻して、 HumanResource を HumanResource1 とかにして試してください。
コレでいいのかは知りません。

値を追加する。

こんなにがんばったのに、ActiveDirectoryのタブには何も表示されません。
ってゆーか既存のモデルには、追加した HumanResource の値も入りません。
資料にある vbscript で追加する必要があります。

ここで注意なので、ドメイン名です。
資料では、 とか書いてますが、ここをあなたのドメイン名に直してください。
私の場合は、 rtitest.local なのでこうなりました。

オリジナルは、 http://technet.microsoft.com/ja-jp/library/bb727064.aspx
です。

Sub ModifyUsers(oObject)
Dim oUser
For Each oUser in oObject
Select Case oUser.Class
Case "user"
oUser.Put "SalaryLevel","10000"
oUser.Put "SocialSecurityNumber",CStr(Int(9999*Rnd()+1))
oUser.SetInfo
Case "organizationalUnit" , "container"
ModifyUsers(oUser)
End select
Next
End Sub

Dim oDomain
Set oDomain=GetObject("LDAP://CN=Users,DC=rtitest,DC=local")
ModifyUsers(oDomain)
Set oDomain = Nothing
MsgBox "Finished"
WScript.Quit

これで HumanResource1の値が正しくセットされました。
資料にあるvbscript で覗くのもいいんですが、 ldapsearch で見るのがスマートだと思います。
こんな感じに追加されているのがわかります。

 ldapsearch -V 3 -h 192.168.1.99 -x -b 'CN=binduser,cn=Users,dc=rtitest,dc=local' -D 'binduser@rtitest.local' -w binduser "*"

 # binduser, Users, rtitest.local
 dn: CN=binduser,CN=Users,DC=rtitest,DC=local
 objectClass: top
 objectClass: person
 objectClass: organizationalPerson
 objectClass: user
 
 中略
 
 userPrincipalName: binduser@rtitest.local
 objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=rtitest,DC=local
 dSCorePropagationData: 20091024155428.0Z
 dSCorePropagationData: 20091024155428.0Z
 dSCorePropagationData: 20091024155428.0Z
 dSCorePropagationData: 16010108151056.0Z
 SocialSecurityNumber: 5334    ←これ
 SalaryLevel: 10000            ←これ

ちなみに、ユーザーを新規作成した場合は、こんな感じで当然のように何も入ってませんwww最悪です。

 ldapsearch -V 3 -h 192.168.1.99 -x -b 'CN=newuser,cn=Users,dc=rtitest,dc=local' -D 'binduser@rtitest.local' -w binduse "*"
 # newuser, Users, rtitest.local
 dn: CN=newuser,CN=Users,DC=rtitest,DC=local
 objectClass: top
 objectClass: person
 objectClass: organizationalPerson
 objectClass: user
 
 中略
 
 userPrincipalName: newuser@rtitest.local
 objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=rtitest,DC=local

もう一度プログラムを実行するか、資料の最後に載っているGUI追加プログラムで修正します。

GUI追加プログラム

実はこのプログラム webの方を実行してもダメです。
webの方のプログラムが追加している409は英語の方です。
日本語で利用しているので、 411 の方に追加しないとダメです。
wordの方は大丈夫です。

×英語 409に追加
"LDAP://CN=409, CN=DisplaySpecifiers," oRoot.get("configurationNamingContext"))
↓
○日本語 411に追加
"LDAP://CN=411, CN=DisplaySpecifiers," oRoot.get("configurationNamingContext"))

それと、webの方は行がつぶれていてコンパイルエラーになっちゃいます。
私の方で 改行をまともに修正したのと、 英語の409を日本語の411 にしたのを作ったのでおいときます。

オリジナルは、 http://technet.microsoft.com/ja-jp/library/bb727064.aspx
です。

Dim oRoot
Dim oDisp
Dim oCont
Dim aMenu
Dim iCount
Dim sNewMenu
Dim oFileSystem
Dim sOutFile
Dim sSystemFolder
Set oFileSystem = WScript.CreateObject("Scripting.FileSystemObject")
sSystemFolder = oFileSystem.GetSpecialFolder(1)
'Connect to Display Specifiers Container
set oRoot = Getobject("LDAP://RootDSE")
set oCont = GetObject("LDAP://CN=411, CN=DisplaySpecifiers," & oRoot.get("configurationNamingContext"))
Set oDisp = oCont.GetObject("displaySpecifier","cn=user-Display")
MsgBox "Display Specifier: " & oDisp.Name
'Add Attribute Display Names
oDisp.PutEx 3,"attributeDisplayNames" , Array("SalaryLevel,Annual Salary","SocialSecurityNumber,Social Security Number")
oDisp.SetInfo
'Add Shell Context Menu
MsgBox "Adding Shell Context Menu item"
iCount = 0
If Not IsEmpty(oDisp.shellContextMenu) Then
aMenu = oDisp.GetEx("shellContextMenu")
For iCount = LBound(aMenu) to UBound(aMenu)
MsgBox "Existing Menu item: " & aMenu(iCount)
Next
iCount = iCount + 1
End If
sNewMenu = CStr(iCount) & ",&HR Info...,hrshell.vbs"
oDisp.PutEx 3,"shellContextMenu" , Array(sNewMenu)
oDisp.SetInfo
MsgBox "Adding Shell Context Menu Program"
Set sOutFile = oFileSystem.CreateTextFile(sSystemFolder & "\hrshell.vbs",True)
sOutFile.WriteLine "Dim Args"
sOutFile.WriteLine "Dim oUser"
sOutFile.WriteLine "Set Args = Wscript.Arguments"
sOutFile.WriteLine "MsgBox " & Chr(34) & "LDAP Path: " & Chr(34) & " & Args(0)"
sOutFile.WriteLine "MsgBox " & Chr(34) & "Object Class: " & Chr(34) & " & Args(1)"
sOutFile.WriteLine "Set oUser = GetObject(Args(0))"
sOutFile.WriteLine "MsgBox " & Chr(34) & "HR Info" & Chr(34) & " & vbCRLF & " & Chr(34) & "Salary: " &  Chr(34) & " & oUser.SalaryLevel & vbCRLF & " & Chr(34) &  "Soc Sec No: " & Chr(34) & " & oUser.SocialSecurityNumber"
sOutFile.WriteLine "Set oUser = Nothing"
sOutFile.WriteLine "WScript.Quit"
sOutFile.Close

'Add Admin Context Menu
MsgBox "Adding Admin Context Menu item"
iCount = 0
If Not IsEmpty(oDisp.adminContextMenu) Then
aMenu = oDisp.GetEx("adminContextMenu")
For iCount = LBound(aMenu) to UBound(aMenu)
MsgBox "Existing Menu item: " & aMenu(iCount)
Next
iCount = iCount + 1
End If
sNewMenu = CStr(iCount) & ",&HR Admin...,hradmin.vbs"
oDisp.PutEx 3,"adminContextMenu" , Array(sNewMenu)
oDisp.SetInfo
MsgBox "Adding Admin Context Menu Program"
Set sOutFile = oFileSystem.CreateTextFile(sSystemFolder & "\hradmin.vbs",True)
sOutFile.WriteLine "Dim Args"
sOutFile.WriteLine "Dim oUser"
sOutFile.WriteLine "Dim temp"
sOutFile.WriteLine "Set Args = Wscript.Arguments"
sOutFile.WriteLine "MsgBox " & Chr(34) & "LDAP Path: " & Chr(34) & " & Args(0)"
sOutFile.WriteLine "MsgBox " & Chr(34) & "Object Class: " & Chr(34) & " & Args(1)"
sOutFile.WriteLine "Set oUser = GetObject(Args(0))"
sOutFile.WriteLine "temp = InputBox(" & Chr(34) & "Old Salary: " &  Chr(34) & " & oUser.SalaryLevel & vbCRLF & " &  Chr(34) & "New Salary" & Chr(34) & ")"
sOutFile.WriteLine "if temp <> " & Chr(34) & Chr(34) & " then oUser.Put " & Chr(34) & "SalaryLevel" & Chr(34) & ",temp"
sOutFile.WriteLine "temp = InputBox(" & Chr(34) & "Soc Sec Number: " &  Chr(34) & " & oUser.SocialSecurityNumber & vbCRLF & " &  Chr(34) & "New Number" & Chr(34) & ")"
sOutFile.WriteLine "if temp <> " & Chr(34) & Chr(34) & " then oUser.Put " & Chr(34) & "SocialSecurityNumber" & Chr(34) &  ",temp"
sOutFile.WriteLine " oUser.SetInfo"
sOutFile.WriteLine "Set oUser = Nothing"
sOutFile.WriteLine "WScript.Quit"
sOutFile.Close
MsgBox "Quit..."
Set oDisp = Nothing
Set oCont = Nothing
Set oRoot = Nothing
Set oFileSystem = Nothing
WScript.Quit

実行して成功しましたか?
資料にも書いてあるとおり、2回実行すると悲惨なことになるので、注意しましょう。

タブに入らないってマジですよ

さて、これでGUIで値が変更できるようになりました。

ldapsearchでみると変更されているのがちゃんとわかります。

ldapsearch -V 3 -h 192.168.1.99 -x -b 'CN=newuser,cn=Users,dc=rtitest,dc=local' -D 'binduser@rtitest.local' -w binduser "*"
 # newuser, Users, rtitest.local
 dn: CN=newuser,CN=Users,DC=rtitest,DC=local
 objectClass: top
 objectClass: person
 objectClass: organizationalPerson
 objectClass: user
 
 中略
 
 userPrincipalName: newuser@rtitest.local
 objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=rtitest,DC=local
 SocialSecurityNumber: 1234
 SalaryLevel: 123

よかった、よかった、、、、ってよくねーよ。

ActiveDirectory のタブで編集できるようにしてくれよ。
これぢゃあかっこが悪すぎるだろう。

何か、タブに追加するには、プログラムを作らないとダメみたいです。

この掲示板に書いてあったんですが、
http://social.technet.microsoft.com/forums/ja-JP/windowsserver2003ja/thread/87042715-852c-439f-a88e-9551f2191e78/

一応こんな感じでいけるらしい。
http://msdn2.microsoft.com/en-us/library/ms677632.aspx
左側のメニューからたどっていくとサンプルソースもあります。
http://msdn.microsoft.com/en-us/library/ms676867%28VS.85%29.aspx

しかし、面倒だなぁ。。。


本当は、sshの公開鍵をADで管理したかったんだ、、、
OpenLDAPsshの鍵を管理するってやつのAD番がやりたかった。
http://gihyo.jp/admin/serial/01/ldap/0006
しかし、道のりは遠いぜ。

一応スキーマの定義を上の方法でやれば、、、ある程度は何とかなるんだろうけど、俺はもう疲れ果てた。。。
http://integ.jp/server/opends/schema/10-openssh-lpk-opends.ldif

続き