WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.



Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.




ADSI Extender

The WinBatch ADSI extender provides access to the powerful functionality of Microsoft's Active Directory Service Interfaces (ADSI) in a style familiar to WinBatch users. With the ADSI extender, you can manage network resources in several directory services with a single, easy to use, set of functions. Please note: We're assuming you have some prior knowledge of how to use Active Directory Services Interfaces in order to effectively utilize this extender. Along with a general understanding of ADSI, it goes without saying that you will need to have knowledge about the directory service that you will be using this extender to manipulate.

The extender allows you to create directory service objects including users, computers, groups and organizational units. It also allows you to change object attributes and move objects from one location to another. There are special functions for managing object membership in security and distribution groups. In addition, there are functions for investigating the directory structure including LDAP style search functions, and functions for handling Active Directory object security. To access the functions in this extender, add the following to your script:

Not all functions in this extender work with all directory service providers. Some directory services do not have the built-in structure to support some functions.

The LDAP provider supplied with the ADSI client software should allow the ADSI extender to support most LDAP directory services. However, the focus of this extender is Active Directory, and Exchange 5.5.

There is a special ADSI forum for posting your comments/questions at our WinBatch Technical Support Forum:

Novell support:
The current release of the ADSI extender has not been tested with Novell 3.x services (NWCOMPAT:).

If you do choose to use the ADSI extender to access a Novell Netware server, you will need to install the appropriate Novell NetWare gateway and client services on your client computer. On Windows NT 4.0 machines, you will need to re-install the ADSI client software after you install the gateway and client services software. If you do not have either Novell NetWare gateway and client services or the ADSI client on your Windows NT 4.0 machine, make sure you install Novell NetWare gateway and client services before you install the ADSI client.

Microsoft does not provide an ADSI client for Windows ME so this extender cannot be used on computers running Windows ME.

Some useful numbers:
Some properties require that one or more bits be set in a bit field or integer. The WIL language provides the Bitwise operators, such as OR "|" for this purpose. Of course, it is also helpful to know the meaning of the bits in a property you are about to set, so check out the file CONSTANTS.WBT. It is packaged with the ADSI extender and can be found in your WinBatch directory. The file gives (hopefully) meaningful names to the numeric constants you need to set properties and supply values to extender function.

Here are a couple of examples of setting directory service properties using constants from CONSTANTS.WBT.

Active Directory group object's groupType property can have one of the first four identifiers. Optionally, any of these values can be ORed with the last identifier, SECURITY_ENABLED.

dsSetProperty(sGroupPath, "groupType", nValue)

The Windows NT user object has the property UserFlags. It can have one or more of the following values ORed together. (Not all combinations may be valid.)



; Preserve existing values by getting the property first.
nUserFlags = dsGetProperty(sUserPath, "Userflags")

; Add new bit to current bit field.
nUserFlags = nUserFlags | UF_DONT_EXPIRE_PASSWD

dsSetProperty( sUserPath, "Userflags", nUserFlags)

ADSI Paths Explained

Most functions in the ADSI extender accept an ADSI path as their first parameter. An ADSI Path is a case-sensitive string, which uniquely identifies an ADSI object on any given directory service. Because ADSI objects exist within the context of the namespace of the underlying directory service, part of the syntax of an ADSI path name is provider-specific.

Microsoft ships four providers with ADSI:

  • WinNT:
  • LDAP:
  • GC:
The initial elements of the ADSI path string are the programmatic identifier of the ADSI provider, followed by "://", followed by whatever syntax is dictated by the provider namespace.

Here are some example ADSI paths:

LDAP://cn=Homer Simpson,o=simpsons,c=us


The Microsoft LDAP provider ADsPath requires the following format.
The "HostName" can be a computer name, an IP address, or a domain name. A server name can also be specified in the binding string. Most LDAP providers follow a model that requires a server name to be specified.

The "PortNumber" specifies the port to be used for the connection. If no port number is specified, the LDAP provider uses the default port number. The default port number is 389 if not using an SSL connection or 636 if using an SSL connection.

