There is little difference between the performance of a PowerShell script and an equivalent VBScript program. In both cases there are often several ways to perform the same task. Usually, if efficiency or speed is a concern, it is more important to select a method that minimizes the need to bind to objects in Active Directory. This is usually the slowest step in the script.
Several scripts were developed to compare the relative performance of PowerShell and VBScript. The task selected was to retrieve the Distinguished Names of 75 objects from their corresponding sAMAccountName values. In VBScript we used ADO and NameTranslate. In PowerShell we also used ADO and NameTranslate, to see if performance would be similar. In PowerShell we also used System.DirectoryServices.DirectorySearcher and the new AD Modules in Windows Server 2008 R2. Specifically, we used Get-ADObject. The amount of time required for each script to convert the 75 names was compared.
In addition, each script was run in two ways. First, each sAMAccountName was converted into the corresponding Distinguished Name in a separate query, so that 75 searches of Active Directory were required. Next, a query was constructed so that the 75 names would be converted in bulk, in one operation. This was always faster.
Each script was run ten times (with a 15 minute pause between runs) on a Windows 7 client with RSAT. The average time in seconds, plus or minus one standard deviation, required to convert the 75 names using each method is tabulated below.
|75 X 1||0.344 ± 0.149||1.307 ± 0.384||1.011 ± 0.088||0.191 ± 0.072||0.338 ± 0.135||0.171 ± 0.125|
|1 X 75||0.231 ± 0.079||0.607 ± 0.398||0.442 ± 0.148||0.136 ± 0.092||0.102 ± 0.105|
The row labeled "75 X 1" means that 75 queries were used. The row labeled "1 X 75" means that one query was used to convert the 75 names in bulk. I was not able to figure out how to convert an array of 75 names using NameTranslate in PowerShell.
Many factors influence the time required to retrieve the 75 Distinguished Names, including the performance of the local client, the performance of the Domain Controller that handles the queries, the speed of the network, the size of the Active Directory database, and how busy the local client and the Domain Controller are with other tasks. In addition, if a script is run twice in succession, the second run is always faster, most likely because of caching on the DC. To be fair, tests should be repeated after enough of a pause to reduce this affect.
The above tests were conducted in a test domain with no other activity. There are about 2,100 user objects in Active Directory, although performance was the same even in a domain with only 100 users. One Domain Controller is a 3 GHz dual core computer with Windows Server 2008 R2 and no FSMO roles (although it is a GC). Other DC's are Windows Server 2003 and Windows 2000 Server. I ran all of the scripts on a 2.5 GHz dual core computer joined to the domain with Windows 7 Professional 64-bit and RSAT installed. All tests would have been faster if they had been run on a Domain Controller, but I don't believe that is the usual situation.
For comparison, tests were repeated on a computer with Windows XP Pro SP2. The is a 631 MHz machine with 256 MB RAM and PowerShell V 1.0. The Get-ADObject cmdlet is not available in this version. The results follow:
|75 X 1||1.350 ± 0.184||N/A||3.994 ± 0.389||0.977 ± 0.221||0.725 ± 0.253||0.618 ± 0.245|
|1 X 75||0.297 ± 0.007||N/A||1.093 ± 0.048||0.220 ± 0.114||0.240 ± 0.099|
The results are similar on the slower computer. However, the NameTranslate
object seems to have less advantage, and using one query with the
DirectorySearcher class makes a bigger difference. In fact, one query for 75
names using the DirectorySearcher class is almost as fast on the slow
computer as on the faster.
The programs linked below were used to make all of the above tests. The first is a PowerShell script that uses the System.ActiveDirectory.DirectorySearcher class. The PropertiesToLoad method was used to specify that just the distinguishedName attribute is being retrieved. The script was slower if this method was not used. This script makes 75 queries of Active Directory to retrieve the 75 names.
Get75Users1.txt <<-- Click here to view or download the PowerShell script
The next PowerShell script uses the Get-ADObject cmdlet, part of the AD Modules included with Windows Server 2008 R2 and RSAT on Windows 7. This script makes 75 queries of Active Directory to retrieve the 75 names.
Get75Users2.txt <<-- Click here to view or download the PowerShell script
The following PowerShell script uses ADO to make 75 queries of Active Directory. The code is very similar to a VBScript solution using ADO. Notice that you specify the attribute values to be retrieved, and you can turn on paging, specify a timeout value, and disable caching.
Get75Users3.txt <<-- Click here to view or download the PowerShell script
The next PowerShell script uses the NameTranslate object 75 times to convert the sAMAccountName values into Distinguished Names. The syntax required is not intuitive, but it works and is fast.
Get75Users4.txt <<-- Click here to view or download the PowerShell script
The fifth PowerShell script, linked below, is identical to the first, using the System.ActiveDirectory.DirectorySearcher class, except that a single query is constructed to retrieve all 75 Distinguished Names at once. As expected, this is more efficient.
Get75Users5.txt <<-- Click here to view or download the PowerShell script
The next PowerShell script is the same as the second, using the Get-ADObject cmdlet, but again a single query is constructed to retrieve all 75 Distinguished Names in bulk.
Get75Users6.txt <<-- Click here to view or download the PowerShell script
The final PowerShell script linked below uses ADO, like the third script above, but constructs a single filter to retrieve all 75 Distinguished Names in one query.
Get75Users7.txt <<-- Click here to view or download the PowerShell script
Next is a VBScript program that uses ADO to query Active Directory 75 times for the 75 Distinguished Names.
Find75Users1.txt <<-- Click here to view or download the VBScript program
The second VBScript program, linked below, uses the NameTranslate object 75 times to convert the sAMAccountName values into distinguishedName values.
Find75Users2.txt <<-- Click here to view or download the VBScript program
The next VBScript program is identical to the first VBScript program above, using ADO, except that a single filter is constructed to query Active Directory once for all 75 Distinguished Names in bulk. As expected, this is faster.
Find75Users3.txt <<-- Click here to view or download the VBScript program
Finally, the following VBScript program employs NameTranslate to convert an array of the 75 sAMAccountNames into Distinguished Names at once. This is the fasted method, but it has the drawback that if even one sAMAccountName is not found in Active Directory, an error is raised and no names are converted.
Find75Users4.txt <<-- Click here to view or download the VBScript program
Two other PowerShell scripts have been compared to their VBScript equivalents. For example, the programs PSCircularNestedGroups.ps1 and CircularNestedGroups.vbs were compared. Both of these programs query all groups in Active Directory, and their direct memberships, to find all instances of circular nested groups. The time required, not counting the time required to output the results, were compared on two computers, one relatively fast and the other slower. The average time in seconds, plus or minus one standard deviation, after 5 trials, 15 minutes apart, is tabulated below.
|Computer||cpu Speed||RAM||PS Version||PowerShell||VBScript|
|Windows 7 64-bit||2.5 GHz||2 GB||2||0.262 ± 0.105||0.248 ± 0.059|
|Windows XP SP2||631 MHz||256 MB||1||0.994 ± 0.063||0.614 ± 0.055|
Next, we compare the programs PSLastLogon.ps1 and LastLogon.vbs. Both of these programs query every Domain Controller in the domain for the largest (latest) value of the lastLogon attribute for all users in the domain. My test domain has three Domain Controllers and 2,109 users. The time required for these queries, not counting the time required to output the results, were compared. The average time in seconds, plus or minus one standard deviation, after 5 trials, 15 minutes apart, is tabulated below.
|Computer||cpu Speed||RAM||PS Version||PowerShell||VBScript|
|W2k8 R2||3 GHz||1 GB||2||4.649 ± 0.071||3.250 ± 0.122|
|Windows XP SP2||631 MHz||256 MB||1||27.718 ± 0.604||7.328 ± 1.030|
PSLastLogon2.ps1, which uses the Get-ADDomainController and Get-ADUser
cmdlets in PowerShell V2, was also tested. However, this program requires that all Domain
Controllers be running on Windows Server 2008 R2, with the Active Directory
Web Services running. Only one of the Domain Controllers in my test domain
meets these requirements, so I modified the script to repeatedly query the
same Domain Controller. The results were very slow. The program required 2
minutes 32 seconds (152.311 ± 0.788 seconds).
The conclusion that should be drawn is that in most cases you should use whatever language and whatever method you are most comfortable with. Other factors, such as the availability of PowerShell or the AD Modules, may be more important. If you are transitioning from VBScript to PowerShell, it may help to use ADO in PowerShell scripts, since the code is familiar. It employs older COM technology, but it works and is not inefficient. If you have a task where performance is an issue, you can use some of the code techniques demonstrated in the linked scripts to perform tests in your own environment.
Update August 2012: The Active Directory module cmdlets, such as Get-ADUser and Get-ADObject, always retrieve a default set of properties, in addition to any others requested. It has been noted that this might account for these modules taking longer than an equivalent VBScript program that only retrieves distinguished names. The Get-ADUser cmdlet, for example, always retrieves DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname, and UserPrincipalName properties. The Get-ADObject cmdlet always retrieves the default Parameters DistinguishedName, Name, ObjectClass, and ObjectGUID. Notice also that some of these properties (Enabled, ObjectGUID, and SID) require code to convert the values in Active Directory into strings for display. To check if this makes a difference, the VBScript programs Get75Users1.vbs and Get75Users3.vbs linked on this page were modified to retrieve the attributes corresponding to the default properties retrieved by Get-ADUser. The programs were also modified to convert all of the values into strings. However, the time required was unchanged from the results shown in the first table on this page.