Tuesday, September 23, 2008

Active Directory Service Interfaces (ADSI) Benefits

url:http://www.activexperts.com/activmonitor/windowsmanagement/adsi/

Active Directory Service Interfaces (ADSI) Benefits

ADSI Basics
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:

* WinNT – access to Windows NT 3.51 and Windows NT4;
* LDAP – LDAP directories including Windows 2000 Active Directory, Site Server 3.0, Microsoft Exchange and third party LDAP servers;
* NDS – Novell NDS.

Benefits of accessing directories with ADSI:

* Open Architecture – Any directory provider can implement an ADSI interface;
* Directory Service Independent – Applications are not bound to a vendor's proprietary directory service since it is using an API;
* Security – ADSI supports authentication.


ADSI Architecture
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 when GetInfo or GetObject are called. Changes reside in cached memory on the client until a SetInfo is issued. SetInfo writes data back to the underlying directory store.


LDAP provider and serverless binding
The preferred method for connecting to an object is to use serverless 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")


RootDse
The RootDse 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 the domain 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.


Non-windows 2000 clients
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" )

Using the Global Catalog
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:

* Finding user's address book information;
* Looking up members of a universal group;
* Mapping the User Principal Name to a specific User Account.


Reading an object
To read an object you must first use GetObject 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.mail
Set objUser = Nothing


Updating an object
To update attributes on an existing object, you have to first bind to the object with GetObject, set each attribute's value (to update the values in the local object cache on the client), then issue a SetInfo 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.SetInfo
Set objUser = Nothing


Enumerating a container
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.Name
Next


Searching
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, use GetObject with the AdsPath
To search the Active Directory you must first create the ADO connection and command objects:

' list all objects in the domain naming context
Dim con, rs, Com
Set con = WScript.CreateObject( "ADODB.Connection" )
Set com = WScript.CreateObject( "ADODB.Command" )

Next, open the ADO provider:

' Open a connection object
con.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, or one level for searching a container.

Set Com.ActiveConnection = con
Com.CommandText = ";(objectClass=*);adspath;subtree"

' Set the preferences for Search

Com.Properties( "Page Size" ) = 512
Com.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.MoveNext
Wend




Managing Domain Information

Extracting Computer Information
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. The computer 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 the whenChanged attribute is more than a month old, the computer probably is not active on a network making periodic password changes. The whenChanged attribute is a non-replicated attribute which means it is calculated on each DC. The lastLogon attribute is not replicated between DCs; to determine the last logon time you have to examine it on all DCs.

Attrinbutes of interest:

* Name – Computer name
* dnsHostName – Full DNS hostname of the machine;
* UserAccountControl – Type of account: ADS_UF_WORKSTATION_TRUST_ACCOUNT, ADS_UF_SERVER_TRUST_ACCOUNT;
* operatingSystem – Windows NT, Windows 2000 Server, Windows 2000 Professional;
* operatingSystemVersion – Operating system version and build;
* operatingSystemServicePack – Operating system service pack numbe;r
* operatingSystemHotfix – List of hotfixes;
* whenCreated – When the object was created;
* whenChanged – When the object was modified.

Locating Computers Based on Computer Account Attributes:

Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.CommandText = _
"SELECT Name, Location, operatingSystemVersion FROM " _
& "'LDAP://DC=fabrikam,DC=com' WHERE objectClass='computer' " _
& "and operatingSystemVersion = '5.0 (2195)'"
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Timeout") = 30
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.Properties("Cache Results") = False
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
Wscript.Echo "Computer Name: " & objRecordSet.Fields("Name").Value
Wscript.Echo "Location: " & objRecordSet.Fields("Location").Value
objRecordSet.MoveNext
Loop



Trust Relation Ships
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 object that resides in the System container in the Domain naming context:

* flatName – The NetBIOS name of the domain for this trust;
* trustDirection – The direction of the established trust relationship: 0=disabled; 1=inbound; 2=outbound; 3=both (trusted and trusting);
* trustPartner – A string representing the DNS-style name of Windows 2000 domains or the NetBIOS name of down-level trust domains;
* trustType – The type of trust relationship established to the domain: 1=downlevel trust; 2=Windows 2000 trust; 3=MIT; 4=DCE.