The "DistinguishedName" specifies the distinguished name of a specific object. A distinguished name for a given object is guaranteed to be unique.

The following table lists examples of binding strings.

LDAP ADsPath example Description
LDAP: Bind to the root of the LDAP namespace.
LDAP://server01 Bind to a specific server.
LDAP://server01:390 Bind to a specific server using the specified port number.
LDAP://CN=Jeff Smith,CN=users,DC=fabrikam,DC=com Bind to a specific object.
LDAP://server01/CN=Jeff Smith,CN=users,DC=fabrikam,DC=com Bind to a specific object through a specific server.

If Kerberos authentication is required for the successful completion of a specific directory request, the binding string must use either a serverless ADsPath, such as LDAP://CN=Jeff Smith,CN=users,DC=fabrikam,DC=com, or it must use an ADsPath with a fully-qualified DNS server name, such as LDAP:// Smith,CN=users, DC=fabrikam,DC=com. Binding to the server using a flat NETBIOS name or a short DNS name, for example, using the name server01 instead of, is not guaranteed to yield Kerberos authentication.

To find all providers installed in your computer you can use ADs: as the programmatic identifier:

; Gets all providers since ADs: is a container.
lProviders = ""
lProviders = dsGetChldPath("ADs:", "")
Message("Available providers", lProviders)
LDAP Special Characters

LDAP has several special characters which are reserved for use by the LDAP API. The list of special characters can be found in Distinguished Names. To use one of these characters in an ADsPath without generating an error, the character must be preceded by a backslash (\) character. This is known as escaping the character. For example, if a user name is given in the form of "<last name>,<first name>", the comma in the name value must be escaped. The resulting string would look like this:

The escaped character can also be specified by its two digit hexadecimal character code. This is shown in the following example.
Non-printable characters, such as the line feed and carriage return, must be escaped and specified by their two digit hexadecimal character code. This is shown in the following example.
For more information about the distinguished name notation used by LDAP-compliant directory services, see RFC 1669.

Characters that have special meaning when referring to an ADSI path are listed in the following table.

Character Comment
" Used to quote any part of the ADSI Path that may contain a special character so the text within the quotes is interpreted literally. For example, "CN=Gate/Mr."
\ Escape character, signifying that the following special character is used as a literal.. For example, CN=Gate\ /Mr.
/ Component separator.
, Component separator.
<> Delimits an ADSI path within another naming convention or another naming convention within an ADSI path.

The ADSI extender also supports binding with COM globally unique identifiers (GUIDs). However, binding to an ADSI container object only allows you to perform a limited number of operations on the container object. These include the examination of its attributes and the enumeration of its immediate children. This is because binding with a GUID (or SID) is intended for low overhead, fast binds.

For example:

; Substitute a container GUID for xxxxxxxx-xxx-xxxx-xxxxxxxx
sGuidPath = "LDAP://MyDomain/<GUID=xxxxxxxx-xxx-xxxx-xxxxxxxx>"
cn = dsGetProperty(sGuidPath, "cn")
Message("Common name", cn)
lChildren = dsGetChldPath(sGuidpath, "")
Message("Contains",  lChildren)
Notice that, in this situation, the GUID must not contain the enclosing braces ({}). For most other uses, the extender expects the GUID to be framed by a right and left brace.

ADSI Paths and Server-less Binding

Active Directory supports server-less binding, so you can access Active Directory on the default domain without specifying the name of a domain controller. The ADSI extender processes your server-less path calls by finding the domain associated with the current security context of the thread that's running your script. This is useful when you do not want to "hard code" the domain name in your script. Examples of ADSI paths that take advantage of server-less binding include "LDAP://rootDSE" and "GC:" See the discussion in the following sections for examples using server-less paths and for more information on these two ADSI paths in particular.

B>ADSI Paths and LDAP's rootDSE

