System Administrators are frequently asked to provide inventory of their environment. Such requests might involve generating a listing of all instances of a particular type -- for example, all users or all computers in the domain, along with their most commonly used properties. Designers of Active Directory intended for it to be much more than its Windows NT predecessor, the Security Account Manager database. The number of available attributes for each object residing in the directory not only has been substantially increased but attributes also became extensible (through schema modifications). Even though many of these attributes are available through a graphical interface of common administrative tools (such as Active Directory Users and Computers), there are also others for which the viewing or changing of requires use of the more advanced tools (such as ADSI Edit or LDAP viewer), or scripting/programming methods.
The purpose of this article is to present the scripting methods of enumerating all user and computer accounts in an Active Directory domain and listing their properties, including these not easily accessible through GUI.
In general, access to Windows 2000 domains can be obtained via one of two providers: WinNT and LDAP. The WinNT provider tends to be used more frequently, and there are two reasons for its popularity. First of all, it is easier to use. Its syntax does not require you to deal with the hierarchical structure of Active Directory. Secondly, the WinNT provider can be used to access domain-based SAM databases (residing on NT 4.0 domain controllers), local-based SAM databases (on NT 4.0/2000 member or stand alone-servers and workstations), as well as Windows 2000 domain controllers. This means that you can create a single script that will run successfully on any system in a mixed NT 4.0/2000 environment.
On the other hand, however, WinNT provider has not been designed to deal with any of new features introduced in Active Directory. It looks at it as a flat listing of accounts, without being able to recognize the multi-layered structure of Organizational Units. It does not know anything about Global Catalogs, operation masters, or sites. In addition, it can not read or write any of the new object attributes introduced in Windows 2000. Despite these drawbacks, WinNT might be sufficient for your set of requirements, even if all your clients and servers are running Windows 2000/XP.
Let's consider, for example, a situation in which you need to create a separate list of users and computers in a Windows NT/2000 domain. For each user, you want to list their username, full name, description, and home directory (separated by tabs). The script allowing you to accomplish this task would look as follows:
sDomain = "YourDomain" Set oDomain = GetObject("WinNT://" & sDomain) oDomain.Filter = Array("User") For Each oADobject In oDomain WScript.Echo oADobject.Name & vbTab & oADobject.FullName & vbTab & oADobject.Description & _ vbTab & oADobject.HomeDirDrive & vbTab & oADobject.HomeDirectory Next
Creating a similar list containing computer accounts would be just as easy:
sDomain = "YourDomain" Set oDomain = GetObject("WinNT://" & sDomain) oDomain.Filter = Array("Computer") For Each oADobject In oDomain WScript.Echo oADobject.Name Next
Of course, the list of properties that can be set for users and computers is different, hence the listing for both contains different type of values.
This approach might serve you well as long as you are not interested in Active Directory-specific attributes (or you need to know which organizational unit each user or computer belongs to). If this happens to be the case, you will need to use the LDAP provider instead.
Let's assume that we want to extract the information about Employee ID, stored in the (appropriately named) employeeID attribute, for all users in an Active Directory domain. We also want to find out which organizational unit each user belongs to. One way to accomplish this is to traverse the OU structure and, for each OU, list all users accounts located in it.
The first part can be handled by using a recursive function (the term "recursive" means that the function calls itself). The function will find all first level OUs, and for each of them find all second level OUs, and continue in the same fashion until the last level is reached. The function is called
Set oRootDSE = GetObject("LDAP://RootDSE") Set oDomain = GetObject("LDAP://" & oRootDSE.Get("DefaultNamingContext")) Call EnumOUs(oDomain.ADsPath) Sub EnumOUs(sADsPath) Set oContainer = GetObject(sADsPath) oContainer.Filter = Array("OrganizationalUnit") For Each oOU in oContainer WScript.Echo oOU.ADsPath EnumUsers(oOU.ADsPath) EnumOUs(oOU.ADsPath) Next End Sub Sub EnumUsers(sADsPath) Set oContainer = GetObject(sADsPath) oContainer.Filter = Array("User") For Each oADobject in oContainer WScript.Echo oADobject.sAMAccountName WScript.Echo oADobject.displayName WScript.Echo oADobject.Description WScript.Echo oADobject.employeeID Next End Sub
If you run this script in order to find all user accounts, you might be in for a bit or surprise, since, unlike WinNT provider, LDAP lists both users and computers. In order to filter out the user or computer accounts only, you will have to use additional ADSI properties.
For example, you can use the Class property, which indicates type of object. This would require adding a single conditional statement, which would need to be added after in the EnumUsers function after the For Each statement. This would make the function definition change to:
Sub EnumUsers(sADsPath) Set oContainer = GetObject(sADsPath) oContainer.Filter = Array("User") For Each oADobject in oContainer If oADobject.Class = "user" Then WScript.Echo oADobject.sAMAccountName WScript.Echo oADobject.displayName WScript.Echo oADobject.Description WScript.Echo oADobject.employeeID End If Next End Sub
Similarly, if you wanted to obtain the list of computers, you would compare the value of oADobject.Class property to "computer" string, and in case of the match, you could display the appropriate properties of the computer object.