In the VBScript solutions, my intention was to provide the answer in as few lines of code as necessary. Since this book is not a pure programming book, I did not want to provide a detailed explanation of how to use ADSI or WMI. If you are looking for that, I recommend Part 3 of Active Directory, Second Edition. The intent of the VBScript code is to provide you the basics for how a task can be automated and let you run with it. Most examples only take some minor tweaking to make them do something useful for you.
Just as with the GUI and CLI solutions, there are some important issues to be aware of when looking at the VBScript solutions.
I mentioned earlier that in the GUI and CLI examples I did not provide instructions for targeting a specific domain controller to perform a task. Instead, I rely on serverless binds in most cases. The same applies to the API solutions. A serverless bind for the RootDSE looks like the following in VBScript:
set objRootDSE = GetObject("LDAP://RootDSE")
That code will query the RootDSE for a domain controller in the domain of the currently logged on user. You can target a specific domain instead by simply specifying the domain name in the ADsPath:
set objRootDSE = GetObject("LDAP://apac.rallencorp.com/RootDSE")
And similarly, you can target a specific domain controller by including the server name in the ADsPath:
set objRootDSE = GetObject("LDAP://dc1/RootDSE")
So depending on how your environment is set up and what forest you want to query, you may or may not need to specify a domain or server name in the code.
Just as you might need to run the GUI and CLI tools with alternate credentials, you may also need to run your scripts and programs with alternate credentials. One way is to use the runas method described earlier when invoking the script. A better option would be to use the Scheduled Tasks service to run the script under credentials you specify when creating the task. And yet another option is to hardcode the credentials in the script. Obviously, this is not very appealing in some scenarios because you do not want the username and password contained in the script to be easily viewable by others. Nevertheless, it is a necessary evil, especially when developing against multiple forests, and I'll describe how it can be done with ADSI and ADO.
With ADSI, you can use the IADsOpenDSObject::OpenDSObject method to specify alternate credentials. You can quickly turn any ADSI-based example in this book into one that authenticates as a particular user. For example, a solution to print out the description of a domain might look like the following:
set objDomain = GetObject("LDAP://dc=apac,dc=rallencorp,dc=com") WScript.Echo "Description: " & objDomain.Get("description")
Using OpenDSObject, it takes only one additional statement to make the same code authenticate as the administrator in the domain:
set objLDAP = GetObject("LDAP:") set objDomain = objLDAP.OpenDSObject( _ "LDAP://dc=apac,dc=rallencorp,dc=com", _ "email@example.com", _ "MyPassword", _ 0) WScript.Echo "Description: " & objDomain.Get("description")
It is just as easy to authenticate in ADO code as well. Take the following example, which queries all computer objects in the apac.rallencorp.com domain:
strBase = "<LDAP://dc=apac,dc=rallencorp,dc=com>;" strFilter = "(&(objectclass=computer)(objectcategory=computer));" strAttrs = "cn;" strScope = "subtree" set objConn = CreateObject("ADODB.Connection") objConn.Provider = "ADsDSOObject" objConn.Open "Active Directory Provider" set objRS = objConn.Execute(strBase & strFilter & strAttrs & strScope) objRS.MoveFirst while Not objRS.EOF Wscript.Echo objRS.Fields(0).Value objRS.MoveNext wend
Now, by adding two lines (shown in bold), we can authenticate with the administrator account:
strBaseDN = "<LDAP://dc=apac,dc=rallencorp,dc=com>;" strFilter = "(&(objectclass=computer)(objectcategory=computer));" strAttrs = "cn;" strScope = "subtree" set objConn = CreateObject("ADODB.Connection") objConn.Provider = "ADsDSOObject" objConn.Properties("User ID") = "firstname.lastname@example.org" objConn.Properties("Password") = "MyPassword" objConn.Open "Active Directory Provider" set objRS = objConn.Execute(strBaseDN & strFilter & strAttrs & strScope) objRS.MoveFirst while Not objRS.EOF Wscript.Echo objRS.Fields(0).Value objRS.MoveNext wend
To authenticate with ADO, you need to set the User ID and Password properties of the ADO connection object. I used the UPN of the administrator for the user ID. With ADSI and ADO, you can use a UPN, NT 4.0 style account name (e.g., APAC\Administrator), or distinguished name for the user ID.
An important part of any script is error checking. Error checking allows your programs to gracefully identify any issues that arise during execution and take the appropriate action. Another best practice is to define variables before you use them and clean them up after you are done with them. In this book, most of the programmatic solutions do not include any error checking, predefined variables, or variable clean up. While admittedly this is not setting a good example, if I included extensive error checking and variable management, it would have made this book considerably longer with little value to the reader. Again, the goal is to provide you with a code snippet that shows you how to accomplish a task, not provide robust scripts that include all the trimmings.
Error checking with VBScript is pretty straightforward. At the beginning of the script include the following declaration:
On Error Resume Next
This tells the script interpreter to continue even if errors occur. Without that declaration, anytime an error is encountered the script will abort. When you use On Error Resume Next, you need to use the Err object to check for errors after any step where a fatal error could occur. The following example shows how to use the Err object.
On Error Resume Next set objDomain = GetObject("LDAP://dc=rallencorp,dc=com") if Err.Number <> 0 then Wscript.Echo "An error occured getting the domain object: " & Err.Description Wscript.Quit end if
Two important properties of the Err object are Number, which if non-zero signifies an error, and Description which will contain the error message.
As far as variable management goes, it is always a good practice to include the following at the beginning of every script:
When this is used, every variable in the script must be declared or an exception will be generated when you attempt to run the script. Variables are declared in VBScript using the Dim keyword. After you are done with a variable, it is a good practice to set it to Nothing so you release any resources bound to the variable, and don't accidentally re-use the variable with its previous value. The following code shows a complete example for printing the display name for a domain with error checking and variable management included:
Option Explicit On Error Resume Next Dim objDomain set objDomain = GetObject("LDAP://cn=users,dc=rallencorp,dc=com") if Err.Number <> 0 then Wscript.Echo "An error occured getting the domain object: " & Err.Description Wscript.Quit end if Dim strDescr strDescr = objDomain.Get("description") if Err.Number <> 0 then Wscript.Echo "An error occured getting the description: " & Err.Description Wscript.Quit end if WScript.Echo "Description: " & strDescr objDomain = Nothing strDescr = Nothing