Active Directory Domain Services にはマルチマスター更新システムがあります。すべてのドメイン コントローラーは、ディレクトリの書き込み可能なコピーを保持します。 スキーマの更新は、同じツリーまたはフォレストに属するすべてのドメインに反映されます。 スキーマの競合する更新を調整することは困難であるため、スキーマの更新は 1 台のサーバーでのみ実行できます。 更新を実行する権限を持つサーバーは変更できますが、その権限を持つサーバーは 1 つだけです。 このサーバーはスキーマ マスターと呼ばれます。 企業に最初にインストールされる DC は、既定ではスキーマ マスターです。
スキーマの変更は、スキーマ マスター レベルでのみ行うことができます。 スキーマ マスターである DC を検出するには、次の手順を実行します。
DCスキーママスターの検出
- 任意の DC 上のスキーマ コンテナーの fsmoRoleOwner 属性を読み取ります。 fsmoRoleOwner 属性は、スキーマ マスターの nTDSDSA オブジェクトの識別名 (DN) を返します。
- 取得した DN を持つ nTDSDSA オブジェクトにバインドします。 このオブジェクトの親は、スキーマ マスターを含む DC のサーバー オブジェクトです。
- nTDSDSA オブジェクトの親の ADsPath を取得します。 親はサーバー オブジェクトです。
- サーバー オブジェクトにバインドします。
- サーバー オブジェクトの dNSHostName 属性を取得します。 これは、スキーマ マスターを含む DC の DNS 名です。
- スキーマ マスターの DNS 名をサーバーとして指定し、DN をスキーマ マスターにバインドするスキーマ コンテナーの DN として指定します。 たとえば、サーバーが "dc1.fabrikam.com" で、スキーマ コンテナー DN が "cn=schema,cn=configuration,dc=fabrikam,dc=com" の場合、ADsPath は次のようになります。
LDAP://dc1.fabrikam.com/cn=schema,cn=configuration,dc=fabrikam,dc=com
スキーマ マスターを検索し、それにバインドしてスキーマを変更することをお勧めします。 ただし、スキーマ マスターを別のサーバーに移動することはできます。
別のサーバーをスキーマ マスターにするには、適切な特権を持つユーザーは次のことができます。
スキーマ マネージャー MMC スナップインを使用します。
手記
Active Directory スキーマ MMC スナップインを手動で登録する必要があります。 スキーマ スナップインを登録するには、Windows System32 ディレクトリのコマンド プロンプトから次のコマンドを実行する必要があります。
regsvr32.exe schmmgmt.dll
NTDSUTIL コマンド ライン ユーティリティを使用します。
サード パーティ製アプリケーション (適切な LDAP 書き込みを発行するアプリケーション) を使用します。
プログラムによってスキーマ マスター になるには、適切な特権を持つユーザーのコンテキストで実行されているアプリケーションが、その DC 上の rootDSE に対し、操作属性「becomeSchemaMaster」の LDAP 書き込みを発行する必要があります。 これにより、現在の所有者からローカル DC へのスキーマ マスターのアトミック転送が開始されます。
次のコード例では、スキーマ マスターを検索します。 次の関数は、スキーマ マスターであるコンピューター上のスキーマ コンテナーにバインドします。
HRESULT BindToSchemaMaster(IADsContainer **ppSchemaMaster)
{
HRESULT hr = E_FAIL;
// Get rootDSE and the schema container DN.
IADs *pObject = NULL;
IADs *pTempSchema = NULL;
IADs *pNTDS = NULL;
IADs *pServer = NULL;
BSTR bstrParent;
LPOLESTR szPath = new OLECHAR[MAX_PATH];
VARIANT var, varRole,varComputer;
hr = ADsOpenObject(L"LDAP://rootDSE",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IADs,
(void**)&pObject);
if (hr == S_OK)
{
hr = pObject->Get(CComBSTR("schemaNamingContext"), &var);
if (hr == S_OK)
{
wcscpy_s(szPath,L"LDAP://");
wcscat_s(szPath,var.bstrVal);
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IADs,
(void**)&pTempSchema);
if (hr == S_OK)
{
/*
Read the fsmoRoleOwner attribute to identify which server is
the schema master.
*/
hr = pTempSchema->Get(CComBSTR("fsmoRoleOwner"), &varRole);
if (hr == S_OK)
{
// The fsmoRoleOwner attribute returns the nTDSDSA object.
// The parent is the server object.
// Bind to NTDSDSA object and get parent.
wcscpy_s(szPath,L"LDAP://");
wcscat_s(szPath,varRole.bstrVal);
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION,
IID_IADs,
(void**)&pNTDS);
if (hr == S_OK)
{
hr = pNTDS->get_Parent(&bstrParent);
if (hr == S_OK)
{
/*
Bind to server object and get the DNS name of
the server.
*/
wcscpy_s(szPath,bstrParent);
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION,
IID_IADs,
(void**)&pServer);
if (hr == S_OK)
{
// Get the dns name of the server.
hr = pServer->Get(CComBSTR("dNSHostName"),
&varComputer);
if (hr == S_OK)
{
wcscpy_s(szPath,L"LDAP://");
wcscat_s(szPath,varComputer.bstrVal);
wcscat_s(szPath,L"/");
wcscat_s(szPath,var.bstrVal);
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION,
IID_IADs,
(void**)ppSchemaMaster);
if (FAILED(hr))
{
if (*ppSchemaMaster)
{
(*ppSchemaMaster)->Release();
(*ppSchemaMaster) = NULL;
}
}
}
VariantClear(&varComputer);
}
if (pServer)
pServer->Release();
}
SysFreeString(bstrParent);
}
if (pNTDS)
pNTDS->Release();
}
VariantClear(&varRole);
}
if (pTempSchema)
pTempSchema->Release();
}
VariantClear(&var);
}
if (pObject)
pObject->Release();
return hr;
}
次のコード例では、スキーマ マスターであるコンピューターの DNS 名を表示します。
On Error Resume Next
'''''''''''''''''''
' Bind to the rootDSE
''''''''''''''''''''
sPrefix = "LDAP://"
Set root= GetObject(sPrefix & "rootDSE")
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on GetObject method"
End If
'''''''''''''''''''
' Get the DN for the schema
''''''''''''''''''''
sSchema = root.Get("schemaNamingContext")
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on Get method"
End If
'''''''''''''''''''
' Bind to the schema container
''''''''''''''''''''
Set Schema= GetObject(sPrefix & sSchema )
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on GetObject method to bind to schema"
End If
''''''''''''''''''''
' Read the fsmoRoleOwner attribute to see which server is the
' schema master.
''''''''''''''''''''
sMaster = Schema.Get("fsmoRoleOwner")
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on IADs::Get method for fsmoRoleOwner"
End If
''''''''''''''''''''
' The fsmoRoleOwner attribute returns the nTDSDSA object.
' The parent is the server object.
' Bind to NTDSDSA object and get the parent object.
''''''''''''''''''''
Set NTDS = GetObject(sPrefix & sMaster)
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on GetObject method for NTDS"
End If
sServer = NTDS.Parent
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on IADs::get_Parent method"
End If
''''''''''''''''''''
' Bind to server object
' and get the reference to the computer object.
''''''''''''''''''''
Set Server = GetObject(sServer)
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on GetObject method for " & sServer
End If
sComputer = Server.Get("dNSHostName")
''''''''''''''''''''
' Display the DNS name for the computer.
''''''''''''''''''''
strText = "Schema master has the following DNS name: "& sComputer
WScript.echo strText
''''''''''''''''''''
' Display subroutines
''''''''''''''''''''
Sub BailOnFailure(ErrNum, ErrText)
strText = "Error 0x" & Hex(ErrNum) & " " & ErrText
MsgBox strText, vbInformation, "ADSI Error"
WScript.Quit
End Sub