I've been using PowerShell for a long while now using Hashtables, OrderedDictionary, and other types of data types in PowerShell, but I never paid attention to how powerful those are. And I don't mean your general knowledge about hashtables that is already covered by Kevin Marquette in his article Everything you wanted to know about Hashtables or my article PowerShell – Few tricks about HashTables and Arrays I wish I knew when I started. Let's find out, how Powerful they are, shall we?
You see, I have this little PowerShell module called PSWinDocumentation.AD. In one of the functions, it gets all users, all groups and all computers to finally create a version of it where each User gets Manager Name and Manager Email address, along with Group Names the user belongs. For this article, I'll simplify things and get just the users SamAccountName and Managers field. The way the manager is stored for a user is by DistinguishedName. So when you want to get a manager that is attached to the user you would do something like this:
$UsersAll = Get-ADUser -Properties Manager, DisplayName, EmailAddress -Filter '*' $Users = foreach ($_ in $UsersAll) { [PSCustomobject] @{ SamAccountName = $_.SamAccountName Manager = $_.Manager } } $Users | Format-Table -AutoSize
But it doesn't look pretty, right? You don't want that. You want to show the Manager's name and Managers email address, but to do that you need to ask Active Directory for Manager based on the field you have (DN). You can do it by asking AD, or you can use the full list you already have for your users. If you went with the first option, it would mean for each user another query to AD. In the case of reusing UsersAll variable, you're working in memory, which is much faster.
$UsersAll = Get-ADUser -Properties Manager, DisplayName, EmailAddress -Filter '*' $UsersWithManagers = foreach ($User in $UsersAll) { [PSCustomobject] @{ SamAccountName = $User.SamAccountName Manager = $User.Manager ManagerDisplay = ($UsersAll | Where-Object { $User.Manager -eq $_.DistinguishedName }).DisplayName ManagerEmail = ($UsersAll | Where-Object { $User.Manager -eq $_.DistinguishedName }).EmailAddress } } $UsersWithManagers | Format-Table -AutoSize
Much better right? Now in my test domain, the first query was 40 milliseconds, the second query was 185 milliseconds. Not that big deal, right? My test domain is about 50 users; that's why the results aren't worrying. But if you test this on a domain with 4412 users it's going to give you 2 seconds and 94 milliseconds. However, the version with the Manager's Name and Manage's Email address will execute in 15 minutes and 49 seconds and 406 milliseconds. Not so fun right? So how one can optimize this? Well, first of all, I was using Where-Object two times for the same object. Let's change it and compare results.
$UsersAll = Get-ADUser -Properties Manager, DisplayName, EmailAddress -Filter '*' $UsersWithManagers = foreach ($User in $UsersAll) { $Manager = ($UsersAll | Where-Object { $User.Manager -eq $_.DistinguishedName }) [PSCustomobject] @{ SamAccountName = $User.SamAccountName Manager = $User.Manager ManagerDisplay = $Manager.DisplayName ManagerEmail = $Manager.EmailAddress } } $UsersWithManagers | Format-Table -AutoSize
It took 7 minutes and 54 seconds. Better? Yes! But the thing about Where-Object is, that while the syntax is excellent and very useful, it's quite slow. While it works fine on smaller arrays, it struggles with more substantial items. So how can we replace Where-Object? Well, Where-Object is essentially a foreach loop. It merely checks each entry in an array against the given condition. So let's use that!
$UsersAll = Get-ADUser -Properties Manager, DisplayName, EmailAddress -Filter '*' $UsersWithManagers = foreach ($User in $UsersAll) { $Manager = foreach ($_ in $UsersAll) { if ($User.Manager -eq $_.DistinguishedName) { $_ break } } [PSCustomobject] @{ SamAccountName = $User.SamAccountName Manager = $User.Manager ManagerDisplay = $Manager.DisplayName ManagerEmail = $Manager.EmailAddress } } $UsersWithManagers | Format-Table -AutoSize
Few more lines more, a bit worse syntax, but a much better results! Just 59 seconds and 367 milliseconds!
While 59 seconds doesn't seem much, things quickly add up as you try to play with more significant data. My domain is just 4000 users. What about 10000 or 40000. What if more fields need to be looked up. Well, this is where Hashtables come in. Let's think for a second what happens in a foreach loop that I've shown above. For every User that I'm checking Manager, I go and check 4000 or fewer users (assuming manager is found sooner rather than later). And I do this every time. So 4000 times I execute another loop with 4000 users, which if my math is correct gives 16000000 (16 million loops – the real value should be smaller due to using break if entry is found earlier). It's even worse in my first example. I was doing it twice with Where-Object, which is very slow. Let's introduce hashtable into the mix.
$UsersAll = Get-ADUser -Properties Manager, DisplayName, EmailAddress -Filter '*' # This time we will prepare Hashtable that will keep DistinguishedName as a key $Optimize = @{} foreach ($_ in $UsersAll) { $Optimize.($_.DistinguishedName) = $_ } # End of preparations $UsersWithManagers = foreach ($User in $UsersAll) { if ($null -ne $User.Manager) { $Manager = $Optimize.($User.Manager) } else { $Manager = $null } [PSCustomobject] @{ SamAccountName = $User.SamAccountName Manager = $User.Manager ManagerDisplay = $Manager.DisplayName ManagerEmail = $Manager.EmailAddress } } $UsersWithManagers | Format-Table -AutoSize
As you can see above things have complicated a bit in the code. I've basically created HashTable with DistinguishedName as a key and all user data as a value. Then I simply check each Manager DistinguishedName if it exists in the hashtable. The result? 7 seconds and 952 milliseconds. Amazing right? While it's an added time to set it up and you may see a few added seconds it pays off in the end. Of course, if you have 50 users domain, you may not notice anything, or you may even see the slowdown (added few seconds). But for larger domains, well this rocks! But that's not all. After posting this article on Reddit @Vortex100 suggested that there's an even faster way to use hashtables (you gotta love Reddit right?!). Using it this way
Measure-Command {$a = @{};1..10000 | % {$a.$_ = $_}} TotalMilliseconds : 10373.6452
Is a bit slower then using it like this
Measure-Command {$b = @{};1..10000 | % {$b.add($_,$_)}} TotalMilliseconds : 55.7214
Same thing applies to accessing it the way I did
$a.someproperty
Or, accessing it as he proposed
$a[$someproperty]
With this knowledge in mind the final code should actually look like this
$UsersAll = Get-ADUser -Properties Manager, DisplayName, EmailAddress -Filter '*' # This time we will prepare Hashtable that will keep DistinguishedName as a key $Optimize = @{} foreach ($_ in $UsersAll) { $Optimize.Add($_.DistinguishedName,$_) } # End of preparations $UsersWithManagers = foreach ($User in $UsersAll) { if ($null -ne $User.Manager) { $Manager = $Optimize[$User.Manager] } else { $Manager = $null } [PSCustomobject] @{ SamAccountName = $User.SamAccountName Manager = $User.Manager ManagerDisplay = $Manager.DisplayName ManagerEmail = $Manager.EmailAddress } } $UsersWithManagers | Format-Table -AutoSize
Or as @Ta11ow (PowerShell Hero 2019 himself) suggested using the index operator instead of a method for a bit nicer syntax
$UsersAll = Get-ADUser -Properties Manager, DisplayName, EmailAddress -Filter '*' # This time we will prepare Hashtable that will keep DistinguishedName as a key $Optimize = @{} foreach ($item in $UsersAll) { $Optimize[$item.DistinguishedName] = $item } # End of preparations $UsersWithManagers = foreach ($User in $UsersAll) { if ($null -ne $User.Manager) { $Manager = $Optimize[$User.Manager] } else { $Manager = $null } [PSCustomobject] @{ SamAccountName = $User.SamAccountName Manager = $User.Manager ManagerDisplay = $Manager.DisplayName ManagerEmail = $Manager.EmailAddress } } $UsersWithManagers | Format-Table -AutoSize
How this impacts PSWinDocumentation.AD? Well, let's see a similar, but a bit more demanding query.
VERBOSE: Getting all information - Start VERBOSE: Getting forest information - Start VERBOSE: Getting forest information - Domains VERBOSE: Getting domain information - domain.test DomainGroupsFullList VERBOSE: Getting domain information - domain.test DomainGroupsFullList - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 841 milliseconds VERBOSE: Getting domain information - domain.test DomainUsersFullList VERBOSE: Getting domain information - domain.test DomainUsersFullList - Time: 0 days, 0 hours, 0 minutes, 14 seconds, 462 milliseconds VERBOSE: Getting domain information - domain.test DomainComputersFullList VERBOSE: Getting domain information - domain.test DomainComputersFullList - Time: 0 days, 0 hours, 0 minutes, 5 seconds, 701 milliseconds VERBOSE: Getting domain information - domain.test DomainUsers VERBOSE: Getting domain information - domain.test DomainUsers - Time: 0 days, 0 hours, 14 minutes, 44 seconds, 473 milliseconds VERBOSE: Getting domain information - domain.test DomainUsersCount VERBOSE: Getting domain information - domain.test DomainUsersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 3 milliseconds VERBOSE: Getting domain information - domain.test - Time to generate: 0 days, 0 hours, 15 minutes, 5 seconds, 509 milliseconds VERBOSE: Getting forest information - Domains - Time: 0 days, 0 hours, 15 minutes, 5 seconds, 510 milliseconds VERBOSE: Getting forest information - Stop - Time to generate: 0 days, 0 hours, 0 minutes, 0 seconds, 121 milliseconds VERBOSE: Getting all information - Stop - Time to generate: 0 days, 0 hours, 15 minutes, 5 seconds, 640 milliseconds
The test, even with optimizations in place (using foreach, instead of Where-Object) takes 15 minutes and 5 seconds. Again, you should consider my domain count for combined users, groups and computers is about 8000 ad objects. But there are domains much bigger than those. Let's see how a bit of hashtable magic changed the same code?
VERBOSE: Getting all information - Start VERBOSE: Getting forest information - Start VERBOSE: Getting forest information - Domains VERBOSE: Getting domain information - domain.test DomainGroupsFullList VERBOSE: Getting domain information - domain.test DomainGroupsFullList - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 226 milliseconds VERBOSE: Getting domain information - domain.test DomainUsersFullList VERBOSE: Getting domain information - domain.test DomainUsersFullList - Time: 0 days, 0 hours, 0 minutes, 19 seconds, 663 milliseconds VERBOSE: Getting domain information - domain.test DomainComputersFullList VERBOSE: Getting domain information - domain.test DomainComputersFullList - Time: 0 days, 0 hours, 0 minutes, 11 seconds, 333 milliseconds VERBOSE: Getting domain information - domain.test DomainUsers VERBOSE: Getting domain information - domain.test DomainUsers - Time: 0 days, 0 hours, 0 minutes, 47 seconds, 146 milliseconds VERBOSE: Getting domain information - domain.test DomainUsersCount VERBOSE: Getting domain information - domain.test DomainUsersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 12 milliseconds VERBOSE: Getting domain information - domain.test - Time to generate: 0 days, 0 hours, 1 minutes, 19 seconds, 434 milliseconds VERBOSE: Getting forest information - Domains - Time: 0 days, 0 hours, 1 minutes, 19 seconds, 464 milliseconds VERBOSE: Getting forest information - Stop - Time to generate: 0 days, 0 hours, 0 minutes, 0 seconds, 60 milliseconds VERBOSE: Getting all information - Stop - Time to generate: 0 days, 0 hours, 1 minutes, 19 seconds, 552 milliseconds
Just 1 minute and 19 seconds. Wow! Isn't it amazing? Associative arrays, or in case of PowerShell HashTables are an abstract data type that can hold data in (key, value) pairs. The easiest thing to understand this concept is to think of it as a phone book. Key is phone number, Value is Name of the phone owner. In this phone book, you can look up a person's name by finding their phone number. Every key can only appear once, just like every phone number can only appear once on the phone book. And, every key can only have one value, just like every phone number can only refer to only one person. In comparison array's can have multiple objects with exactly the same value. That's why in case of array's you need to use foreach to find a value, while in case of HashTable you simply tell PowerShell to get you Value based on the Key. As each key is unique PowerShell knows how to find it and can deliver it very fast. Let's see how it affects the whole PSWinDocumentation.AD module, shall we?
VERBOSE: Getting all information - Start VERBOSE: Getting forest information - Start VERBOSE: Getting forest information - TypesRequired is null. Getting all. VERBOSE: Getting forest information - ForestRootDSE VERBOSE: Getting forest information - ForestRootDSE - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 48 milliseconds VERBOSE: Getting forest information - Forest VERBOSE: Getting forest information - Forest - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 51 milliseconds VERBOSE: Getting forest information - ForestSchemaPropertiesComputers VERBOSE: Getting forest information - ForestSchemaPropertiesComputers - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 607 milliseconds VERBOSE: Getting forest information - ForestSchemaPropertiesUsers VERBOSE: Getting forest information - ForestSchemaPropertiesUsers - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 259 milliseconds VERBOSE: Getting forest information - ForestUPNSuffixes VERBOSE: Getting forest information - ForestUPNSuffixes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 17 milliseconds VERBOSE: Getting forest information - ForestSPNSuffixes VERBOSE: Getting forest information - ForestSPNSuffixes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 9 milliseconds VERBOSE: Getting forest information - ForestGlobalCatalogs VERBOSE: Getting forest information - ForestGlobalCatalogs - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting forest information - ForestFSMO VERBOSE: Getting forest information - ForestFSMO - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 10 milliseconds VERBOSE: Getting forest information - ForestDomainControllers VERBOSE: Getting forest information - ForestDomainControllers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 192 milliseconds VERBOSE: Getting forest information - ForestSites VERBOSE: Getting forest information - ForestSites - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 39 milliseconds VERBOSE: Getting forest information - ForestSites1 VERBOSE: Getting forest information - ForestSites1 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 22 milliseconds VERBOSE: Getting forest information - ForestSites2 VERBOSE: Getting forest information - ForestSites2 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 13 milliseconds VERBOSE: Getting forest information - ForestSubnets VERBOSE: Getting forest information - ForestSubnets - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 15 milliseconds VERBOSE: Getting forest information - ForestSubnets1 VERBOSE: Getting forest information - ForestSubnets1 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 9 milliseconds VERBOSE: Getting forest information - ForestSubnets2 VERBOSE: Getting forest information - ForestSubnets2 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 17 milliseconds VERBOSE: Getting forest information - ForestSiteLinks VERBOSE: Getting forest information - ForestSiteLinks - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 17 milliseconds VERBOSE: Getting forest information - ForestOptionalFeatures VERBOSE: Getting forest information - ForestOptionalFeatures - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 22 milliseconds VERBOSE: Getting forest information - Domains VERBOSE: Getting domain information - TestDomain.pl DomainRootDSE VERBOSE: Getting domain information - TestDomain.pl DomainRootDSE - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 16 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainInformation VERBOSE: Getting domain information - TestDomain.pl DomainInformation - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 124 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsFullList VERBOSE: Getting domain information - TestDomain.pl DomainGroupsFullList - Time: 0 days, 0 hours, 0 minutes, 2 seconds, 420 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersFullList VERBOSE: Getting domain information - TestDomain.pl DomainUsersFullList - Time: 0 days, 0 hours, 0 minutes, 14 seconds, 906 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersFullList VERBOSE: Getting domain information - TestDomain.pl DomainComputersFullList - Time: 0 days, 0 hours, 0 minutes, 7 seconds, 325 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersAll VERBOSE: Getting domain information - TestDomain.pl DomainComputersAll - Time: 0 days, 0 hours, 0 minutes, 31 seconds, 637 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersAllCount VERBOSE: Getting domain information - TestDomain.pl DomainComputersAllCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 78 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainServers VERBOSE: Getting domain information - TestDomain.pl DomainServers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 29 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainServersCount VERBOSE: Getting domain information - TestDomain.pl DomainServersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 60 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputers VERBOSE: Getting domain information - TestDomain.pl DomainComputers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 43 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersCount VERBOSE: Getting domain information - TestDomain.pl DomainComputersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 31 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknown VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknown - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 28 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknownCount VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknownCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 24 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainRIDs VERBOSE: Getting domain information - TestDomain.pl DomainRIDs - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 133 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGUIDS VERBOSE: Getting domain information - TestDomain.pl DomainGUIDS - Time: 0 days, 0 hours, 0 minutes, 3 seconds, 510 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicies VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 31 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicySilos VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicySilos - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 30 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessPolicies VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 24 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessRules VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessRules - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 44 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainClaimTransformPolicies VERBOSE: Getting domain information - TestDomain.pl DomainClaimTransformPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 43 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainClaimTypes VERBOSE: Getting domain information - TestDomain.pl DomainClaimTypes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 50 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDNSData VERBOSE: Getting domain information - TestDomain.pl DomainDNSData - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 470 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDNSSrv VERBOSE: Getting domain information - TestDomain.pl DomainDNSSrv - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDNSA VERBOSE: Getting domain information - TestDomain.pl DomainDNSA - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFSMO VERBOSE: Getting domain information - TestDomain.pl DomainFSMO - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 27 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainTrustsClean VERBOSE: Getting domain information - TestDomain.pl DomainTrustsClean - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 56 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainTrusts VERBOSE: Getting domain information - TestDomain.pl DomainTrusts - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 389 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesClean VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesClean - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 365 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPolicies VERBOSE: Getting domain information - TestDomain.pl DomainGroupPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 744 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesDetails VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesDetails - Time: 0 days, 0 hours, 0 minutes, 19 seconds, 182 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesACL VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesACL - Time: 0 days, 0 hours, 0 minutes, 11 seconds, 451 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainBitlocker VERBOSE: Getting domain information - TestDomain.pl DomainBitlocker - Time: 0 days, 0 hours, 0 minutes, 9 seconds, 677 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainLAPS VERBOSE: Getting domain information - TestDomain.pl DomainLAPS - Time: 0 days, 0 hours, 0 minutes, 2 seconds, 449 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDefaultPasswordPolicy VERBOSE: Getting domain information - TestDomain.pl DomainDefaultPasswordPolicy - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 46 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsClean VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsClean - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 140 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnits VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnits - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 100 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsBasicACL VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsBasicACL - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 11 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsExtendedACL VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsExtendedACL - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 323 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsers VERBOSE: Getting domain information - TestDomain.pl DomainUsers - Time: 0 days, 0 hours, 15 minutes, 55 seconds, 605 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersAll VERBOSE: Getting domain information - TestDomain.pl DomainUsersAll - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 123 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersSystemAccounts VERBOSE: Getting domain information - TestDomain.pl DomainUsersSystemAccounts - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 143 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiring VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiring - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 139 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiringInclDisabled VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiringInclDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 137 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredInclDisabled VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredInclDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 148 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredExclDisabled VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredExclDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 159 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersCount VERBOSE: Getting domain information - TestDomain.pl DomainUsersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 39 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainControllers VERBOSE: Getting domain information - TestDomain.pl DomainControllers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 71 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPolicies VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 32 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsers VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 36 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsersExtended VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsersExtended - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 32 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroups VERBOSE: Getting domain information - TestDomain.pl DomainGroups - Time: 0 days, 0 hours, 9 minutes, 23 seconds, 409 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembers VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembers - Time: 0 days, 0 hours, 0 minutes, 12 seconds, 399 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembersRecursive VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembersRecursive - Time: 0 days, 0 hours, 31 minutes, 9 seconds, 707 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviliged VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviliged - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 23 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecial VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecial - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 19 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembers VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 39 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembersRecursive VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembersRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 413 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembers VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 30 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembersRecursive VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembersRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 703 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAdministrators VERBOSE: Getting domain information - TestDomain.pl DomainAdministrators - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 26 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAdministratorsRecursive VERBOSE: Getting domain information - TestDomain.pl DomainAdministratorsRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 705 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministrators VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministrators - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 23 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministratorsRecursive VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministratorsRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 705 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataUsers VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataUsers - Time: 0 days, 0 hours, 0 minutes, 8 seconds, 531 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswords VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswords - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 30 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswordsHashes VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswordsHashes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 9 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordClearTextPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordClearTextPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 17 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordLMHash VERBOSE: Getting domain information - TestDomain.pl DomainPasswordLMHash - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 1 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 30 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordEnabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordEnabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordDisabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordList VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordList - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDefaultComputerPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDefaultComputerPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 29 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNotRequired VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNotRequired - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNeverExpires VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNeverExpires - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordAESKeysMissing VERBOSE: Getting domain information - TestDomain.pl DomainPasswordAESKeysMissing - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPreAuthNotRequired VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPreAuthNotRequired - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDESEncryptionOnly VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDESEncryptionOnly - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDelegatableAdmins VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDelegatableAdmins - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDuplicatePasswordGroups VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDuplicatePasswordGroups - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 10 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordEnabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordEnabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordDisabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordStats VERBOSE: Getting domain information - TestDomain.pl DomainPasswordStats - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 51 milliseconds VERBOSE: Getting domain information - TestDomain.pl - Time to generate: 0 days, 0 hours, 58 minutes, 42 seconds, 428 milliseconds VERBOSE: Getting forest information - Domains - Time: 0 days, 0 hours, 58 minutes, 42 seconds, 503 milliseconds VERBOSE: Getting forest information - Stop - Time to generate: 0 days, 0 hours, 0 minutes, 4 seconds, 904 milliseconds VERBOSE: Getting all information - Stop - Time to generate: 0 days, 0 hours, 58 minutes, 47 seconds, 435 milliseconds
And this is how it looks with the new approach
VERBOSE: Loading module from path 'C:\Program Files\WindowsPowerShell\Modules\PSWinDocumentation.AD\0.0.8\PSWinDocumentation.AD.psd1'. VERBOSE: Populating RepositorySourceLocation property for module PSWinDocumentation.AD. VERBOSE: Loading module from path 'C:\Program Files\WindowsPowerShell\Modules\PSWinDocumentation.AD\0.0.8\PSWinDocumentation.AD.psm1'. VERBOSE: Importing function 'Get-WinADDomainInformation'. VERBOSE: Importing function 'Get-WinADForestInformation'. VERBOSE: Getting all information - Start VERBOSE: Getting forest information - Start VERBOSE: Getting forest information - TypesRequired is null. Getting all. VERBOSE: Getting forest information - ForestRootDSE VERBOSE: Getting forest information - ForestRootDSE - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 21 milliseconds VERBOSE: Getting forest information - Forest VERBOSE: Getting forest information - Forest - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 13 milliseconds VERBOSE: Getting forest information - ForestSchemaPropertiesComputers VERBOSE: Getting forest information - ForestSchemaPropertiesComputers - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 369 milliseconds VERBOSE: Getting forest information - ForestSchemaPropertiesUsers VERBOSE: Getting forest information - ForestSchemaPropertiesUsers - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 242 milliseconds VERBOSE: Getting forest information - ForestUPNSuffixes VERBOSE: Getting forest information - ForestUPNSuffixes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting forest information - ForestSPNSuffixes VERBOSE: Getting forest information - ForestSPNSuffixes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting forest information - ForestGlobalCatalogs VERBOSE: Getting forest information - ForestGlobalCatalogs - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting forest information - ForestFSMO VERBOSE: Getting forest information - ForestFSMO - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting forest information - ForestDomainControllers VERBOSE: Getting forest information - ForestDomainControllers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 106 milliseconds VERBOSE: Getting forest information - ForestSites VERBOSE: Getting forest information - ForestSites - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 49 milliseconds VERBOSE: Getting forest information - ForestSites1 VERBOSE: Getting forest information - ForestSites1 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 11 milliseconds VERBOSE: Getting forest information - ForestSites2 VERBOSE: Getting forest information - ForestSites2 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting forest information - ForestSubnets VERBOSE: Getting forest information - ForestSubnets - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 12 milliseconds VERBOSE: Getting forest information - ForestSubnets1 VERBOSE: Getting forest information - ForestSubnets1 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting forest information - ForestSubnets2 VERBOSE: Getting forest information - ForestSubnets2 - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 11 milliseconds VERBOSE: Getting forest information - ForestSiteLinks VERBOSE: Getting forest information - ForestSiteLinks - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 14 milliseconds VERBOSE: Getting forest information - ForestOptionalFeatures VERBOSE: Getting forest information - ForestOptionalFeatures - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 14 milliseconds VERBOSE: Getting forest information - Domains VERBOSE: Getting domain information - TestDomain.pl DomainRootDSE VERBOSE: Getting domain information - TestDomain.pl DomainRootDSE - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 17 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainInformation VERBOSE: Getting domain information - TestDomain.pl DomainInformation - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 63 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsFullList VERBOSE: Getting domain information - TestDomain.pl DomainGroupsFullList - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 964 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersFullList VERBOSE: Getting domain information - TestDomain.pl DomainUsersFullList - Time: 0 days, 0 hours, 0 minutes, 20 seconds, 622 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersFullList VERBOSE: Getting domain information - TestDomain.pl DomainComputersFullList - Time: 0 days, 0 hours, 0 minutes, 21 seconds, 758 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersAll VERBOSE: Getting domain information - TestDomain.pl DomainComputersAll - Time: 0 days, 0 hours, 0 minutes, 39 seconds, 450 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersAllCount VERBOSE: Getting domain information - TestDomain.pl DomainComputersAllCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 73 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainServers VERBOSE: Getting domain information - TestDomain.pl DomainServers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 21 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainServersCount VERBOSE: Getting domain information - TestDomain.pl DomainServersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 68 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputers VERBOSE: Getting domain information - TestDomain.pl DomainComputers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 44 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersCount VERBOSE: Getting domain information - TestDomain.pl DomainComputersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 39 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknown VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknown - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 20 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknownCount VERBOSE: Getting domain information - TestDomain.pl DomainComputersUnknownCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 11 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainRIDs VERBOSE: Getting domain information - TestDomain.pl DomainRIDs - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 38 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGUIDS VERBOSE: Getting domain information - TestDomain.pl DomainGUIDS - Time: 0 days, 0 hours, 0 minutes, 3 seconds, 579 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicies VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 27 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicySilos VERBOSE: Getting domain information - TestDomain.pl DomainAuthenticationPolicySilos - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 29 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessPolicies VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 22 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessRules VERBOSE: Getting domain information - TestDomain.pl DomainCentralAccessRules - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 34 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainClaimTransformPolicies VERBOSE: Getting domain information - TestDomain.pl DomainClaimTransformPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 25 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainClaimTypes VERBOSE: Getting domain information - TestDomain.pl DomainClaimTypes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 43 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDNSData VERBOSE: Getting domain information - TestDomain.pl DomainDNSData - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 15 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDNSSrv VERBOSE: Getting domain information - TestDomain.pl DomainDNSSrv - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDNSA VERBOSE: Getting domain information - TestDomain.pl DomainDNSA - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFSMO VERBOSE: Getting domain information - TestDomain.pl DomainFSMO - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainTrustsClean VERBOSE: Getting domain information - TestDomain.pl DomainTrustsClean - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 36 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainTrusts VERBOSE: Getting domain information - TestDomain.pl DomainTrusts - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 46 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesClean VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesClean - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 48 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPolicies VERBOSE: Getting domain information - TestDomain.pl DomainGroupPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 479 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesDetails VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesDetails - Time: 0 days, 0 hours, 0 minutes, 16 seconds, 881 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesACL VERBOSE: Getting domain information - TestDomain.pl DomainGroupPoliciesACL - Time: 0 days, 0 hours, 0 minutes, 11 seconds, 496 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainBitlocker VERBOSE: Getting domain information - TestDomain.pl DomainBitlocker - Time: 0 days, 0 hours, 0 minutes, 7 seconds, 889 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainLAPS VERBOSE: Getting domain information - TestDomain.pl DomainLAPS - Time: 0 days, 0 hours, 0 minutes, 2 seconds, 434 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainDefaultPasswordPolicy VERBOSE: Getting domain information - TestDomain.pl DomainDefaultPasswordPolicy - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 35 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsClean VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsClean - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 126 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnits VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnits - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 59 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsBasicACL VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsBasicACL - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 950 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsExtendedACL VERBOSE: Getting domain information - TestDomain.pl DomainOrganizationalUnitsExtendedACL - Time: 0 days, 0 hours, 0 minutes, 1 seconds, 290 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsers VERBOSE: Getting domain information - TestDomain.pl DomainUsers - Time: 0 days, 0 hours, 0 minutes, 44 seconds, 843 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersAll VERBOSE: Getting domain information - TestDomain.pl DomainUsersAll - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 25 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersSystemAccounts VERBOSE: Getting domain information - TestDomain.pl DomainUsersSystemAccounts - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 13 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiring VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiring - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 25 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiringInclDisabled VERBOSE: Getting domain information - TestDomain.pl DomainUsersNeverExpiringInclDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 21 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredInclDisabled VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredInclDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 22 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredExclDisabled VERBOSE: Getting domain information - TestDomain.pl DomainUsersExpiredExclDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 41 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainUsersCount VERBOSE: Getting domain information - TestDomain.pl DomainUsersCount - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 12 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainControllers VERBOSE: Getting domain information - TestDomain.pl DomainControllers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 54 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPolicies VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPolicies - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 29 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsers VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 12 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsersExtended VERBOSE: Getting domain information - TestDomain.pl DomainFineGrainedPoliciesUsersExtended - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 10 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroups VERBOSE: Getting domain information - TestDomain.pl DomainGroups - Time: 0 days, 0 hours, 0 minutes, 23 seconds, 920 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembers VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembers - Time: 0 days, 0 hours, 0 minutes, 39 seconds, 399 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembersRecursive VERBOSE: Getting domain information - TestDomain.pl DomainGroupsMembersRecursive - Time: 0 days, 0 hours, 1 minutes, 54 seconds, 350 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviliged VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviliged - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 12 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecial VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecial - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 16 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembers VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 298 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembersRecursive VERBOSE: Getting domain information - TestDomain.pl DomainGroupsSpecialMembersRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 417 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembers VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembers - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 493 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembersRecursive VERBOSE: Getting domain information - TestDomain.pl DomainGroupsPriviligedMembersRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 751 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAdministrators VERBOSE: Getting domain information - TestDomain.pl DomainAdministrators - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 482 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainAdministratorsRecursive VERBOSE: Getting domain information - TestDomain.pl DomainAdministratorsRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 743 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministrators VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministrators - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 471 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministratorsRecursive VERBOSE: Getting domain information - TestDomain.pl DomainEnterpriseAdministratorsRecursive - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 753 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataUsers VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataUsers - Time: 0 days, 0 hours, 0 minutes, 8 seconds, 110 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswords VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswords - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 9 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswordsHashes VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDataPasswordsHashes - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordClearTextPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordClearTextPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordLMHash VERBOSE: Getting domain information - TestDomain.pl DomainPasswordLMHash - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordEmptyPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 1 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordEnabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordEnabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordDisabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordList VERBOSE: Getting domain information - TestDomain.pl DomainPasswordWeakPasswordList - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDefaultComputerPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDefaultComputerPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNotRequired VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNotRequired - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 9 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNeverExpires VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPasswordNeverExpires - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordAESKeysMissing VERBOSE: Getting domain information - TestDomain.pl DomainPasswordAESKeysMissing - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPreAuthNotRequired VERBOSE: Getting domain information - TestDomain.pl DomainPasswordPreAuthNotRequired - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDESEncryptionOnly VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDESEncryptionOnly - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDelegatableAdmins VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDelegatableAdmins - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 8 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDuplicatePasswordGroups VERBOSE: Getting domain information - TestDomain.pl DomainPasswordDuplicatePasswordGroups - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 7 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPassword VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPassword - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 6 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordEnabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordEnabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordDisabled VERBOSE: Getting domain information - TestDomain.pl DomainPasswordHashesWeakPasswordDisabled - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 5 milliseconds VERBOSE: Getting domain information - TestDomain.pl DomainPasswordStats VERBOSE: Getting domain information - TestDomain.pl DomainPasswordStats - Time: 0 days, 0 hours, 0 minutes, 0 seconds, 23 milliseconds VERBOSE: Getting domain information - TestDomain.pl - Time to generate: 0 days, 0 hours, 6 minutes, 4 seconds, 935 milliseconds VERBOSE: Getting forest information - Domains - Time: 0 days, 0 hours, 6 minutes, 4 seconds, 973 milliseconds VERBOSE: Getting forest information - Stop - Time to generate: 0 days, 0 hours, 0 minutes, 3 seconds, 43 milliseconds VERBOSE: Getting all information - Stop - Time to generate: 0 days, 0 hours, 6 minutes, 8 seconds, 62 milliseconds
58 minutes vs 6 minutes and 8 seconds. And in case of my other domain 1 hour and 42 minutes down to 18 minutes and 12 seconds. So instead of going to lunch, I can go for longer coffee and have that done. I call that a win. Now keep in mind that there are still things I need to optimize within PSWinDocumentation.AD but armed with hashtables it may have a nice performance boost I was looking for. Keep in mind that Version 0.0.8 is not yet released to production. There may still be bugs that I've introduced while testing things.
Let's end this article with what I've started. Two links – Kevin Marquette, and his Everything you wanted to know about Hashtables and my very own PowerShell – Few tricks about HashTables and Arrays I wish I knew when I started. Especially in my article I mention a couple of other bad practices you should avoid if you care for speed. What have we learned today? Stop using Where-Object, prefer foreach loop where speed matters. ForEach-Object is as bad in speed as Where-Object (while I didn't provide a speed test, trust me – it's slow). Use a ForEach loop instead. If you see a way to use Hashtables, use them! They are much faster than using Arrays (where it makes sense). And to make it evident, I don't want you to suddenly stop using Where-Object or ForEach-Object when your code executes in 10 seconds or less. I'm talking about optimization when it's taking 15 minutes, 1 hour or 5 hours to run and you want that to finish much sooner. Both ForEach-Object and Where-Object have their use cases, and I use them frequently. Now let me go and start fixing my long running scripts, they deserve some speed boost! In case you would like to try it out yourself, here's a test case:
Measure-Command {$a = @{}; 1..10000 | ForEach-Object {$a.$_ = $_}} Measure-Command {$b = @{}; 1..10000 | ForEach-Object {$b.add($_, $_)}} Measure-Command {$c = @{}; 1..10000 | ForEach-Object {$c[$_] = $_}} Measure-Command { $b = @{} for ($i = 1; $i -le 10000; $i++) { $b.add($i, $i) } }
And the results are
Days : 0 Hours : 0 Minutes : 0 Seconds : 12 Milliseconds : 764 Ticks : 127641020 TotalDays : 0,000147732662037037 TotalHours : 0,00354558388888889 TotalMinutes : 0,212735033333333 TotalSeconds : 12,764102 TotalMilliseconds : 12764,102 Days : 0 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 139 Ticks : 1398151 TotalDays : 1,61823032407407E-06 TotalHours : 3,88375277777778E-05 TotalMinutes : 0,00233025166666667 TotalSeconds : 0,1398151 TotalMilliseconds : 139,8151 Days : 0 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 138 Ticks : 1382375 TotalDays : 1,59997106481481E-06 TotalHours : 3,83993055555556E-05 TotalMinutes : 0,00230395833333333 TotalSeconds : 0,1382375 TotalMilliseconds : 138,2375 Days : 0 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 56 Ticks : 563933 TotalDays : 6,52700231481481E-07 TotalHours : 1,56648055555556E-05 TotalMinutes : 0,000939888333333333 TotalSeconds : 0,0563933 TotalMilliseconds : 56,3933
As you see, using add method is the fastest, and with for/foreach even faster than using ForEach-Object (which has % as an alias). If you're into speed you may want to reconsider your usage scenarios.