What Is Active Directory? An understanding with VB Script.

Active Directory is a special-purpose database — it is not a registry replacement. The directory is designed to handle a large number of read and search operations and a significantly smaller number of changes and updates. Active Directory data is hierarchical, replicated, and extensible. Because it is replicated, you do not want to store dynamic data, such as corporate stock prices or CPU performance. If your data is machine-specific, store the data in the registry. Typical examples of data stored in the directory include printer queue data, user contact data, and network/computer configuration data. The Active Directory database consists of objects and attributes. Objects and attribute definitions are stored in the Active Directory schema.

You may be wondering what objects are currently stored in Active Directory. In Windows 2000, Active Directory has three partitions. These are also known as naming contexts: domain, schema, and configuration. The domain partition contains users, groups, contacts, computers, organizational units, and many other object types. Because Active Directory is extensible, you can also add your own classes and/or attributes. The schema partition contains classes and attribute definitions. The configuration partition includes configuration data for services, partitions, and sites.

An AD domain controller authenticates and authorizes all users and computers in a Windows domain type network—assigning and enforcing security policies for all computers and installing or updating software. For example, when a user logs into a computer that is part of a Windows domain, Active Directory checks the submitted password and determines whether the user is a system administrator or normal user.

Connecting to Active Directory

There are several methods used to access Active Directory. It is recommended that you use the ADSI API to access Active Directory. ADSI implements the LDAP protocol to communicate with Active Directory. The following code examples show how to access Active Directory.

Set ns = GetObject("LDAP:")

 This opens the LDAP provider and prepares it to retrieve data. No connection is established until data is requested. When data is requested, ADSI, with the help of the locator service, attempts to find the best domain controller (DC) for the connection and will establish a connection to the server. This process is known as serverless binding.

ADSI also enables you to specify the server name to use for the connection.

Set obj = GetObject("LDAP://mysrv01")

 In another scenario, you may only know the domain name but not the specific server name. Again, ADSI enables you to specify the domain name.

Set obj = GetObject("LDAP://fabrikam.com")

 Binding to Active Directory Objects

Before continuing with this scenario, you must understand how objects are named in Active Directory. A simple analogy for understanding the object naming system can be made by looking at how files are named within a file system. Each file has a name and a path. The file name must be unique among siblings. Consider the following example: “c:publicspecsadsi25.doc”. In this case, the file name is “adsi25.doc” and the file path is “c:publicspecsadsi25.doc”.

Objects in Active Directory are named in a similar manner. Every object has an object name or relative distinguished name (RDN), and an object path or distinguished name (DN). The RDN or object name has two parts: the attribute ID and the value itself. For example, “DC=Fabrikam”. DC is an RDN attribute ID and stands for domain component, and Fabrikam is the value of the attribute. In this scenario, the distinguished name of the Fabrikam domain object is “DC=Fabrikam,DC=Com”. A DN is composed of multiple RDNs.

In ADSI, the binding string is called the ADsPath. The ADsPath contains the service provider moniker, which identifies the type of service that is being used, followed by the distinguished name of the object. LDAP or WinNT are examples of service provider monikers. So an ADsPath looks like the following:

“LDAP://DC=Fabrikam,DC=Com”

Now, you can bind to the domain object as follows:

Set dom = GetObject("LDAP://DC=Fabrikam,DC=Com")

When you run this code example, ADSI uses the DN to determine which ADSI objects to bind to. When ADSI has bound to these objects, you can access all methods available on those objects. For example, the previous code example binds to IADs and IADsContainer. You can now use methods on those interfaces such as Get, Put, Create, Delete, MoveHere, and so on.

Creating an Organizational Unit

Now that you have the domain object, you can start creating organizational units. Fabrikam has two divisions: Sales and Production. The company is planning to hire two Windows 2000 administrators to manage each division. Joe Worden, as the enterprise administrator, will create two new organizational units under the Fabrikam domain. By creating an organizational unit, Joe can group multiple objects together and let someone else manage these objects. The following code example creates the Sales Organizational Unit (OU).

