I want to find out where from a user account is locked out in my domain.
The manual way to do this would be to open up Event Viewer, scan the event logs on the DC for event ID 4740, open it up and see the message to identify the machine from where this account was locked out. But using PowerShell we can obviously automate this way easily!
First things first: how to scan the event logs using PowerShell? You can use either of Get-EventLog
or Get-WinEvent
. The former only works on the classic event logs while the latter works on both the classic and newer event logs such as those found in Windows Vista and later. I find the latter faster too, so let’s use that. Here’s me running that cmdlet to filter the security logs for event ID 4740 on a remote DC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
PS> Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName dc01 | fl * Message : A user account was locked out. Subject: Security ID: S-1-5-18 Account Name: DC01$ Account Domain: ENTERPRISE Logon ID: 0x3e7 Account That Was Locked Out: Security ID: S-1-5-21-1606980848-515967899-682003330-66682 Account Name: User01 Additional Information: Caller Computer Name: MANGO Id : 4740 Version : 0 Qualifiers : Level : 0 Task : 13824 Opcode : 0 Keywords : -9214364837600034816 RecordId : 148130232 ProviderName : Microsoft-Windows-Security-Auditing ProviderId : 54849625-5478-4994-a5ba-3e3b0328c30d LogName : Security ProcessId : 468 ThreadId : 1304 MachineName : DC01.enterprise.local UserId : TimeCreated : 23/05/2013 09:26:04 ActivityId : RelatedActivityId : ContainerLog : security MatchedQueryIds : {} Bookmark : System.Diagnostics.Eventing.Reader.EventBookmark LevelDisplayName : Information OpcodeDisplayName : Info TaskDisplayName : User Account Management KeywordsDisplayNames : {Audit Success} Properties : {System.Diagnostics.Eventing.Reader.EventProperty, System.Diagnostics.Eventing.Reader.EventPrope rty, System.Diagnostics.Eventing.Reader.EventProperty, System.Diagnostics.Eventing.Reader.EventP roperty...} |
There’s only one event here so the details of that are returned. It gives us all the info we need at a glance: user name “User01” is locked thanks to a bad password attempt from the computer “MANGO”.
Neat!
Now let’s go about making this prettier and better.
In the code above I am specifying the domain controller to query. That’s only helpful if I have an idea of the machine or site the user is trying to login to, and so know the domain controller responsible for that site. A better approach would be to query the domain controller with the PDC Emulator FSMO role for event ID 4740. This DC is the one that eventually processes the account lockout requests for all accounts in the domain.
The cmdlet Get-ADDomainContoller
gives you the domain controller of the site you are in. Adding the switch -Filter *
gives you all the domain controllers in the domain. And piping this through a where-object
to filter out the domain controller with the “PDC Emulator” FSMO role gives you the domain controller we are interested in. Like thus:
1 |
Get-ADDomainController -Filter * | where-object {$_.OperationMasterRoles -contains "PDCEmulator"} |
The snippet above returns an object for the DC with the PDC Emulator role, to extract just the name do thus:
1 |
(Get-ADDomainController -Filter * | where-object {$_.OperationMasterRoles -contains "PDCEmulator"}).Name |
Next, let’s go about presenting the output above in a tidier format.
One thing to note is that all the info we want is in a property called Message
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
PS> Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName dc01 | gm TypeName: System.Diagnostics.Eventing.Reader.EventLogRecord Name MemberType Definition ---- ---------- ---------- Dispose Method System.Void Dispose() Equals Method bool Equals(System.Object obj) FormatDescription Method string FormatDescription(), string FormatDescription(System.Collections.Generic.IE... GetHashCode Method int GetHashCode() GetPropertyValues Method System.Collections.Generic.IList[System.Object] GetPropertyValues(System.Diagnosti... GetType Method type GetType() ToString Method string ToString() ToXml Method string ToXml() Message NoteProperty System.String Message=A user account was locked out.... ActivityId Property System.Nullable`1[[System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, Public... ... Properties Property System.Collections.Generic.IList`1[[System.Diagnostics.Eventing.Reader.EventProper... ... PS> Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName dc01 | select -ExpandProperty Message A user account was locked out. Subject: Security ID: S-1-5-18 Account Name: DC01$ Account Domain: ENTERPRISE Logon ID: 0x3e7 Account That Was Locked Out: Security ID: S-1-5-21-1606980848-515967899-682003330-66682 Account Name: User01 Additional Information: Caller Computer Name: MANGO |
So one option would be to do some fancy filtering using foreach-object
like thus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
PS> Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName dc01 | %{ write-host -ForegroundColor Red "At" $_.TimeCreated; write-host -ForegroundColor White $_.Message; write-host } At 23/05/2013 09:26:04 A user account was locked out. Subject: Security ID: S-1-5-18 Account Name: DC01$ Account Domain: ENTERPRISE Logon ID: 0x3e7 Account That Was Locked Out: Security ID: S-1-5-21-1606980848-515967899-682003330-66682 Account Name: User01 Additional Information: Caller Computer Name: MANGO |
This will list out the multiple lockout entries with the timestamp on top in red.
A better option, however, is to use the Properties
property, which is an array containing exactly the info we want as values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
PS> Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName dc01 | select -ExpandProperty Properties Value ----- User01 MANGO S-1-5-21-1606980848-515967899-682003330-66682 S-1-5-18 DC01$ ENTERPRISE 999 PS> (Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName dc01).Properties | gm TypeName: System.Diagnostics.Eventing.Reader.EventProperty Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Value Property System.Object Value {get;} |
Format-Table
is your friend here:
1 2 3 4 5 6 7 8 9 10 11 12 |
PS> Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName (Get-ADDomainController -Filter * | where-object {$_.OperationMasterRoles -contains "PDCEmulator"}).Name | ` ft @{Name="User";Expression={$_.Properties[0].Value}},@{Name="Computer";Expression={$_.Properties[1].Value}},@{Name="Time";Expression={$_.TimeCreated}} User Computer Time ---- -------- ---- User01 Computer01 23/05/2013 12:20:41 User02 Computer02 23/05/2013 11:55:56 User01 Computer01 23/05/2013 11:42:40 User03 Computer06 23/05/2013 11:36:53 User01 Computer01 23/05/2013 11:03:39 User01 Computer01 23/05/2013 10:25:37 |
A one line code that’s way better than opening Event Viewer and filtering through the logs manually.
And if you want to filter by the locked out status of a specific $user
, pipe the output through a where-object
like thus:
1 2 3 |
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740} -ComputerName (Get-ADDomainController -Filter * | where-object {$_.OperationMasterRoles -contains "PDCEmulator"}).Name | ` Where-Object {$_.Properties[0].Value -match "$user"} | ` ft @{Name="User";Expression={$_.Properties[0].Value}},@{Name="Computer";Expression={$_.Properties[1].Value}},@{Name="Time";Expression={$_.TimeCreated}} |