Get a Teams Notification the Moment an Active Directory User gets Locked Out with PowerShell Using Webhooks

I have been recently using Teams as a central location for my organizations technical notifications instead of email as it provides a way for an entire Help Desk team to openly collaborate on the message and its contents. I recently got a request to get a Teams notification when a user gets locked out of their Active Directory account. By setting up a Webhook connector we can make it happen. The script will be triggered from Task Scheduler on Event ID 4740 which is created when a user gets locked out. By using “Search-ADAccount -LockedOut” we can return an array of locked out accounts, but by ordering it by lockout time we can ensure that we grab the most recent locked out user that corresponds to the security event.

I set the script and scheduled task up on my PDC because as far as I know, the actual lockout event is only logged on the DC holding PDC Emulator FSMO role.

Configure Incoming Webhook

To allow PowerShell to send data to your Teams Channel you will need to configure an incoming Webhook.

In your Team, click on the channel you want the messages to be sent to Click on the 3 dots underneath the chat window, and then select “Go to store” Search for Webhook and then select it to begin configuring the Webhook You can keep the settings as is and press “Install” button located at the bottom Select the channel you want the incoming webhook to use and then press “Set Up” Give you webhook a good name. This is what users will see in the Teams chat. Upload an image and then press “Create” Copy the URL and save it for later, it will be needed. Click “Done” when you have saved the URL in a safe spot. Back in the Teams channel you can see that the webhook has been created.

Set Scheduled Task

Download the script from GitHub or from below Add your Teams Webhook URL Enter the name of your domain controller. I am running mine on a domain controller so Save the script somewhere you can reference later. I save all of mine at “C:\Automation” Open Task Scheduler and create a new Task Configure the trigger to be “When a specific event is logged” Set the log to “Security” and the Event ID to “4740” Set the Action to “Start a program” The program/script will be pointed to your script that you saved earlier. Before entering the file name enter “powershell.exe -filepath” so it knows to run the file in PowerShell. When you save your newly created scheduled task, go back in and make sure you enable “Run whether user is logged on or not”

Script / Source Code

The source code is maintained on GitHub but you can download it below as well.

<# .NOTES =========================================================================== Created on: 12/13/2018 3:57 PM Created by: Bradley Wyatt Filename: PSPush_LockedOutUsers.ps1 =========================================================================== .DESCRIPTION Sends a Teams notification via webhook of a recently locked out user. Set up a scheduled task to trigger on event ID 4740. #> #Teams webhook url $uri = "[INSERT WEBHOOK URL]" #Image on the left hand side, here I have a regular user picture $ItemImage = 'https://img.icons8.com/color/1600/circled-user-male-skin-type-1-2.png' $ArrayTable = New-Object 'System.Collections.Generic.List[System.Object]' $Event = Get-EventLog -LogName Security -InstanceId 4740 | Select-object -First 1 [string]$Item = $Event.Message $Item.SubString($Item.IndexOf("Caller Computer Name")) $sMachineName = $Item.SubString($Item.IndexOf("Caller Computer Name")) $sMachineName = $sMachineName.TrimStart("Caller Computer Name :") $sMachineName = $sMachineName.TrimEnd("}") $sMachineName = $sMachineName.Trim() $sMachineName = $sMachineName.TrimStart("\\") $RecentLockedOutUser = Search-ADAccount -server $DomainContoller -LockedOut | Get-ADUser -Properties badpwdcount, lockoutTime, lockedout, emailaddress | Select-Object badpwdcount, lockedout, Name, EmailAddress, SamAccountName, @{ Name = "LockoutTime"; Expression = { ([datetime]::FromFileTime($_.lockoutTime).ToLocalTime()) } } | Sort-Object LockoutTime -Descending | Select-Object -first 1 $RecentLockedOutUser | ForEach-Object { $Section = @{ activityTitle = "$($_.Name)" activitySubtitle = "$($_.EmailAddress)" activityText = "$($_.Name)'s account was locked out at $(($_.LockoutTime).ToString("hh:mm:ss tt")) and may require additional assistance" activityImage = $ItemImage facts = @( @{ name = 'Lockout Source:' value = $sMachineName }, @{ name = 'Lock-Out Timestamp:' value = $_.LockoutTime.ToString() }, @{ name = 'Locked Out:' value = $_.lockedout }, @{ name = 'Bad Password Count:' value = $_.badpwdcount }, @{ name = 'SamAccountName:' value = $_.SamAccountName } ) } $ArrayTable.add($section) } $body = ConvertTo-Json -Depth 8 @{ title = "Locked Out User - Notification" text = "$($RecentLockedOutUser.Name)'s account got locked out at $(($RecentLockedOutUser.LockoutTime).ToString("hh:mm:ss tt"))" sections = $ArrayTable } Write-Host "Sending lockedout account POST" -ForegroundColor Green Invoke-RestMethod -uri $uri -Method Post -body $body -ContentType 'application/json' 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 <# .NOTES =========================================================================== Created on: 12/13/2018 3:57 PM Created by: Bradley Wyatt Filename: PSPush_LockedOutUsers.ps1 =========================================================================== .DESCRIPTION Sends a Teams notification via webhook of a recently locked out user. Set up a scheduled task to trigger on event ID 4740. #> #Teams webhook url $uri = "[INSERT WEBHOOK URL]" #Image on the left hand side, here I have a regular user picture $ItemImage = 'https://img.icons8.com/color/1600/circled-user-male-skin-type-1-2.png' $ArrayTable = New-Object 'System.Collections.Generic.List[System.Object]' $Event = Get-EventLog -LogName Security -InstanceId 4740 | Select-object -First 1 [ string ] $Item = $Event . Message $Item . SubString ( $Item . IndexOf ( "Caller Computer Name" ) ) $sMachineName = $Item . SubString ( $Item . IndexOf ( "Caller Computer Name" ) ) $sMachineName = $sMachineName . TrimStart ( "Caller Computer Name :" ) $sMachineName = $sMachineName . TrimEnd ( "}" ) $sMachineName = $sMachineName . Trim ( ) $sMachineName = $sMachineName . TrimStart ( "\\" ) $RecentLockedOutUser = Search-ADAccount -server $DomainContoller -LockedOut | Get-ADUser -Properties badpwdcount , lockoutTime , lockedout , emailaddress | Select-Object badpwdcount , lockedout , Name , EmailAddress , SamAccountName , @ { Name = "LockoutTime" ; Expression = { ( [ datetime ] :: FromFileTime ( $_ . lockoutTime ) . ToLocalTime ( ) ) } } | Sort-Object LockoutTime -Descending | Select-Object -first 1 $RecentLockedOutUser | ForEach-Object { $Section = @ { activityTitle = "$($_.Name)" activitySubtitle = "$($_.EmailAddress)" activityText = "$($_.Name)'s account was locked out at $(($_.LockoutTime).ToString(" hh : mm : ss tt ")) and may require additional assistance" activityImage = $ItemImage facts = @ ( @ { name = 'Lockout Source:' value = $sMachineName } , @ { name = 'Lock-Out Timestamp:' value = $_ . LockoutTime . ToString ( ) } , @ { name = 'Locked Out:' value = $_ . lockedout } , @ { name = 'Bad Password Count:' value = $_ . badpwdcount } , @ { name = 'SamAccountName:' value = $_ . SamAccountName } ) } $ArrayTable . add ( $section ) } $body = ConvertTo-Json -Depth 8 @ { title = "Locked Out User - Notification" text = "$($RecentLockedOutUser.Name)'s account got locked out at $(($RecentLockedOutUser.LockoutTime).ToString(" hh : mm : ss tt "))" sections = $ArrayTable } Write-Host "Sending lockedout account POST" -ForegroundColor Green Invoke-RestMethod -uri $uri -Method Post -body $body -ContentType 'application/json'

My name is Bradley Wyatt; I am a Microsoft Most Valuable Professional and I am currently a Manager DevOps Cloud Automation at BDO Digital in the Chicagoland area.

Share this: Twitter

Facebook

LinkedIn

Reddit

Email

