DSInternals
DSInternals copied to clipboard
Test-PasswordQuality - Groups are not returned as object
I'm trying to use Test-PasswordQuality but it seems while output is something like this:
These groups of accounts have the same passwords:
Group 1:
adm.pklys
przemyslaw.klys
Group 2:
expiration
new
When you try to access it like this
$Results = Get-ADReplAccount -All -Server $Server -NamingContext $DomainDN | Test-PasswordQuality -WeakPasswordsFile $DictionaryDatabase1 -IncludeDisabledAccounts
$Results.DuplicatePasswordGroups
You only get strings of names. I would expect to access groups by using
$Results.DuplicatePasswordGroups.'Group 1'
or similar. Is there a way to achieve this?
Good point! I will probably change it to IList. For now, you can either use foreach or $Results.DuplicatePasswordGroups.ToArray()[0].
Thanks. That works!
Forgive me here for being a noob. Would I be able to produce information about the users in these groups? For example samaccountname, lastlogondate, etc? For all the other password objects, I've done it like this:
$riskyAccounts = $accounts | Where-Object LogonName -in $results.EmptyPassword $riskyAccounts | Select-Object -Property SamAccountName,DistinguishedName,userPrincipalName,LastLogonTimeStamp,createTimeStamp |Export-Csv "G:\dits\output\Empty-pwd-$name-output.csv" -NoTypeInformation
Thanks, Joe
Hi,
@joejsullivan - if you want I wrote it already.
I've created PowerShell module called PasswordSolution (https://github.com/EvotecIT/PasswordSolution). While main part controls the password expiration I also added "reporting" on top of what Test-PasswordQuality offers using DSInternals if you have them installed
As an object:
$Users = Find-PasswordQuality
$Users | Format-Table
As html report:
Show-PasswordQuality -FilePath $PSScriptRoot\Reporting\PasswordQuality.html -Online -WeakPasswords "Test1", "Test2", "Test3" -Verbose

I actually forgot last logon date, but it should be trivial to add.
I missed this functionality in Michael's module, so I made it all pretty.
Hope it helps.
Przemek
Thanks, @PrzemyslawKlys . Unfortunately, all of my work must remain offline. I've been given separate hardware to perform. From what it looks like, all the discovery is done online.
But wow.... the output and reporting are phenomenal.
Thanks again, Joe
Depends on your definition of offline. It works "online" as in requires Domain Controller that is up and running and then it uses:
Get-ADReplAccount -All -Server $Server
and then Test-PasswordQuality against that.
If by Online you mean switch you saw in the command is for "HTML" - it's for using CDN JS and CSS which means lower size of generated HTML. But if you skip the Online switch it embedded all required CSS and JS and works well offline. So it can be run on machine without internet that just have DC visibility. It just makes it 3mb more in the final HTML output.
@PrzemyslawKlys , closer. I don't even have DC visibility. The DIT's have all been dumped and placed on separate, isolated hardware. I'm executing these as:
$accounts = Get-ADDBAccount -DatabasePath $path -BootKey $key -All | Where-Object samaccounttype -like User $results = $accounts |Test-PasswordQuality
I see. Adding "offline" mode would be possible, since it's just additional if/else logic. A bit harder would be to get users from AD first, so one would have to allow something like:
- Get ADForest, Get-ADUser, Save them to XML with the required data, and pass them all to a single command that merges that data. Not really big deal, and that could be used in "offline" mode after that.
Thanks. I think getting the users as a Get-ADDBAccount should get all the details necessary? No?
Joe
I've never used it, so dunno what sort of data it shows up. For me it's easier to get them with Get-ADUser, and merge with Test-PasswordQuality. Maybe if I will have some free time I will play with it and improve my module to work offline like this. But so far haven't had a need.
Thanks @PrzemyslawKlys. I just took a look at the output of $results.DuplicatePasswordGroups and the output is totally different than any other result. It's been a vey long day for me, I'm somewhat tired.
Thanks for the input today. The first suggestion I had made to complete this was doing it on a new VM that had network access. Was given all the dit's on a laptop instead.
Joe
This gets me through it.
I really appreciate you putting up with me today while I worked through it.
$results.DuplicatePasswordGroups | ForEach-Object { $_.Replace("Remove_the_leading_domain_name\", "") } | ForEach-Object {
$newJunk = $_
$accounts | Where-Object { $_.samaccountname -eq $newJunk } | Select-Object -Property SamAccountName,DistinguishedName,LastLogonTimeStamp | Export-Csv "G:\dits\output\dupe-pwdgrps-$name-output.csv" -NoTypeInformation -Append
}
Btw. don't use where-object. Switch to standard foreach. It will be 10x faster. Maybe if you have small domain it doesn't matter, but for large ones you will get heavy penalty.
Ha. Thanks for the heads up. Yes - I may take quite a penalty for some of the domains. Some of the good part is, I can let it run overnight. The bad part, I have to let it run overnight and somewhat "monitor" the progress.
Perhaps something like this:
# Get the domain name from the first line of $results.DuplicatePasswordGroups
$domain = ($results.DuplicatePasswordGroups[0] -split '\\')[0]
# Loop through each username in $results.DuplicatePasswordGroups
foreach ($username in $results.DuplicatePasswordGroups) {
# Remove the domain name from the username
$newJunk = $username.Replace("$domain\", "")
# Loop through each account in $accounts and find the matching object
foreach ($account in $accounts) {
if ($account.samaccountname -eq $newJunk) {
# Select the desired properties and export to CSV
Select-Object -InputObject $account -Property SamAccountName,DistinguishedName,LastLogonTimeStamp |
Export-Csv "G:\dits\output\dupe-pwds-$name-output.csv" -NoTypeInformation -Append
break # exit the inner foreach loop once a match is found
}
}
}
If you want it to be really fast you should cache it using hashtable. The inner loop will hit you hard, as for every user you would possible have to go thru a full number of users. WIth hashtable that's just super fast
$domain = ($results.DuplicatePasswordGroups[0] -split '\\')[0]
$CacheAccounts = [ordered] @{}
foreach ($account in $accounts) {
$CacheAccounts[$account.samaccountname] = $account
}
# Loop through each username in $results.DuplicatePasswordGroups
foreach ($username in $results.DuplicatePasswordGroups) {
# Remove the domain name from the username
$newJunk = $username.Replace("$domain\", "")
# Loop through each account in $accounts and find the matching object
if ($CacheAccounts[$newJunk]) {
# Select the desired properties and export to CSV
Select-Object -InputObject $CacheAccounts[$newJunk] -Property SamAccountName, DistinguishedName, LastLogonTimeStamp |
Export-Csv "G:\dits\output\dupe-pwds-$name-output.csv" -NoTypeInformation -Append
}
}