Post Users with Expiring Passwords as Microsoft Teams Message with PowerShell
Table of Contents
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.
- 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.
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.
- Download or copy the script here
- Put in the URL for your webhook that you save earlier, as the value for the variable, “$uri”
- 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.
- 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.
- Once you have made it fit your organizations needs, run the PowerShell script.
- In my example I ran it in ISE. At the bottom I can see it ran without any issues
- Back in Teams I can see my two users that need their passwords changed.
Configure Job as Scheduled Task
- In my environment I saved the script at C:\Automation
- In Task Scheduler I am going to create a basic task
- In the program/script, enter “Powershell -file “FILE LOCATION AND NAME.ps1″”
- 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 $uri = "[INSERT WEBHOOK URI]" $ItemImage = 'https://img.icons8.com/color/1600/circled-user-male-skin-type-1-2.png' $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" } else { #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' = $_.name 'DaysUntil' = $daystoexpire 'EmailAddress' = $_.emailaddress 'LastSet' = $_.PasswordLastSet.ToShortDateString() 'LockedOut' = $_.LockedOut 'UPN' = $_.UserPrincipalName 'Enabled' = $_.Enabled 'PasswordNeverExpires' = $_.PasswordNeverExpires } $PWExpiringTable.Add($obj) } Else { 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 "$($_.name) is expired" -ForegroundColor DarkRed $SectionExpired = @{ activityTitle = "$($_.Name)" activitySubtitle = "$($_.EmailAddress)" activityText = "$($_.Name)'s password has already expired!" activityImage = $ItemImage } $ArrayTableExpired.add($SectionExpired) } Else { write-host "$($_.name) is expiring" -ForegroundColor DarkYellow $Section = @{ activityTitle = "$($_.Name)" activitySubtitle = "$($_.EmailAddress)" activityText = "$($_.Name) needs to change their password in $($_.DaysUntil) days" activityImage = $ItemImage } $ArrayTable.add($Section) } } 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 @{
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
Sources
- https://blogs.technet.microsoft.com/privatecloud/2016/11/02/post-notifications-to-microsoft-teams-using-powershell/
My name is Bradley Wyatt; I am a 5x Microsoft Most Valuable Professional (MVP) in Microsoft Azure and Microsoft 365. I have given talks at many different conferences, user groups, and companies throughout the United States, ranging from PowerShell to DevOps Security best practices, and I am the 2022 North American Outstanding Contribution to the Microsoft Community winner.
9 thoughts on “Post Users with Expiring Passwords as Microsoft Teams Message with PowerShell”
Very nice article appreciate you taking the time to go through this step by Step Brad!!
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.
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
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
not yet, believe there is a github bug request on it
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?
there is a size limit to the webhook. at this time it cannot be changed. youll have to modify the size of the message
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.
Hello,
How would you modify the script to pull the expiration date from a specific OU instead of the whole of AD?