Post Users with Expiring Passwords as Microsoft Teams Message with PowerShell

Post Users with Expiring Passwords as Microsoft Teams Message with PowerShell

Microsoft Teams has many connectors available including Incoming Webhook. “This provides an easy solution to post notifications / messages from any scripting language through JSON formatted web service call.”1

In this post I will show you how you can gather all of your users who have passwords expiring within a specified time range, and send a notification including all relevant information to a Teams Channel. In my example I will get all users who have passwords expiring in 7 days and less and have it notify my “Help Desk” Teams Channel.  

The current script will parse only enabled users because we don’t need to report on users, ‘passwordlastset’ attribute if the account isn’t even allowed to log in. It will also sort all of our data, so the users with passwords expiring the earliest will always be at the top of the message. The top description under, “Users With Passwords Expiring – Notification” will display the total amount of users and the amount of days you originally set. 

**Update 12/10**: I have split the tables into their own messages. So you will get one message containing all of your users that are expiring, and another of all accounts that have expired. This helps keep the data separate as well as avoid HTTP error 413 which occurs when your webhook message is too large.

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.

Configure PowerShell to Push to Webhook

Now we will configure a PowerShell script to scrape Active Directory for our data, parse it, and then send over items that match our query to Teams as a message. 

  1. Download or copy the script here
  2. Put in the URL for your webhook that you save earlier, as the value for the variable, “$uri”
  3. If you want to change the number of days you want to be notified on, change the variable, “$LessThan”. Currently I have it set at 7. So PowerShell will only gather users who have passwords expiring in 7 days or less.
  4. In my notification message, the user avatar is a red haired “person”. But you can make it whatever you want by modifying the ItemImage variable.
  5. Once you have made it fit your organizations needs, run the PowerShell script.
  6. In my example I ran it in ISE. At the bottom I can see it ran without any issues
  7. Back in Teams I can see my two users that need their passwords changed.

Configure Job as Scheduled Task

  1. In my environment I saved the script at C:\Automation
  2. In Task Scheduler I am going to create a basic task
  3. In the program/script, enter “Powershell -file “FILE LOCATION AND NAME.ps1″” 
  4. Save the scheduled task. Back in general make sure it will run if you are logged in or not. Also modify the privileges to best fir your environment. 

Script / Download

You can download or copy the script below or on GitHub

$SendMessage = $null
#Get all users whose password expires in X days and less, this sets the days
$LessThan = 7
#Teams web hook URL

$ItemImage = ''

$PWExpiringTable = New-Object 'System.Collections.Generic.List[System.Object]'
$ArrayTable = New-Object 'System.Collections.Generic.List[System.Object]'
$ArrayTableExpired = New-Object 'System.Collections.Generic.List[System.Object]'

