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 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 (at random intervals between 10 and 15 minutes apart) 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.
| Language | PowerShell | PowerShell | PowerShell | PowerShell | VBScript | VBScript |
| Method | Searcher | Get-ADObject | ADO | NameTranslate | ADO | NameTranslate |
| 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:
| Language | PowerShell | PowerShell | PowerShell | PowerShell | VBScript | VBScript |
| Method | Searcher | Get-ADObject | ADO | NameTranslate | ADO | NameTranslate |
| 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 |
The program
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.