New Question
 
 
PRTG Network Monitor

Intuitive to Use.
Easy to manage.

200.000 administrators have chosen PRTG to monitor their network. Find out how you can reduce cost, increase QoS and ease planning, as well.

Free PRTG
Download >>

 

What is this?

This knowledgebase contains questions and answers about PRTG Network Monitor and network monitoring in general. You are invited to get involved by asking and answering questions!

Learn more

 

Top Tags


View all Tags


Powershell Custom Sensor for monitoring AD User lockouts

Votes:

1

Your Vote:

Up

Down

There is a desire to monitor user lockouts in my organization. This thread offers a script that comes close, but isn't quite what we needed, as it monitors all disabled users.

I've taken that script and adjusted it to only look at non-disabled, non-expired, locked out users. I've decided to post it here in its own thread, because the use case is sufficiently different from the original script. The script is generic and should work in any AD environment. Do keep in mind, though, that the server running the sensor needs to have the Active Directory Powershell module installed.

Import-Module ActiveDirectory

$server=Get-ADUser -Filter * -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {$_.lockedout -eq "True" -and $_.enabled -eq "True" -and (($_.AccountExpirationDate -gt (Get-Date) -or ($_.AccountExpirationDate -eq $null)))}

if ($server.count -eq $null -and $server -eq $null){
    $a=0
}
Elseif ($server.count -eq $null -and $server -ne $null){
    $a=1
}
Else
{
    $a=@($server.count)
}
Write-Host "<prtg>"
Write-Host "<result>" 
"<channel>Locked Out Users</channel>" 
"<value>"+ $a +"</value>" 
"</result>"
"<text>" + (($server | select SamAccountName,DisplayName,@{n='LockoutTime';e={[DateTime]::FromFileTime($_.lockouttime)}} | sort LockoutTime -descending | ConvertTo-Csv -NoTypeInformation | select -skip 1 ) -join ", ").replace("""","") + "</text>"
Write-Host "</prtg>"

If anyone spots any issues with the script, or has suggestions for optimization, I'm happy to hear them.

active-directory powershell user-accounts

Created on Sep 20, 2017 10:50:02 AM by  Bdmm (1) 1



Best Answer

Accepted Answer

Votes:

0

Your Vote:

Up

Down

Tested this - and yes you are right.. the correct line would be:

-LT (less than) and a -AND instead of -OR

resulting in this command:

Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {(($_.AccountExpirationDate -lt (Get-Date) -and ($_.AccountExpirationDate -ne $null)))} 

and here the corrected full script:

Import-Module ActiveDirectory

#you could add - filters, a OU limitation or a server against whom this would be executed.. see Get-ADUser options for more details

#all locked users that aren't disabled or expired
$LockedOutUsers=Get-ADUser -Filter {Enabled -eq $True -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {$_.lockedout -eq $True -and (($_.AccountExpirationDate -gt (Get-Date) -or ($_.AccountExpirationDate -eq $null)))} 

#all users that are disabled - this is a manual action in Active Directory
$DisabledUsers=Get-ADUser -Filter {Enabled -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate

#all users that are not disabled but expired already
$ExpiredUsers=Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {(($_.AccountExpirationDate -lt (Get-Date) -and ($_.AccountExpirationDate -ne $null)))} 

#users with not expiring passwords and are enabled and not expired
$NotExpiringPWD=Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate 

If ($LockedOutUsers.count -eq $null -and $LockedOutUsers -eq $null){
    $cntLockedOutUsers=0
}Elseif ($LockedOutUsers.count -eq $null -and $LockedOutUsers -ne $null){
    $cntLockedOutUsers=1
}Else{
    $cntLockedOutUsers=@($LockedOutUsers.count)
}

If ($DisabledUsers.count -eq $null -and $DisabledUsers -eq $null){
    $cntDisabledUsers=0
}Elseif ($DisabledUsers.count -eq $null -and $DisabledUsers -ne $null){
    $cntDisabledUsers=1
}Else{
    $cntDisabledUsers=@($DisabledUsers.count)
}

If ($ExpiredUsers.count -eq $null -and $ExpiredUsers -eq $null){
    $cntExpiredUsers=0
}Elseif ($ExpiredUsers.count -eq $null -and $ExpiredUsers -ne $null){
    $cntExpiredUsers=1
}Else{
    $cntExpiredUsers=@($ExpiredUsers.count)
}

If ($NotExpiringPWD.count -eq $null -and $NotExpiringPWD -eq $null){
    $cntNotExpiringPWD=0
}Elseif ($NotExpiringPWD.count -eq $null -and $NotExpiringPWD -ne $null){
    $cntNotExpiringPWD=1
}Else{
    $cntNotExpiringPWD=@($NotExpiringPWD.count)
}

$XML += "<prtg>"
$XML += "<result>" 
$XML += "<channel>Locked Out Users</channel>" 
$XML += "<value>$cntLockedOutUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Disabled Users</channel>" 
$XML += "<value>$cntDisabledUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Expired Users - not disabled</channel>" 
$XML += "<value>$cntExpiredUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Users with password never expires</channel>" 
$XML += "<value>$cntNotExpiringPWD</value>" 
$XML += "</result>"
$XML += "</prtg>"

Function WriteXmlToScreen ([xml]$xml)
{
    $StringWriter = New-Object System.IO.StringWriter;
    $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter;
    $XmlWriter.Formatting = "indented";
    $xml.WriteTo($XmlWriter);
    $XmlWriter.Flush();
    $StringWriter.Flush();
    Write-Output $StringWriter.ToString();
}

WriteXmlToScreen $XML

Thanks for pointing this out - did not realize this mistake...

Regards

Florian Rossmark

www.it-admins.com

Created on Nov 2, 2018 1:48:47 PM by  Florian Rossmark (2,760) 2 2



11 Replies

Votes:

0

Your Vote:

Up

Down

Hello Bdmm,

Thank you very much for your valuable input, I'm pretty sure that there are users out there who will benefit from your script.

Best regards and thanks again.
Sebastian

Created on Sep 21, 2017 10:33:09 AM by  Sebastian Kniege [Paessler Support]



Votes:

0

Your Vote:

Up

Down

Is it possible to monitor user account lockout status where the sensor is running on a different domain ?

Created on Mar 12, 2018 9:06:12 PM by  gbuelna (0)



Votes:

0

Your Vote:

Up

Down

Tested, works smooth as I have one user locked out and I can see the details in de settings. The counters says 0, so it needs a minor tweak to show 1 ? If users locked out >1, does this sensor show the number of locks, but without the details ??

Created on Mar 16, 2018 4:13:46 PM by  CoDesk (100) 1 1



Votes:

0

Your Vote:

Up

Down

Is it possible to monitor user account lockout status where the sensor is running on a different domain ?

possible yes, but does anybody have a solution to get the Data without the use of a Domain Admin ? Wen don't wan't to use any Domain Admins - instead we use local admins (when necessary and possible)

Created on Apr 17, 2018 2:37:19 PM by  Philipp (222) 1 1



Votes:

0

Your Vote:

Up

Down

This should actually work with a regular user account - any domain user can gather information like this.

What you might need is the RSAT tools installed on the probe-device, besides that I do not see a reason why you shouldn't be able to execute this as a regular user.

Actually, you would be quite surprised seeing what a regular user account can read from your Active Directory. Please have a look at my free IT-Admins Tool at https://www.it-admins.com - you might be quite astonished.

Having this said - I would re-write the script and do it differently and a bit more extended.

The following script will read through your current Active Directory and filter for user accounts with the following specific conditions:

  • Lockedout users - please read below
    • all users that are lockedout
    • must be an enabled user
    • that is not expired
  • disabled users
    • all users that have been disabled
  • expired users
    • must be an enabled user
    • the expiration date is set and past the current date
  • users with password never expires set
    • must be an enabled user

This will give you a pure counter output per channel in an for PRTG Extended script sensor XML result.

But there is a theoretical flaw in one of the methods - the locked out users. Now, user accounts get locked out in Active Directory due to too many logon attempts with an invalid password. This causes Active Directory to set the lockedout bit in the object properties. The issue here is that this bit will not be set back to 0 after the defined lockout duration (GPO) is past, the property will only be set back to 0 once the lockout duration is passed and he successfully logged on.

This means, the counter might give you more results then currently true, it might count users that have been locked out but the lockout-duration passed - but they did not yet logon successfully. This is somehow a false positive, while not totally false. In any case, you need to be aware of this.

The script could be more efficient as well in the way it filters a few things, so far I optimized it as far as I could - the LockedOut value can not be set as a -Filter, in theory it might be possible to speed it up with a -Filter to the UserAccountControl - but I am not certain this would work. If you really want to speed it up you would need to work with -LDAPFilter - but this actually needs to completely replace the internal filter capabilities of Get-ADUser - you can't use both - it is one or the other.

Import-Module ActiveDirectory

#you could add - filters, a OU limitation or a server against whom this would be executed.. see Get-ADUser options for more details

#all locked users that aren't disabled or expired
$LockedOutUsers=Get-ADUser -Filter {Enabled -eq $True -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {$_.lockedout -eq $True -and (($_.AccountExpirationDate -gt (Get-Date) -or ($_.AccountExpirationDate -eq $null)))} 

#all users that are disabled - this is a manual action in Active Directory
$DisabledUsers=Get-ADUser -Filter {Enabled -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate

#all users that are not disabled but expired already
$ExpiredUsers=Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {(($_.AccountExpirationDate -gt (Get-Date) -or ($_.AccountExpirationDate -ne $null)))} 

#users with not expiring passwords and are enabled and not expired
$NotExpiringPWD=Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate 

If ($LockedOutUsers.count -eq $null -and $LockedOutUsers -eq $null){
    $cntLockedOutUsers=0
}Elseif ($LockedOutUsers.count -eq $null -and $LockedOutUsers -ne $null){
    $cntLockedOutUsers=1
}Else{
    $cntLockedOutUsers=@($LockedOutUsers.count)
}

If ($DisabledUsers.count -eq $null -and $DisabledUsers -eq $null){
    $cntDisabledUsers=0
}Elseif ($DisabledUsers.count -eq $null -and $DisabledUsers -ne $null){
    $cntDisabledUsers=1
}Else{
    $cntDisabledUsers=@($DisabledUsers.count)
}

If ($ExpiredUsers.count -eq $null -and $ExpiredUsers -eq $null){
    $cntExpiredUsers=0
}Elseif ($ExpiredUsers.count -eq $null -and $ExpiredUsers -ne $null){
    $cntExpiredUsers=1
}Else{
    $cntExpiredUsers=@($ExpiredUsers.count)
}

If ($NotExpiringPWD.count -eq $null -and $NotExpiringPWD -eq $null){
    $cntNotExpiringPWD=0
}Elseif ($NotExpiringPWD.count -eq $null -and $NotExpiringPWD -ne $null){
    $cntNotExpiringPWD=1
}Else{
    $cntNotExpiringPWD=@($NotExpiringPWD.count)
}

$XML += "<prtg>"
$XML += "<result>" 
$XML += "<channel>Locked Out Users</channel>" 
$XML += "<value>$cntLockedOutUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Disabled Users</channel>" 
$XML += "<value>$cntDisabledUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Expired Users - not disabled</channel>" 
$XML += "<value>$cntExpiredUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Users with password never expires</channel>" 
$XML += "<value>$cntNotExpiringPWD</value>" 
$XML += "</result>"
$XML += "</prtg>"

Function WriteXmlToScreen ([xml]$xml)
{
    $StringWriter = New-Object System.IO.StringWriter;
    $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter;
    $XmlWriter.Formatting = "indented";
    $xml.WriteTo($XmlWriter);
    $XmlWriter.Flush();
    $StringWriter.Flush();
    Write-Output $StringWriter.ToString();
}

WriteXmlToScreen $XML

The above script was as well posted on my blog here.

Regards

Florian Rossmark

www.it-admins.com

Created on Jul 19, 2018 3:36:16 PM by  Florian Rossmark (2,760) 2 2



Votes:

0

Your Vote:

Up

Down

For correct value display when only 1 account is locked you can replace this :

if ($server.count -eq $null -and $server -eq $null){
    $a=0
}
Elseif ($server.count -eq $null -and $server -ne $null){
    $a=1
}
Else
{
    $a=@($server.count)
}

by this :

$server | ForEach-Object {$a++}

Created on Aug 31, 2018 12:02:12 PM by  PO (0) 1

Last change on Aug 31, 2018 12:53:08 PM by  Sebastian Kniege [Paessler Support]



Votes:

0

Your Vote:

Up

Down

Thanks for that script, looking good so far. However, I fear there's an issue with "Expired Users - not disabled". I can imagine this is down to "Get-Date". It also matches users with an expiry date in the future which obviously is wrong. Adding a "-Format "dd.MM.yyyy HH:mm"" also returns the list containing too many users. Any idea?

Created on Nov 2, 2018 9:27:59 AM by  bezibaerchen (0)



Accepted Answer

Votes:

0

Your Vote:

Up

Down

Tested this - and yes you are right.. the correct line would be:

-LT (less than) and a -AND instead of -OR

resulting in this command:

Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {(($_.AccountExpirationDate -lt (Get-Date) -and ($_.AccountExpirationDate -ne $null)))} 

and here the corrected full script:

Import-Module ActiveDirectory

#you could add - filters, a OU limitation or a server against whom this would be executed.. see Get-ADUser options for more details

#all locked users that aren't disabled or expired
$LockedOutUsers=Get-ADUser -Filter {Enabled -eq $True -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {$_.lockedout -eq $True -and (($_.AccountExpirationDate -gt (Get-Date) -or ($_.AccountExpirationDate -eq $null)))} 

#all users that are disabled - this is a manual action in Active Directory
$DisabledUsers=Get-ADUser -Filter {Enabled -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate

#all users that are not disabled but expired already
$ExpiredUsers=Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate | where {(($_.AccountExpirationDate -lt (Get-Date) -and ($_.AccountExpirationDate -ne $null)))} 

#users with not expiring passwords and are enabled and not expired
$NotExpiringPWD=Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and objectCategory -eq "person" -and objectClass -eq "user"} -Properties sAMAccountName,DisplayName,LockedOut,LockoutTime,Enabled,AccountExpirationDate 

If ($LockedOutUsers.count -eq $null -and $LockedOutUsers -eq $null){
    $cntLockedOutUsers=0
}Elseif ($LockedOutUsers.count -eq $null -and $LockedOutUsers -ne $null){
    $cntLockedOutUsers=1
}Else{
    $cntLockedOutUsers=@($LockedOutUsers.count)
}

If ($DisabledUsers.count -eq $null -and $DisabledUsers -eq $null){
    $cntDisabledUsers=0
}Elseif ($DisabledUsers.count -eq $null -and $DisabledUsers -ne $null){
    $cntDisabledUsers=1
}Else{
    $cntDisabledUsers=@($DisabledUsers.count)
}

If ($ExpiredUsers.count -eq $null -and $ExpiredUsers -eq $null){
    $cntExpiredUsers=0
}Elseif ($ExpiredUsers.count -eq $null -and $ExpiredUsers -ne $null){
    $cntExpiredUsers=1
}Else{
    $cntExpiredUsers=@($ExpiredUsers.count)
}

If ($NotExpiringPWD.count -eq $null -and $NotExpiringPWD -eq $null){
    $cntNotExpiringPWD=0
}Elseif ($NotExpiringPWD.count -eq $null -and $NotExpiringPWD -ne $null){
    $cntNotExpiringPWD=1
}Else{
    $cntNotExpiringPWD=@($NotExpiringPWD.count)
}

$XML += "<prtg>"
$XML += "<result>" 
$XML += "<channel>Locked Out Users</channel>" 
$XML += "<value>$cntLockedOutUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Disabled Users</channel>" 
$XML += "<value>$cntDisabledUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Expired Users - not disabled</channel>" 
$XML += "<value>$cntExpiredUsers</value>" 
$XML += "</result>"
$XML += "<result>" 
$XML += "<channel>Users with password never expires</channel>" 
$XML += "<value>$cntNotExpiringPWD</value>" 
$XML += "</result>"
$XML += "</prtg>"

Function WriteXmlToScreen ([xml]$xml)
{
    $StringWriter = New-Object System.IO.StringWriter;
    $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter;
    $XmlWriter.Formatting = "indented";
    $xml.WriteTo($XmlWriter);
    $XmlWriter.Flush();
    $StringWriter.Flush();
    Write-Output $StringWriter.ToString();
}

WriteXmlToScreen $XML

Thanks for pointing this out - did not realize this mistake...

Regards

Florian Rossmark

www.it-admins.com

Created on Nov 2, 2018 1:48:47 PM by  Florian Rossmark (2,760) 2 2



Votes:

0

Your Vote:

Up

Down

You're welcome :)

Always happy to give back. Thanks a bunch to you for coming up with that. Saved time to self-develop.

Created on Nov 2, 2018 1:57:52 PM by  bezibaerchen (0)



Votes:

0

Your Vote:

Up

Down

Hi, where and how can I set a specific OU? I locked out one user for a test but the script shows 0 locked out users. I think the script is looking in the wrong OU.

Created on Nov 12, 2018 10:08:37 AM by  AndreB (0)



Votes:

0

Your Vote:

Up

Down

Script doesn't cehck for a specific OU.

Bear in mind that lock out status needs to be synced between DCs so at the point of time you ran the script, it might either be not synced yet or account may have already unlock again, depending on your domain settings.

Created on Nov 12, 2018 10:15:26 AM by  bezibaerchen (0)



Please log in or register to enter your reply.


Disclaimer: The information in the Paessler Knowledge Base comes without warranty of any kind. Use at your own risk. Before applying any instructions please exercise proper system administrator housekeeping. You must make sure that a proper backup of all your data is available.