Dim dom as IADsContainer
Set dom = GetObject("LDAP://DC=Fabrikam,DC=Com")
Set salesOrg = dom.Create("organizationalUnit", "OU=Sales")
salesOrg.Put "description", "Sales Headquarter,SXR"
salesOrg.Put "wwwHomePage", "http://fabrikam.com/sales"
salesOrg.SetInfo

 The IADsContainer.Create method accepts the class name and the name of the new object. At this point the object is not committed to Active Directory. You, however, will have an ADSI/COM object reference on the client. With this ADSI object, you can set or modify attributes using the IADs.Put method. The IADs.Put method accepts the attribute name and the value of the attribute. Still, nothing is committed to the directory; everything is cached at the client. When you call the IADs.SetInfo method, the changes, in this case, object creation and attribute modification, are committed to the directory. These changes are transacted, meaning that you will see either the new object with all attributes you set, or no object at all.

You can also nest organizational units. The following code example assumes that the Sales division is divided further into the East and West regions.

Set east = salesOrg.Create("organizationalUnit", "OU=East")
east.SetInfo

This also applies for the West region.

Creating a New Group

The enterprise administrator, must create a new group. He would like to secure some resources, such as file, Active Directory objects, or other objects, based on the membership of this group. The following code example shows how to create a new group.

Set ou = GetObject("LDAP://OU=Sales,DC=Fabrikam,DC=COM")
Set grp = ou.Create("group", "CN=Management")
grp.Put "samAccountName", "mgmt"
grp.Put "groupType", ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP Or ADS_GROUP_TYPE_SECURITY_ENABLED
grp.SetInfo

 This group, Management, will be created in the Sales organizational unit.

  • First, Joe must create an ADSI object for the Sales organizational unit.
  • Second, he must set the samAccountName attribute on this object, because it is a mandatory attribute for backward compatibility. For this example, when samAccountName is set, Windows NT 4.0 tools such as User Manager see the attribute as mgmt instead of Management.
  • Third, Joe must specify the type of group. In a Windows 2000 domain, there are three types of groups: Global, Domain Local, and Universal. In addition, the group carries its security characteristic. A group can be either a security-enabled or a non-secured group. Essentially, security-enabled groups are those that can be granted or denied access rights to resources, similar to a user. Granting a group access to a file share, for example, implies that all members of the group can access the file share. Distribution lists cannot be used in a similar manner—you cannot, for example, grant a distribution list the right to access a file share. During the upgrade, Windows NT 4.0 groups are migrated as security-enabled groups. Non-secured groups in Active Directory are similar to distribution lists in Exchange. Hence, creating groups or distribution lists are similar operations in Windows 2000. In the Windows 2000 native mode (native mode means that all domain controllers in a domain are computers running Windows 2000 Server), the groups can be nested to any level.

Creating New Users in the Organizational Unit

Terry Adams was hired into the Fabrikam Sales organization. His direct report is Patrick Hines.

As shown in the following code example, Joe Andreshak, the enterprise administrator, will create a new account for him.

Dim salesOU as IADsContainer
Set salesOU = GetObject("LDAP://OU=Sales,DC=Fabrikam,DC=COM")
Set usr = salesOU.Create("user", "CN=Terry Adams")
usr.Put "sAMAccountName", "terryadams"
usr.Put "userPrincipalName", "terryadams@fabrikam.com" 
usr.Put "title" "Marketing Manager"
usr.SetInfo

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

 When creating a new user, you must specify a sAMAccountName. This is a mandatory attribute for the user class. Before an instance of an object can be created, all mandatory attributes must be set. The user sAMAccountName is used to log on from computers running versions of Windows earlier than Windows 2000. Computers running Windows 2000 continue to understand the sAMAccountName. Beginning with Windows Server 2003, the sAMAccountName will automatically be generated if one is not specified for a new user.

In a Windows 2000 environment (both the client and the DC are running Windows 2000), the user can log on using either the sAMAccountName or userPrincipalName. In this example, Terry’s userPrincipalName is set to “terryadams@fabrikam.com”. If Terry moves to a different domain in the same enterprise, he can continue to use his userPrincipalName.

When creating a new user, all of the required attributes must be set in the local cache before the IADs.SetInfo method is called.

Joe, as an administrator, can assign Terry’s password using the IADsUser.SetPassword method. The IADsUser.SetPassword method will not work until the IADs.SetInfo method has been called.

Then, Joe enables the user account by setting the IADsUser.AccountDisabled property to FALSE.

