PowerShell Script to Determine What Device is Locking Out an Active Directory User Account

I recently received a request to determine why a specific user account was constantly being locked out after changing their Active Directory password and while I've previously written scripts to accomplish this same type of task, I decided to write an updated script.

Active Directory user account lockouts are replicated to the PDC emulator in the domain through emergency replication and while I could have used the Get-ADDomain cmdlet to easily determine the PDC emulator for the domain:

1(Get-ADDomain).PDCEmulator

pdc-emulator.png

That would have had a dependency of requiring the RSAT tools to be installed on the workstation this script was being run from so I decided to use the .NET framework to accomplish that particular task to eliminate the Active Directory module dependency:

1[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain((
2    New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $env:USERDNSDOMAIN))
3).PdcRoleOwner.name

pdc-emulator-netframework.png

If you're interested in using the .NET framework to determine the Active Directory FSMO role holders with PowerShell, I wrote a blog article titled PowerShell Function to Determine the Active Directory FSMO Role Holders via the .NET Framework that covers that subject in more detail.

This Get-LockedOutUser.ps1 script allows you to specify the following via parameter input to narrow down the results:

  • Specific userid, defaulting to all locked out userid's
  • Start time to begin searching records for, defaulting to the last three days
  • Domain name to search for lockouts in, defaulting to the user's domain who is running the script
 1#Requires -Version 3.0
 2<#
 3.SYNOPSIS
 4    Get-LockedOutUser.ps1 returns a list of users who were locked out in Active Directory.
 5
 6.DESCRIPTION
 7    Get-LockedOutUser.ps1 is an advanced script that returns a list of users who were locked out in Active Directory
 8by querying the event logs on the PDC emulator in the domain.
 9
10.PARAMETER UserName
11    The userid of the specific user you are looking for lockouts for. The default is all locked out users.
12
13.PARAMETER StartTime
14    The datetime to start searching from. The default is all datetimes that exist in the event logs.
15
16.EXAMPLE
17    Get-LockedOutUser.ps1
18
19.EXAMPLE
20    Get-LockedOutUser.ps1 -UserName 'mikefrobbins'
21
22.EXAMPLE
23    Get-LockedOutUser.ps1 -StartTime (Get-Date).AddDays(-1)
24
25.EXAMPLE
26    Get-LockedOutUser.ps1 -UserName 'mikefrobbins' -StartTime (Get-Date).AddDays(-1)
27#>
28
29[CmdletBinding()]
30param (
31    [ValidateNotNullOrEmpty()]
32    [string]$DomainName = $env:USERDOMAIN,
33
34    [ValidateNotNullOrEmpty()]
35    [string]$UserName = "*",
36
37    [ValidateNotNullOrEmpty()]
38    [datetime]$StartTime = (Get-Date).AddDays(-3)
39)
40
41Invoke-Command -ComputerName (
42
43    [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain((
44        New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $DomainName))
45    ).PdcRoleOwner.name
46
47) {
48
49    Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740;StartTime=$Using:StartTime} |
50    Where-Object {$_.Properties[0].Value -like "$Using:UserName"} |
51    Select-Object -Property TimeCreated,
52        @{Label='UserName';Expression={$_.Properties[0].Value}},
53        @{Label='ClientName';Expression={$_.Properties[1].Value}}
54
55} -Credential (Get-Credential) |
56Select-Object -Property TimeCreated, UserName, ClientName

The script prompts for alternate credentials because in my opinion, you shouldn't be running your PowerShell session with elevated credentials:

ad-lockout-device0.png

As you can see, jimmy0 is being locked out by a device named PC01:

ad-lockout-device1.png

The StartTime parameter can be used to specify more or fewer days if the default of three days doesn't meet your needs as shown in the following example where one day is used:

ad-lockout-device2.png

Update 08/30/15:

I've posted an updated version of this script which is now a PowerShell function that includes error handling that can be downloaded from my Active Directory repository on GitHub.

µ