Quicklinks
Active Directory Services Interface (ADSI) is a set of COM (Common Object Model) programming Interfaces. Like ODBC, ADSI provides common access to directories by adding a provider for each directory protocol type.Windows 2000 contains providers for:
Benefits of accessing directories with ADSI:
Adsi includes a comprehensive set of powerfull classes, to enable (remote) administration, including:
ADSI objects are COM objects, which represent objects in an underlying directory service. Objects can be container objects (like Folders) or Leaf objects (like Files). Each object has a unique ADSI path - a provider name followed by an object path. ADSI provides an abstract schema which describes the type of objects and attributes supported by each provider.Objects are read into cache whenGetInfo orGetObject are called. Changes reside in cached memory on the client until a SetInfo is issued. SetInfo writes data back to the underlying directory store.
The preferred method for connecting to an object is to useserverless binding; this means that the server is not explicitly provided; the default domain controller is the source of the LDAP requests. If the requested operations cannot be serviced in the local domain, a referral to the correct server is generated when possible, and the closest server is given.A serverless path is of the form LDAP://object.To bind to the domain DNS object which is the root container of the domain naming context:
Set Odse = GetObject( "LDAP//DC=corp,DC=Microsoft,DC=com")
TheRootDse is a special LDAP object that exists on all LDAP v3 servers. With it you can write scripts that are independent of the domain or enterprise on whih they are run:
Set Odse = GetObject( "LDAP://RootDse" )
For example, to reference an object in the current domain you can read the value for DefaultNamingContext to determine the current domain:
' Get path to the configuration naming context.Set RootDse = GetObject( "LDAP//RootDse" )Path = "LDAP://" & RootDse.get( "DefaultNamingContext" )
Domain-based information such as users, groups, and computers resides in thedomain naming context. Enterprise configuration information such as sites and subnets can be found in the Configuration Naming context. You can learn the path to naming contexts by examining ConfigurationNamingContext and SchemaNamingContext.
ADSI serverless binding is not avalable on Windows NT4 or Windows 98, so on these platforms you must always supply the name of an LDAP server for the connections:
Set Odse = GetObject( "LDAP//servername/RootDse" )
A global catalog (GC) server is a domain controller that contains a partial read-only replica of every object in every naming context. The replica is used to quickly search the enterprise for an object.The GC contains all objects from all naming contexts, but it is partial in that it contains only attributes designated for replication to the GC. The GC is accessed using port 3268 or by the GC provider as alias. In ADSI any reference to the GC is mapped to the LDAP provider on port 3268.Some of the common uses for searching the GC are:
To read an object you must first useGetObject to bind to it:
Set objUser = GetObject( "LDAP://CN=wjohnson;CN=Users;DC=klm,DC=com" )
This fills the object cache in the client's memory with the object's attributes. You can access each attribute by it's name:
WScript.Echo objUser.givenName & " " & objUser.sn & " " & objUser.mailSet objUser = Nothing
To update attributes on an existing object, you have to first bind to the object withGetObject, set each attribute's value (to update the values in the local object cache on the client), then issue aSetInfo to write the changes back to the directory. This example sets the mail address and the user's last name on user account:
Set objUser = GetObject( "LDAP://CN=wjohnson,CN=User,DC=klm,DC=com" )objUser.mail = "wjohnson@klm.com"objUser.sn = "wjohnson"objUser.SetInfoSet objUser = Nothing
To enumerate a container such as on OU, first bind to the OU and then use a loop to enumerate the container's object. To list all objects in the Users container:
Set ou = GetObject( "LDAP://CN=Users,DC=klm,DC=com" )For each obj in ou WScript.Echo obj.NameNext
OLE DB provides a common way to query for information in a database-like way. The ADSI OLE DB provider allows you to use ActiveX Data Object (ADOs) to search the Active Directory. The provider is read-only, so if you need to modify an object after you search for it, useGetObject with theAdsPath
To search the Active Directory you must first create the ADO connection and command objects:
' list all objects in the domain naming contextDim con, rs, ComSet con = WScript.CreateObject( "ADODB.Connection" )Set com = WScript.CreateObject( "ADODB.Command" )
Next, open the ADO provider:
' Open a connection objectcon.Provider = "ADsDSObject"con.Open "Active Directory Provider"
Then set the command object to use the current connection object, and build the query using either a simple SQL format or the LDAP search filter format (used in the example below). The query specifies the search's starting path and a filter for matching (the sample below looks for any type of object). Then list the attributes you want the query to return. Finally, specify the depth of the search:subtree for a deep search,base for a single object, orone level for searching a container.
Set Com.ActiveConnection = conCom.CommandText = "<GC://DC=hq,DC=klm,DC=com>;(objectClass=*);adspath;subtree"' Set the preferences for SearchCom.Properties( "Page Size" ) = 512Com.Properties( "TimeOut" ) = 30 ' seconds
When you execute the query, the results are retuned in a recordset. Loop through the recordset and print out the value, then move on to the next record.
While Not rs.EOF WScript.Echo rs.Fields( "AdsPath").Value Rs.MoveNextWend
Network Administrators have always wanted an easy way to get a list of network workstations along with operating system and service pack information. You can now do this by using new attributes on Windows 2000 computer accounts to identify the computer's current status. Thecomputer object is now automatically updated with information (from the netlogon service during secure channel setup) about the client's operating system, operating system version, and service pack level.
You can identify unused or possibly inactive computer accounts; accounts that have never been used do not have the operating system and version attributes set. If thewhenChanged attribute is more than a month old, the computer probably is not active on a network making periodic password changes. ThewhenChanged attribute is a non-replicated attribute which means it is calculated on each DC. ThelastLogon attribute is not replicated between DCs; to determine the last logon time you have to examine it on all DCs.
Attrinbutes of interest:
Locating Computers Based on Computer Account Attributes:
Const ADS_SCOPE_SUBTREE = 2Set objConnection = CreateObject("ADODB.Connection")Set objCommand = CreateObject("ADODB.Command")objConnection.Provider = "ADsDSOObject"objConnection.Open "Active Directory Provider"Set objCommand.ActiveConnection = objConnectionobjCommand.CommandText = _ "SELECT Name, Location, operatingSystemVersion FROM " _ & "'LDAP://DC=fabrikam,DC=com' WHERE objectClass='computer' " _ & "and operatingSystemVersion = '5.0 (2195)'"objCommand.Properties("Page Size") = 1000objCommand.Properties("Timeout") = 30objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREEobjCommand.Properties("Cache Results") = FalseSet objRecordSet = objCommand.ExecuteobjRecordSet.MoveFirstDo Until objRecordSet.EOF Wscript.Echo "Computer Name: " & objRecordSet.Fields("Name").Value Wscript.Echo "Location: " & objRecordSet.Fields("Location").Value objRecordSet.MoveNextLoop
When you want to document your configuration, it is useful to have a list of all domain trust relation ships, especially during migrations, when you usually have a mix of trusts between Windows 2000 domains. Windows NT account domains, and Windows NT 4.0 resource domains. Each trust relationship in a domain has a trusted domain objectthat resides in the System container in the Domain naming context:
When creating a new user account, you must set the CN (unique in the account's OU) andSamaccountname (unique in the domain) attributes. TheuserPrincipalName attribute (unique in the enterprise) is optional. To create a new user in the OU:
Dim salesOU as IADsContainerSet salesOU = GetObject("LDAP://OU=Sales,DC=Fabrikam,DC=COM")Set usr = salesOU.Create("user", "CN=Jay Adams")usr.Put "sAMAccountName", "jayadams"usr.Put "userPrincipalName", "jayadams@fabrikam.com" usr.Put "title", "Marketing Manager"usr.SetInfousr.SetPassword "seahorse"usr.AccountDisabled = Falseusr.SetInfo
There are three scopes for groups:
Groups are of these types:
Group type is required. Specify an integer that contains the flags that specify the group type and scope using these combinations:
The process of creating a group is similar to other objects. First bind to the OU which will contain the group. Next create the group object. Then set the group type, andsamAccountName for down-level clients.
Set ou = GetObject( "LDAP://OU=DSys,DC=Northwind,DC=tld" )Set grp = ou.Create( "group", "CN=Distributed System Admin" )' Creating a domain local groupgrp.Put "groupType", ADS_GROUP_TYPE_LOCAL Or ADS_GROUP_TYPE_SECURITY_ENABLEDgrp.Put "samAccountName", "DSysAdmin"grp.SetInfo
Use theadd method to add members to a group using the path to the user's object.
' Adding a user to a groupgrp.Add( "LDAP://CN=James Smith,OU=Marketing,OU=DSys,DC=Northwind,DC=tld" )
Configuration information is global information shared among all domains in the enterprise and usually managed by the enterprise administrator.
The partitions container'scrossRef objects list the enterprise naming contexts - one for Configuration, one for Schema and one for each Domain. You can add objects to point to partitions on other LDAP servers that are not part of the enterprise, in which case you generate an LDAP referral to the proper partition when requesting an object from that portion of the namespace.
This script enumerates crossRef objects in the partitions container:
On Error Resume Next msgbox "This script enumerates crossRef objects in the partitions container." sPrefix = "LDAP://" ' Get distinguished name for config container and build ADsPath to partitions container.Set root= GetObject(sPrefix & "rootDSE")If (Err.Number <> 0) Then BailOnFailure Err.Number, "on GetObject method for rootDSE"End IfsConfigDN = root.Get("configurationNamingContext")If (Err.Number <> 0) Then BailOnFailure Err.Number, "on Get method"End IfsContainerDN = "cn=Partitions," & sConfigDN '''''''''''''''''''''''''''''''''''''''' Bind to the container'''''''''''''''''''''''''''''''''''''''Set cont= GetObject(sPrefix & sContainerDN)If (Err.Number <> 0) Then BailOnFailure Err.Number, "on GetObject method for partitions container"End If'''''''''''''''''''''''''''''''''''''''' Enumerate the container.''''''''''''''''''''''''''''''''''''''For Each obj In cont strText = strText & "Name: " & obj.Get("name") & vbCrLf values = obj.GetEx("objectClass") For Each value In values sValue = value Next strText = strText & " objectClass: " & sValue & vbCrLf strText = strText & " DnsRoot: " & obj.Get("dnsRoot") & vbCrLf strText = strText & " NCName: " & obj.Get("NCName") & vbCrLf sTrustParent = obj.Get("trustParent") If (Err.Number = 0) Then strText = strText & " TrustParent: " & sTrustParent & vbCrLf Else Err.Clear End If sNetBIOSName = obj.Get("nETBIOSName") If (Err.Number = 0) Then strText = strText & " NETBIOSName: " & sNetBIOSName & vbCrLf Else Err.Clear End IfNextshow_items strText, "Display crossRef"'''''''''''''''''''''''''''''''''''''''' Display subroutines'''''''''''''''''''''''''''''''''''''''Sub show_items(strText, strName) MsgBox strText, vbInformation, "Create CrossRef"End Sub Sub BailOnFailure(ErrNum, ErrText) strText = "Error 0x" & Hex(ErrNum) & " " & ErrText MsgBox strText, vbInformation, "ADSI Error" WScript.QuitEnd Sub
Each domain controller in the enterprise is represented by several objects:
To get a list of all enterprise DCs, issue a query with a base path of the site's container that searches for object categorynTDSDSA. Some attributes of interest:
Listing domain controllers:
' Get the Configuration Naming ContextSet oRootDSE = GetObject("LDAP://RootDSE")strConfigNC = oRootDSE.Get("configurationNamingContext")' Set up the oConnectionectionset oConnection = CreateObject("ADODB.Connection")oConnection.Provider = "ADsDSOObject"oConnection.Open "ADs Provider"' Build the querystrQuery = "<LDAP://" & strConfigNC & ">;(objectClass=nTDSDSA);ADsPath;subtree"set oCmd = CreateObject("ADODB.Command")oCmd.ActiveConnection = oConnectionoCmd.CommandText = strQuerySet oRecordset = oCmd.Execute' Iterate through the resultsIf oRecordset.Eof and oRecordSet.Bof Then WScript.Echo "No Domain Controllers were found"Else While Not oRecordset.EOF Set oParent = GetObject(GetObject(oRecordset.Fields("ADsPath")).Parent) ' Output the name of the server WScript.Echo "Server: " & oParent.cn & " dNSHostName: " & oParent.dNSHostName oRecordset.MoveNext WendEnd if
Flexible Single-Master Operations (FSMO) implements operations (there are only a few) that must be handled in a single master replication model. There are five FSMO roles, two per enterprise and three per domain, and you can use scripting to find out which DCs hold which FSMO roles.
The Schema Master is unique in the entire enterprise; it alone can create new classes or attributes, after which it replicates updates to all domains in the forest. To identify it, read the value offsmoRoleOwner attribute on the Schema container found under the Configuration container. Schema and Configuration naming contexts are enterprise wide and reside on all domain controllers.
Set objRootDse = GetObject( "LDAP://RootDse" )Set objSchema = GetObject( "LDAP://" & objRootDse.get( "SchemaNamingContext" ))WScript.Echo objSchema.fsmoRoleOwner
The Domain Naming Master is unique in the enterprise; it manages the addition and removal of domains into the forest. To identify it, read thepartitions container object and examine itsfsmoRoleOwner attribute:
Set objRootDse = GetObject( "LDAP://RootDse" )Set objDomains = GetObject( "LDAP://CN=Partitions," & _ objRootDse.Get( "ConfigurationNamingContext" ))WScript.Echo objDomains.fsmoRoleOwner
The PDC Emulator is a per-domain role. To identify it, read the value of the fsmoRoleOwner attribute on the DomainDNS object:
Set objRootDse = GetObject( "LDAP://RootDse" )Set objPDC = GetObject( "LDAP://" & objRootDse.Get( "DefaultNamingContext" ))WScript.Echo objDomains.fsmoRoleOwner
The RID Master is a per-domain role. It allocates RID blocks for all DCs in a domain that are used for SIDs in security principals such as users and groups. To identify it, read the value of the fsmoRoleOwner attribute on the RIDManager object of name CN="Rid Manager$" found in the domain's System Container:
Set objRootDse = GetObject( "LDAP://RootDse" )Set objRID = GetObject( "LDAP://CN=Rid Manager$,CN=System," & _ objRootDse.Get( "DefaultNamingContext" ))WScript.Echo objDomains.fsmoRoleOwner
The Infrastructure Master is a per-domain role. To identify it, read the value of thefsmoRoleOwner attribute on theInfrastructureUpdate object for the domain:
Set objRootDse = GetObject( "LDAP://RootDse" )Set objInfra = GetObject( "LDAP://CN=Infrastructure," & _ objRootDse.Get( "DefaultNamingContext" ))WScript.Echo objDomains.fsmoRoleOwner
Footer Navigation