The following code example shows how to set Terry as Patrick’s manager.

Set usr = GetObject("LDAP://CN=patrickhines, OU=Sales, DC=Fabrikam, DC=COM")
usr.Put "manager", "CN=Terry Adams,OU=Sales, DC=Fabrikam,DC=COM"
usr.SetInfo

You may wonder what happens if Terry changes his name, moves to a different organization, or leaves the company. Who maintains this manager-direct report link? For more information, and the solution to this problem, see Reorganization. Because the Active Directory schema is extensible, you can model your objects to include similar manager-direct report style relationships.

Before going on to the next task, look at a code example that shows how Joe would view Terry’s direct reports.

Set usr = GetObject("LDAP://CN=Terry Adams, OU=Sales, DC=Fabrikam, DC=COM")
reports = usr.GetEx ("directReports")

For each directReport in reports
    Debug.Print directReport
Next

In this code example, Patrick will display as Terry’s direct report, even though the directReports attribute was never modified. Active Directory does this automatically.

In the directory world, an attribute can have single or multiple values. Because directReports has multiple values, you can get this information by looking at the schema, it is easier to use the IADs.GetEx method, which returns an array of values regardless of whether single or multiple values are returned.

The Active Directory Users and Computers snap-in lets you view direct reports and manager relationships on the user’s property page.

Adding Users to a Group

Joe Worden, the enterprise administrator, will now add Julie Bankert to the Management group. To add an object to a group, use the IADsGroup.Add method.

Dim grp as IADsGroup

Set grp = GetObject("LDAP://CN=Management,OU=Sales,DC=Fabrikam,DC=COM") 
grp.Add ("LDAP://CN=Julie Bankert,OU=Sales,DC=Fabrikam,DC=COM")

Moving Existing Users to the Organizational Unit

When the enterprise administrator, Joe Worden, upgrades the Windows NT 4.0 domain to Active Directory, all users and groups are migrated to the Users containers in the Fabrikam domain. Joe can now move those users and groups to the appropriate organizational units. An object can also be moved between related Windows 2000 domains using ADSI.

In the following code example, Joe moves “jeffsmith” to the Sales organization.

Set usr = salesOU.MoveHere("LDAP://CN=jeffsmith,CN=Users,DC=fabrikam,DC=com", vbNullString)

The IADsContainer.MoveHere method takes the ADsPath of the object to be moved and the new object name (RDN). To keep the same name, you can specify NULL (vbNullString) for the bstrNewName parameter. To rename the object when it is moved, specify the new relative distinguished name for the bstrNewName parameter. For example, to move jeffsmith to the sales organization and rename the “jeffsmith” object to “jeff_smith” in the same operation, Joe would execute the following code:

Set usr = salesOU.MoveHere("LDAP://CN=jeffsmith,CN=Users,DC=fabrikam,DC=com", "CN=jeff_smith")

Adding Users to a Group

Joe Worden, the enterprise administrator, will now add Julie Bankert to the Management group. To add an object to a group, use the IADsGroup.Add method.

Dim grp as IADsGroup

Set grp = GetObject("LDAP://CN=Management,OU=Sales,DC=Fabrikam,DC=COM") 
grp.Add ("LDAP://CN=Julie Bankert,OU=Sales,DC=Fabrikam,DC=COM")

 Enumerating Objects

To view the child object of a container, such as an organizational unit (OU), enumerate the container object. To make an analogy to a file system, the child object would correspond to files in the directory, while the container, which is the parent object, would correspond to the directory itself. You may also use the enumerate operation when you want to get the parent object of an object.

When you enumerate an object, you actually bind to an object in the directory, and an IADs interface is returned on each object.

The following code example shows how to enumerate the children of a container.

Dim ou As IADs
' Bind to an object using its DN.
On Error GoTo Cleanup

Set ou = GetObject("LDAP://OU=Sales, DC=Fabrikam, DC=COM")

For each child in ou
    Debug.Print child.Name
Next

Cleanup:
    If (Err.Number<>0) Then
        MsgBox("An error has occurred. " & Err.Number)
    End If
    Set ou = Nothing

You can filter the types of objects returned from the enumeration. For example, to display only users and groups, use the following code example before the enumeration.