In LDAP 3.0, rootDSE is defined as the root of the directory information tree on a directory server. The purpose of rootDSE is to provide information about the directory server.

Use "LDAP://rootDSE" or "LDAP://myservername/rootDSE" as ADSI paths (substitute a computer name for myservername) to access the rootDSE object.

Here is an example of obtaining information from rootDSE and then using it to access an Active Directory container:

; Notice the server-less ADSI path.
;  Get the domain DN with a server-less ADSI path.
domain = dsGetProperty("LDAP://rootDSE"  , "defaultNamingContext")
sPath = "LDAP://cn=users,%domain%"
; List all objects in the user container of the default domain.
lValues = dsGetChldPath(sPath, "")
Message("Object in Users", lValues)
ADSI Paths and the Global Catalog

With Active Directory, you can also use the "GC:" programmatic identifier to access the Global Catalog. The Global Catalog is a namespace that contains directory information from all domains in a forest. It is a partial replica of every domain directory so it does not contain all the properties of each object. It is useful for performing quick searches.

For example:

; GC always has just one child object.
OneChildPath = dsGetChldPath("GC:", "")
lfound = dsFindPath(OneChildPath, "(cn=Homer Simpson)")

Permitting Access to a Single Object Property

Property-specific permissions can be used to provide granular delegation of administration. You can set a property-specific ACE to allow a specified user or group to read and/or write a specific attribute on a specified object. For example, you could set an ACE on an organizational unit to allow a group to read and write the telephone number attribute of all user objects in the organizational unit.


   ; Get the schema path using server-less binding.
   sAdsiPath = "LDAP://rootDSE"
   sSchemaDN = dsGetProperty(sAdsiPath , "schemaNamingContext")

   ; Create full schema object paths.
   sSchemaPropPath = "LDAP://cn=Telephone-Number,%sSchemaDN%"
   sSchemaClassPath = "LDAP://cn=User,%sSchemaDN%"

   ; Organizational Unit path
   sAdsiPath = "LDAP://smdomain/OU=sales,DC=smdonaim,DC=ConGlom,DC=com"
   ; Get GUID of the property.
   sPropGUID = dsGetProperty(sSchemaPropPath , "schemaIDGUID")
   ; Get the GUID of the class that can inherit the new ACE.
   sClassGUID = dsGetProperty(sSchemaClassPath, "schemaIDGUID")
   ; Constants from the supplied file, CONSTANTS.WBT
   ACCESS_ALLOWED                  = 0
   INHERIT_ACE                     = 2
   INHERIT_ONLY_ACE                = 8
   OBJECT_TYPE_PRESENT             = 1
   DS_READ_PROP                    = 16
   DS_WRITE_PROP                   = 32
   ; Create a new ACE to allow reading and writing of phone numbers.
   Ace = dsCreatSecObj(sAdsiPath, 3)
   dsSetSecProp(Ace, "AceType", ACCESS_ALLOWED )
   dsSetSecProp(Ace, "ObjectType",  sPropGUID )
   dsSetSecProp(Ace, "InheritedObjectType", sClassGUID)
   dsSetSecProp(Ace, "AceFlags", INHERIT_ACE | INHERIT_ONLY_ACE)
   dsSetSecProp(Ace, "AccessMask",DS_READ_PROP | DS_WRITE_PROP)
   dsSetSecProp(Ace, "Trustee","hrdomain\hr group")
   ;Get the OU's SD  and DACL
   Sd  = dsGetProperty(sAdsiPath, "ntSecurityDescriptor")
   Acl = dsGetSecProp(sd, "DiscretionaryAcl")
   ; Add the ace to the object
   dsAclAddAce(Acl, Ace, -1)
   dsSetSecProp(Sd, "DiscretionaryAcl", Acl)
   dsSetProperty(sAdsiPath, "ntSecurityDescriptor", Sd)

Set Active Directory User Login Times

