スキーママスターの検出
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スナップインを使用します。
Note
Active DirectoryスキーマMMCスナップインは手動で登録する必要があります。 スキーマスナップインを登録するには、Windows System32ディレクトリのコマンドプロンプトから次のコマンドを実行する必要があります。
regsvr32.exe schmmgmt.dll
NTDSUTILコマンドラインユーティリティを使用します。
サードパーティのアプリケーション (適切なLDAP書き込みを発行するアプリケーション) を使用します。
プログラムによってスキーママスターになるために、適切な特権を持つユーザーのコンテキストで実行されているアプリケーションは、操作属性becomeSchemaMasterのLDAP書き込みをそのDCのrootDSEに発行できます。 これにより、現在の所有者からローカル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