Ou.Filter = Array("user", "group")

If you have an object reference, you can get the object’s parent using the IADs Parent property. The following code example shows how to bind to the parent object.,pre>parentPath = obj.Parent
Set parent = GetObject(parentPath)

parentPath = obj.Parent
Set parent = GetObject(parentPath)

Searching for Objects

Julie Bankert must find telephone numbers for all Program Managers who work in Department 101. She can create a script that uses ADO and ADSI to accomplish this.

When using the ADO provider to perform a query, the result set will only return the first 1000 objects. For this reason, it is important to use a paged query so that you can retrieve all of the objects in the result set, as long as the directory service has not been set to limit the number of items in a result set. Return sets will contain the number of items that you specify in the page size, and paged sets will continue to be returned until all items in the result set have been returned.

' Create the connection and command object.
Set oConnection1 = CreateObject("ADODB.Connection")
Set oCommand1 = CreateObject("ADODB.Command")
' Open the connection.
oConnection1.Provider = "ADsDSOObject"  ' This is the ADSI OLE-DB provider name
oConnection1.Open "Active Directory Provider"
' Create a command object for this connection.
Set oCommand1.ActiveConnection = oConnection1

' Compose a search string.
oCommand1.CommandText = "select name,telephoneNumber " & _
"from 'LDAP://DC=Fabrikam,DC=com'" & _
"WHERE objectCategory='Person'" & _
"AND objectClass='user'" & _
"AND title='Program Manager'" & _
"AND department=101"

' Execute the query.
Set rs = oCommand1.Execute
'--------------------------------------
' Navigate the record set
'--------------------------------------
While Not rs.EOF
    Debug.Print rs.Fields("Name") & " , " & rs.Fields("telephoneNumber")
    rs.MoveNext
Wend

 To perform an ADSI search in Visual Basic or a scripting environment, three ADO components are required: Connection, Command, and Recordset. The Connection object enables you to specify the provider name, alternate credentials, if applicable, and other flags. The Command object enables you to specify search preferences and the query string. You must associate the Connection object to a Command object before the execution of the query. Finally, the Recordset object is used to iterate the result set.

ADSI supports two types of query strings or dialects. The preceding code example uses the SQL dialect. You can also use the LDAP dialect. The LDAP dialect query string is based on RFC 2254 (an RFC is a Request For Comments document, which is the basis for developing LDAP standards). The preceding example can be translated to the following code example.

oCommand1.CommandText = ";" & _
"(&(objectCategory=Person)" & _
"(objectClass=user)" & _
"(title=ProgramManager)" & _
"(department=101));" & _
"name,telephoneNumber;subTree"

Why is the word “subtree” at the end of the string? In the directory world, you can specify the scope of search. The choices are: “base”, “onelevel”, and “subtree”. “base” is used to read the object itself; “onelevel” refers to the immediate children, similar to the dir command; “subtree” is used to search deep or down multiple levels (similar to dir /s).

With the SQL dialect, you can specify scope in the command property, such as in the following code example.

oCommand1.Properties("SearchScope") = ADS_SCOPE_ONELEVEL

If scope is not specified, by default it uses a subtree search.

Reorganization

The sales organization has moved to a new organization — “Sales and Support.” Julie Bankert has been promoted to vice president and will lead the new organization. Joe Worden, the enterprise administrator, must move Sales OU to a new OU, Sales and Support.

Set dom = GetObject("LDAP://DC=Fabrikam, DC=COM")
Set salesSupport = dom.Create("organizationalUnit", "CN=Sales and Support")
Set sales = salesSupport.MoveHere("LDAP://OU=Sales, DC=Fabrikam, DC=COM", vbNullString)

With this code example, all objects in the sales organizational unit, including the sub-organizational units, are moved to the new organizational unit.

Now, Joe can move Julie into the Sales and Support organizational unit.

Set usr = salesSupport.MoveHere("LDAP://CN=Julie Bankert, OU=Sales, OU=Sales and Support, DC=Fabrikam,DC=COM")
usr.Put "title", "Vice President"
usr.SetInfo

Be aware that the manager-direct report link between Julie Bankert and Chris Gray is automatically updated by Active Directory.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Powered by WordPress.com.

Up ↑

%d bloggers like this: