One of the most basic and repetitive tasks for system administrators is certainly unlocking Active Directory user accounts. It’s very easy to underestimate it, in fact, this operation isn’t perceived not just by users, but more importantly by junior engineers not important at all! Frequently providing some general feedback to the user on this issue or simply a response that this issue is now it’s been fixed.
On top of that, I’ve found that in some situation finding the root cause it requires a bit of investigation and experience not just to guess what more likely is causing it and not many of us are willing to take this effort. But I like to explain and document what happened to the user, to me it’s absolutely key to raise the awareness and trust people and processes involved in the IT System.
What do we use to unlock AD Accounts?
AD User and Computers or RSAT (Remote Server Administration Tool) and more specifically ADAC (Active Directory Administrative Center) are the go-to choices for most sysadmins, it offers a neat and very intuitive interface that it’s very easy to become familiar with the interface. But, again, what I think is more effective is Powershell for the lockout events analysis and solution in all phases. Let me explain why.
First of all, every issue, not just simple user support request have these 5 usual steps:
- Mitigate
- Investigate/ Root Cause Analysis
- Solve
- Document
- Monitor
Let me show you how to achieve all these steps with Powershell, if you like you can also glue all these scripts together in one, for example for this unlocking an account (unlock the account, investigate on the root cause, send an email to the user with the findings and keep it monitor for another hour).
Mitigation: Unlock the account
A simple AD Account lock-out event can be perceived differently and the impact on productivity can be subjective, in any case, our intent in case this event was triggered by the legitimate user by mistake our goal is to limit this access outage to be shorter as possible and protecting our environment at the same time.
So let’s start with the first step search for a locked out account (these cmd-lets requires the ActiveDirectory module).
1 |
Search-ADAccount -lockedout |
If you know the user you can search it using the display name attribute
1 |
get-aduser -filter {displayname -like "Paolo*"} -properties LockedOut |
So the action to mitigate is simply using the cmd-let:
1 |
Unlock-ADAccount |
And if we have an AD User object or list of object we can pipe the commands :
1 |
Search-ADAccount -LockedOut | Unlock-ADAccount |
In this example, we unlocked all locked-out users. It’s a good idea to use the arguments -confirm, -whatif or -verbose to show a little bit more output on the shell session.
Investigate / Find the root cause of the Account Lockout Event
Users have a limited knowledge of the security policies involved in the IT Systems. An account locked out it’s a good opportunity of for the user to know what he needs to prevent for happening again and for the system administrator to measure the user awareness of how the IT systems work.
AAA of the security: Authentication, Authorization and Accounting
All user are sometimes familiar with AD password complexity and requirements, but not on the security policies that apply to the authentication process.
So let’s show them what rules are applied for each user password with this simple cmd-let according to the Group Policies of your AD domain:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#And Password policy Get-ADDefaultDomainPasswordPolicy ComplexityEnabled : True DistinguishedName : DC=contoso,DC=com LockoutDuration : 00:15:00 LockoutObservationWindow : 00:15:00 LockoutThreshold : 5 MaxPasswordAge : 42.00:00:00 MinPasswordAge : 1.00:00:00 MinPasswordLength : 7 objectClass : {domainDNS} objectGuid : dfc165e9-bbf7-46a3-82c8-6eecac1e2496 PasswordHistoryCount : 24 ReversibleEncryptionEnabled : False |
All keys and value are easy to explain the important values here are mainly 3:
- LockoutThreshold (5)
- LockoutObservationWindow (15minutes)
- LockoutDuration (15minutes)
The way that I simply explain this security policy with an example like this:
After 5 (LockoutThreshold ) unsuccessful authentication attempts within a period of 15 minutes (LockoutObservationWindow) your account will be locked out for 15 minutes (LockoutDuration).
It’s implicit that even with the right password if the account is still locked out every authentication process will fail. The lockout will last just 15 minutes, then the user will be able to log in again. To unlock it manually the required permissions are delegated to a support security group or performed directly by a Domain Admin.
Not all users are aware of what is a brute-force attack, or a dictionary attack, for this reason, in order to prevent these risks we need security policies to mitigate or make these types of attacks more visible for the admins and expensive for the attacker.
Ok, now we know the why and what policies that are applied in our organization. But what happened to that user account?
User authentication attempts on devices, computers or servers joined to the domain are requested to a Domain Controller. These requests are logged and with event viewer, we can filter the Windows Logs, Security and filter the ID 4740:
With powershell, we can even perform this operation remotely and against more ADDC.
1 |
Get-EventLog -LogName Security -ComputerName $(Get-ADDomainController).hostname -InstanceId 4740 -newest 5 |
In case you have multiple DC you can simply pipe the list to the same one-liner
1 |
"dc01.contoso.com", "dc02.contoso.com", "dc03.contoso.com", "dc04.contoso.com" | % { Get-EventLog -LogName Security -ComputerName $_ -InstanceId 4740 -newest 5} |
I’m just interested in the newest 5 events. The Message field contains also the Caller ID, the source hostname of the authentication request that caused the lockout.
There is also an interesting function on the powershell gallery called Get-LockedOutLocation
Document
The common root cause for a lockout can be simple or more complicated according to the experience of the user, but in general, a user change password and then there are some authentication attempts:
- from application or clients with an old password saved
- a shared folder mount persistently
- script or service running with an old password
Especially for junior developers service accounts usage is sometimes avoided or left as the last step. But using user accounts instead of service accounts or storing that credential is some scripts are all bad practices that’s better to stop from the start.
Service Accounts benefits
Service accounts have limited access (according to least privilege principle), a strong credential, the password can be set to never expire and the password can’t be changed.
Document all these steps can be a simple script that collects all the previous commands, a brief description of the security policies and can be pasted on the support request or emailed back to the user.
Lock-out Events can be rare, but when the change credential and there is a first lockout event, can happen again triggered by some other request. For this reason after the first attempt can be useful to monitor lockout events.
Monitoring: Active Directory account LockOut
I’ve created this ad-hoc script that whenever an AD User is being locked out it displays a toast message with the username.
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 |
#requires -module BurntToast,ActiveDirectory #Paolo Frigo, https://www.scriptinglibrary.com $SleepTime = 60 #seconds do { if (Search-ADAccount -LockedOut) { foreach ($user in (Search-ADAccount -LockedOut)) { New-Burnttoastnotification -text "Locked-Out User Notification", "$($($user).name) is now Locked-Out" } } start-sleep -Seconds $SleepTime } while ($True) <# # If you want a limited number of test you can use a foreach loop instead of a do-while # ForEach ($i in (1..100)){ if (Search-ADAccount -LockedOut) { foreach ($user in (Search-ADAccount -LockedOut)) { New-Burnttoastnotification -text "Locked-Out User Notification", "$($($user).name) is now Locked-Out" } } start-sleep -Seconds $SleepTime } #> # A better approach can be simple schedule this job and run it any minute. |
This is a sample of the notification message using BurntToast:
BurntToast it’s really nice if you haven’t tried it already please do! It’s flexible and easy to use.
Let’s prove that works with a test account.
In this last part I wrap up on a powershell script a way to prove all that I’ve explained creating a test account and locking out and search for the caller id:
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
#Paolo Frigo, scriptinglibrary.com #requires -runasadministrator #requires -module ActiveDirectory #how to test ad account lockout policies #create a test user New-aduser -name "test-user" #let's check the user created Get-ADUser test-user <# DistinguishedName : CN=test-user,CN=Users,DC=contoso,DC=com Enabled : False GivenName : Name : test-user ObjectClass : user ObjectGUID : 8353f6f5-2a3d-4096-a021-a430e3f257cc SamAccountName : test-user SID : S-1-5-21-3655427247-682778731-3851803015-1103 Surname : UserPrincipalName : #> #Reset the password for this user: Get-ADUser test-user | Set-ADAccountPassword <# Please enter the current password for 'CN=test-user,CN=Users,DC=contoso,DC=com' Password: Please enter the desired password for 'CN=test-user,CN=Users,DC=contoso,DC=com' Password: ********* Repeat Password: ********* #> #Let's enable our test account Get-ADUser test-user | Enable-ADAccount #let's check the AD default domain password policies Get-ADDefaultDomainPasswordPolicy <# ComplexityEnabled : True DistinguishedName : DC=contoso,DC=com LockoutDuration : 00:30:00 LockoutObservationWindow : 00:30:00 LockoutThreshold : 5 MaxPasswordAge : 42.00:00:00 MinPasswordAge : 1.00:00:00 MinPasswordLength : 7 objectClass : {domainDNS} objectGuid : dfc165e9-bbf7-46a3-82c8-6eecac1e2496 PasswordHistoryCount : 24 ReversibleEncryptionEnabled : False #> #let's double check our user before staring to test Get-ADUser test-user -properties LockedOut, LastBadPasswordAttempt <# DistinguishedName : CN=test-user,CN=Users,DC=contoso,DC=com Enabled : True GivenName : LastBadPasswordAttempt : 1/11/2018 11:37:45 PM LockedOut : False Name : test-user ObjectClass : user ObjectGUID : 8353f6f5-2a3d-4096-a021-a430e3f257cc SamAccountName : test-user SID : S-1-5-21-3655427247-682778731-3851803015-1103 Surname : UserPrincipalName : #> #Let's make 5 wrong authentication attempts to lockout the test user (1..5)| %{runas /user:contoso\test-user cmd} <# Enter the password for contoso\test-user: Attempting to start cmd as user "contoso\test-user" ... RUNAS ERROR: Unable to run - cmd 1326: The user name or password is incorrect. Enter the password for contoso\test-user: Attempting to start cmd as user "contoso\test-user" ... RUNAS ERROR: Unable to run - cmd 1326: The user name or password is incorrect. Enter the password for contoso\test-user: Attempting to start cmd as user "contoso\test-user" ... RUNAS ERROR: Unable to run - cmd 1909: The referenced account is currently locked out and may not be logged on to. Enter the password for contoso\test-user: Attempting to start cmd as user "contoso\test-user" ... RUNAS ERROR: Unable to run - cmd 1909: The referenced account is currently locked out and may not be logged on to. Enter the password for contoso\test-user: Attempting to start cmd as user "contoso\test-user" ... RUNAS ERROR: Unable to run - cmd 1909: The referenced account is currently locked out and may not be logged on to. #> #let's check our user Get-ADUser test-user -properties LockedOut, LastBadPasswordAttempt <# DistinguishedName : CN=test-user,CN=Users,DC=contoso,DC=com Enabled : True GivenName : LastBadPasswordAttempt : 1/11/2018 11:59:00 PM LockedOut : True Name : test-user ObjectClass : user ObjectGUID : 8353f6f5-2a3d-4096-a021-a430e3f257cc SamAccountName : test-user SID : S-1-5-21-3655427247-682778731-3851803015-1103 Surname : UserPrincipalName : #> #search for locked out accounts Search-ADAccount -LockedOut <# AccountExpirationDate : DistinguishedName : CN=test-user,CN=Users,DC=contoso,DC=com Enabled : True LastLogonDate : LockedOut : True Name : test-user ObjectClass : user ObjectGUID : 8353f6f5-2a3d-4096-a021-a430e3f257cc PasswordExpired : False PasswordNeverExpires : False SamAccountName : test-user SID : S-1-5-21-3655427247-682778731-3851803015-1103 UserPrincipalName : #> #investigate on specific user Get-ADUser -Filter {DisplayName -like "John D*"} -Properties PasswordExpired, PasswordLastSet, EmailADdress,BadLogonCount,lastbadpasswordattempt, Lastlogondate, LockedOut, LockoutTime Get-EventLog -LogName Security -ComputerName $(Get-ADDomainController).hostname -InstanceId 4740 -newest 5 <# Index Time EntryType Source InstanceID Message ----- ---- --------- ------ ---------- ------- 13236 Nov 01 23:59 SuccessA... Microsoft-Windows... 4740 A user account was locked out.... #> #Let's search for the caller ID Get-EventLog -LogName Security -ComputerName $(Get-ADDomainController).hostname -InstanceId 4740 -newest 1 | select -exp Message <# A user account was locked out. Subject: Security ID: S-1-5-18 Account Name: MYDC$ Account Domain: CONTOSO Logon ID: 0x3e7 Account That Was Locked Out: Security ID: S-1-5-21-3655427247-682778731-3851803015-1103 Account Name: test-user Additional Information: Caller Computer Name: MYDC #> #Let's close this test by disabling the test-account Disable-ADAccount test-user |
It’s a long article, I hope that you’ll find it useful. As always all these scripts are available on my github repository.