The ADSI extender represents the Active Directory logonhours property as a space-delimited list of 21 decimal numbers. Each number is a byte value and represents a block of 8 hours within a day in the week. The order is as follows: item 1 is Saturday 16:00-23:59, item 2 is Sunday 00:00-7:59, item 3 is Sunday 8:00-15:59, item 4 is Sunday 16:00-23:59, item 5 is Monday 00:00-7:59,..., item 21 is Saturday 8:00-15:59. Within each item (byte), each bit represents a unique hour in the week. For item 2, the first (right most) bit is Sunday, 0:00 to 0:59; and so on.

In the example below, we prevent a workaholic user from logging in after noon on Saturday so we can perform system maintenance.


;  A few descriptive constants
SAdsiPath = “LDAP://BIGCO/cn=BK OverWork,cn=users,dc=BIGCO,dc=COM”
Delimiter = " "
Hour1 = 1
Hour2 = 2
Hour3 = 4
Hour4 = 8
Hour5 = 16
Hour6 = 32
Hour7 = 64
Hour8 = 128
sHours = dsGetProperty(sAdsiPath,   "logonHours" )
; Remove Saturnday's 8am to 4pm byte
sHours = ItemRemove( 21, sHours, Delimiter)
; Set only the least significant 4 bits of the 8am to 4pm byte.
; The most significant 4 remain clear to deny access for the last 4 hours
; of the period so the user can not logon after 11:59 pm on Saturday.
nByte = Hour1 + Hour2 + Hour3 + Hour4
sHours = ItemInsert( nByte, -1, sHours, Delimiter)
; Deny the last period for Saturday by removing
; and adding the first item (byte) of the list.
sHours = ItemRemove( 1, sHours, Delimiter)
sHours = ItemInsert( 0, 0, sHours, Delimiter)
; Deny all of Sunday by first removing the existing bytes…
sHours = ItemRemove( 2, sHours, Delimiter)
sHours = ItemRemove( 2, sHours, Delimiter)
sHours = ItemRemove( 2, sHours, Delimiter)
; And then inserting zeros in their place.
sHours = ItemInsert( 0, 1, sHours, Delimiter)
sHours = ItemInsert( 0, 1, sHours, Delimiter)
sHours = ItemInsert( 0, 1, sHours, Delimiter)
; Set the new value.
dsSetProperty(sAdsiPath, "logonHours", sHours)


When the ADSI extender was written we really didn't have a feel for how people would use it. We opted to make it easy to use with as few steps as possible. We felt that it was the only way to give it some advantage over VBS or VB and COM. One way we did this was to remove the need save cached object changes to the Directory Service. The extender does this in the background every time you make a change to a property. While this makes ADSI a little less error prone and easier to use, it also adds a lot of network traffic. (With straight COM objects you have to tell ADSI to update the DS with your cached changes or they are lost.)

The problem is that the extender is prone to timing issues because of all the extra network traffic it generates. We may be making changes to the extender to work around this problem. But right now I recommend using COM whenever possible.

Sample code to get and set a user full name:

newname = "Fred Flintstone"
objUser = ObjectGet("WinNT://YourDomain/UserID")
origname  = objUser.FullName
ret = AskYesNo('Change Users Full Name', StrCat('Are you sure you want to change the users full name from ',origname,' to ',newname,'?'))
If ret == @YES
   objUser.FullName = "Users Name"
objUser = ""
Message('Changed Users Full Name', StrCat('Changed ',origname,' to ',newname))
Microsoft offers a tool called ADSI Scriptomatic. It is designed to help you write ADSI COM scripts; that is, scripts that can be used to manage Active Directory. The ADSI Scriptomatic also teaches you an important point about ADSI scripting: like WMI, there are consistent patterns to ADSI scripts.

ADSI Reference Links

ADSI Extender Technicial Support Database

ADSI Extender Forum Conference

ADSI Schema Attributes

Active Directory Service Interfaces

Windows Scripting Host / ADSI Cookbook

Article ID:   W17353
File Created: 2010:02:17:10:58:06
Last Updated: 2014:07:18:12:09:28