$maxPasswordAge = ((Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge).Days
#Get all users and store in a variable named $Users
get-aduser -filter { (PasswordNeverExpires -eq $false) -and (enabled -eq $true) } -properties * | ForEach-Object{
	Write-Host "Working on $($_.Name)" -ForegroundColor White
	#Get Password last set date
	$passwordSetDate = ($_.PasswordLastSet)
	if ($null -eq $passwordSetDate)
		#0x1 = Never Logged On
		$daystoexpire = "0x1"
		#Check for Fine Grained Passwords
		$PasswordPol = (Get-ADUserResultantPasswordPolicy -Identity $_.objectGUID -ErrorAction SilentlyContinue)
		if ($Null -ne ($PasswordPol))
			$maxPasswordAge = ($PasswordPol).MaxPasswordAge
		$expireson = $passwordsetdate.AddDays($maxPasswordAge)
		$today = (Get-Date)
		#Gets the count on how many days until the password expires and stores it in the $daystoexpire var
		$daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
		If ($daystoexpire -lt ($LessThan + 1))
			write-host "$($_.Name) will be added to table" -ForegroundColor red
			If ($daystoexpire -lt 0)
				#0x2 = Password has been expired
				$daystoexpire = "Password is Expired"
			$obj = [PSCustomObject]@{
				'Name' = $
				'DaysUntil' = $daystoexpire
				'EmailAddress' = $_.emailaddress
				'LastSet' = $_.PasswordLastSet.ToShortDateString()
				'LockedOut' = $_.LockedOut
				'UPN'  = $_.UserPrincipalName
				'Enabled' = $_.Enabled
				'PasswordNeverExpires' = $_.PasswordNeverExpires
			write-host "$($_.Name)'s account is compliant" -ForegroundColor Green

#Sort the table so the Teams message shows expiring soonest to latest
$PWExpiringTable = $PWExpiringTable | sort-Object DaysUntil

$PWExpiringTable | ForEach-Object{
	If ($_.DaysUntil -eq "Password is Expired")
		write-host "$($ is expired" -ForegroundColor DarkRed
		$SectionExpired = @{
			activityTitle = "$($_.Name)"
			activitySubtitle = "$($_.EmailAddress)"
			activityText  = "$($_.Name)'s password has already expired!"
			activityImage = $ItemImage
		write-host "$($ is expiring" -ForegroundColor DarkYellow
		$Section = @{
			activityTitle = "$($_.Name)"
			activitySubtitle = "$($_.EmailAddress)"
			activityText  = "$($_.Name) needs to change their password in $($_.DaysUntil) days"
			activityImage = $ItemImage

Write-Host "Expired Accounts: $($($ArrayTableExpired).count)" -ForegroundColor Yellow
write-Host "Expiring Accounts: $($($ArrayTable).count)" -ForegroundColor Yellow

$body = ConvertTo-Json -Depth 8 @{
	title = 'Users With Password Expiring - Notification'
	text  = "There are $($ArrayTable.Count) users that have passwords expiring in $($LessThan) days or less"
	sections = $ArrayTable
Write-Host "Sending expiring users notification" -ForegroundColor Green
Invoke-RestMethod -uri $uri -Method Post -body $body -ContentType 'application/json'

$body2 = ConvertTo-Json -Depth 8 @{
	title = 'Users With Password Expired - Notification'
	text  = "There are $($ArrayTableExpired.Count) users that have passwords that have expired already"
	sections = $ArrayTableExpired
Write-Host "Sending expired users notification" -ForegroundColor Green
Invoke-RestMethod -uri $uri -Method Post -body $body2 -ContentType 'application/json'

Frequently Asked Questions

Q: What if I have a lot of users will I just get a giant Teams Message

A: No, it will cap the message which is currently set to a depth of 8

$body = ConvertTo-Json -Depth 8 @{
Since there are a lot of users, you can expand the message by click “See More” at the bottom


Q: What if I have a user or users who have passwords that have already expired?

A: It will display that the password is already expired instead of a number




8 thoughts on “Post Users with Expiring Passwords as Microsoft Teams Message with PowerShell

  1. This is very useful, and I’ve been testing it in my teams environment.

    Is there anyway to attach a complete list to the post in teams? It seems to only add the first 11, when I have 24 in my report.

    1. Let me look into this, I do see it being cut around 11obj. Side note, I am surprised its even allowing 24 objects to be sent. If the message is too large you will see an error. This is on the Teams end

      1. Brad, did you ever find a solution to the limited number of items shown? Our passwords expire in waves and it is not uncommon for us to see upwards of 40 on a given day

          1. Hey Brad, any news on this? I have increased the Depth to 20 but still only receive the first 10. Got any fix? Or as mentioned above, a way to attach the log?

          2. there is a size limit to the webhook. at this time it cannot be changed. youll have to modify the size of the message

  2. Nice idea.

    One suggestion to allow it to scale would be to simply have the power shell add/update this information in an Excel file stored within the Files area of your MS Team, then the notification message could just serve as a heads up and provide a handy link to that file. Could also add that file as a tab within Teams channel as well.

Leave a Reply

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