Creating User Accounts
When creating a new user account, you must set the CN (unique in the account's OU) and Samaccountname (unique in the domain) attributes. The userPrincipalName attribute (unique in the enterprise) is optional. To create a new user in the OU:

Dim salesOU as IADsContainer
Set 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.SetInfo

usr.SetPassword "seahorse"
usr.AccountDisabled = False
usr.SetInfo



Creating Groups
There are three scopes for groups:

* Universal;
* Global;
* Domain Local.

Groups are of these types:

* Security – To control access to resources, and to use them as e-mail distribution lists;
* Distribution – Only used for e-mail distribution lists.

Group type is required. Specify an integer that contains the flags that specify the group type and scope using these combinations:

* Global security – ADS_GROUP_TYPE_GLOBAL_GROUP | ADS_GROUP_TYPE_SECURITY_ENABLED;
* Domain local security – ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP | ADS_GROUP_TYPE_SECURITY_ENABLED;
* Universal security – ADS_GROUP_TYPE_UNIVERSAL_GROUP | ADS_GROUP_TYPE_SECURITY_ENABLED;
* Domain local distribution – ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP;
* Universal Distribution – ADS_GROUP_TYPE_DOMAIN_UNIVERSAL_GROUP.

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, and samAccountName 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 group
grp.Put "groupType", ADS_GROUP_TYPE_LOCAL Or ADS_GROUP_TYPE_SECURITY_ENABLED
grp.Put "samAccountName", "DSysAdmin"
grp.SetInfo

Use the add method to add members to a group using the path to the user's object.

' Adding a user to a group
grp.Add( "LDAP://CN=James Smith,OU=Marketing,OU=DSys,DC=Northwind,DC=tld" )




Managing Enterprise Configuration Information
Configuration information is global information shared among all domains in the enterprise and usually managed by the enterprise administrator.

Partitions
The partitions container's crossRef 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 If
sConfigDN = root.Get("configurationNamingContext")
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on Get method"
End If
sContainerDN = "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 If
Next

show_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.Quit
End Sub



Listing Domain Controllers
Each domain controller in the enterprise is represented by several objects:

* A computer object residing in the Domain renaming context (the computer account for the machine);
* a nTDSDSA object (NTDS settings) residing in the Configuration naming context. NTDS settings is a container that represents an instance of the directory service; it holds the DC's inbound replication connections. If you accidentally remove a NTDS Settings, the DC owning the object will resurrect the deleted object;
* A Servers object residing under the object in the DC's site.

To get a list of all enterprise DCs, issue a query with a base path of the site's container that searches for object category nTDSDSA. Some attributes of interest:

* AdsPath - Use the full path to the object to find the parent object, which contains the DNS hostname of the DCs;
* HasMasterNCs - A multi-valued list of all the writable naming contexts. For this release of Windows 2000, these are Schema, Configuration and Domain;
* Options - Bit 1 indicates if this DC is also GC.

Listing domain controllers:

' Get the Configuration Naming Context
Set oRootDSE = GetObject("LDAP://RootDSE")
strConfigNC = oRootDSE.Get("configurationNamingContext")

' Set up the oConnectionection
set oConnection = CreateObject("ADODB.Connection")
oConnection.Provider = "ADsDSOObject"
oConnection.Open "ADs Provider"

' Build the query
strQuery = "">;(objectClass=nTDSDSA);ADsPath;subtree"

set oCmd = CreateObject("ADODB.Command")
oCmd.ActiveConnection = oConnection
oCmd.CommandText = strQuery
Set oRecordset = oCmd.Execute

' Iterate through the results
If 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
Wend
End if



FSMO
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.


Schema Master
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 of fsmoRoleOwner 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



Domain Naming Master
The Domain Naming Master is unique in the enterprise; it manages the addition and removal of domains into the forest. To identify it, read the partitions container object and examine its fsmoRoleOwner attribute:

Set objRootDse = GetObject( "LDAP://RootDse" )
Set objDomains = GetObject( "LDAP://CN=Partitions," & _
objRootDse.Get( "ConfigurationNamingContext" ))
WScript.Echo objDomains.fsmoRoleOwner



PDC Emulator
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



RID Master
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



Infrastructure Master
The Infrastructure Master is a per-domain role. To identify it, read the value of the fsmoRoleOwner attribute on the InfrastructureUpdate object for the domain:

Set objRootDse = GetObject( "LDAP://RootDse" )
Set objInfra = GetObject( "LDAP://CN=Infrastructure," & _
objRootDse.Get( "DefaultNamingContext" ))
WScript.Echo objDomains.fsmoRoleOwner



ADSI Samples
Click here to view ADSI samples.

No comments: