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

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.

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

Set Scheduled Task

  1. Download the script from GitHub or from below
  2. Add your Teams Webhook URL
  3. Enter the name of your domain controller. I am running mine on a domain controller so 
  4. Save the script somewhere you can reference later. I save all of mine at “C:\Automation”
  5. Open Task Scheduler and create a new Task
  6. Configure the trigger to be “When a specific event is logged”
  7. Set the log to “Security” and the Event ID to “4740”
  8. Set the Action to “Start a program”
  9. 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.
  10. 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.

	 Created on:   	12/13/2018 3:57 PM
	 Created by:   	Bradley Wyatt
	 Filename:     	PSPush_LockedOutUsers.ps1
		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

#Image on the left hand side, here I have a regular user picture
$ItemImage = ''

$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

$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'


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

  1. I’m getting an error “Invoke-RestMethod : Invalid URI: The hostname could not be parsed”, my PSVersion is 4.0 so i’m trying to update to 5.1 to see if that helps, any idea why i would be getting that error

  2. Great guide, looking to have fun with it 🙂 I did notice one thing though, under Set Scheduled Task, point 3:
    “Enter the name of your domain controller. I am running mine on a domain controller so ”
    It just cuts off there!
    Thanks, Coen

  3. Thank you Brad, nice script. It encourages me to use Teams for more notifications, for example snapshots on our clusters (which need to be cleaned up, but often don’t get cleaned up).

    Keep up the good work!

  4. Great job! Some notes from our version of doing.

    * Run your script on the DC running the PDC emulator. Lockout events are immediately replicated to the PDC. I *think* bad password attempts are local to each source and not replicated.
    * Have the script also send the event data to syslog and/or email. We find it easier to spot patterns in lockout events when viewing them in Kibana or Outlook.

  5. Experiencing the following error:

    You cannot call a method on a null-valued expression.
    At C:\Scripts\PSPush_LockOut.ps1:37 char:70
    + … ed out at $(($RecentLockedOutUser.LockoutTime).ToString(“hh:mm:ss tt” …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

  6. The message I receive says ” ‘s account was locked out at and may require additional assistance”

    Basically, the script isn’t getting any of the specific information just that there has been a locked account. Any ideas why this is happening? I am running the script on the domain controller.

    1. how are you tripping the script to run? it should be ran on the eventID thats tripped when an account is locked out, if its being tripped any other time it will return NULL because there may not be any accounts locked out

      1. Good Morning,

        I am getting this same issue, i have followed you guide to the T and its still giving me this same message.

        Locked Out User – Notification
        ‘s account got locked out at
        ‘s account was locked out at and may require additional assistance

        1. go through the script step by step – sounds like it’s not reading the event log correctly. make sure the variables show objects or values

  7. Thanks for this! this is really useful. any way to get a notification in teams if a user is deleted from AD?

  8. Hey Brad,

    Thanks for your scripts they are super helpful.

    I am curious if you know of a way to perform the @person or @team using the webhook? Would be super helpful to get a notification pop-up in Teams when this occurs.


  9. Hey, Brad.

    I’ve been searching far and wide across the internet.. I used to run a script that does almost exactly what yours does, but it monitors all changes, like lock-outs, disabled/enabled events, password expires, etc and pumps them all to Teams.

    It ran great in my environment for a while until it got encrypted by baddies and I lost it. I thought I got it from your site, but not sure.. did you ever post anything like that?

    Here’s a screenshot of it as well:

    1. I have several posts that send alerts to teams via webhooks using PS – that should be the same with what you had

    2. @Neil, would you be able to provide the event ID code for enable/disabling of the account or which event ID is used when an account has been created?

  10. I got the script working after a bit of stuffing about, and it was step 3 that was tripping me up.

    it looks like you’ve made a variable for the script (line 29 $DomainContoller), but haven’t given the user a spot to define it, so glancing over the script you miss it.

    If its not defined it will “work” but the notifications will be

    ” ‘s account was locked out at and may require additional assistance”

Leave a Reply

Your email address will not be published. Required fields are marked *