Microsoft 365: Password Expiration Notification Email

Password expiration notifications play a crucial role in maintaining the security of your organization’s Microsoft 365 accounts. Ensuring users are aware of upcoming password expirations can help prevent account lockouts and encourage users to create strong, unique passwords. In this guide, we will demonstrate how to create a Microsoft Graph PowerShell script that automatically sends email notifications to users when their Office 365 passwords are nearing expiration.

What is a Password Expiry Notification Email?

A password expiry notification email is a message that is sent to a user when their password is about to expire. The email typically includes information about the password expiration date and instructions on how to change the password. Password expiry notification emails are an essential tool for organizations that want to ensure that their users change their passwords regularly and keep their accounts secure.

How to Send Password Expiration Notification Emails in Microsoft 365?

As the Password expiration notifications are no longer supported in the Microsoft 365 admin center (Maybe because Microsoft recommends disabling password expiration!), we must implement a Windows PowerShell script-based solution. To create a Microsoft Graph PowerShell script for Office 365 password expiration notifications, follow these steps:

Pre-requisites: Install the Microsoft Graph PowerShell Module. You need the Microsoft Graph PowerShell module installed on your computer. Use: Install-Module Microsoft.Graph.

Use this PowerShell script to send out an Email reminder to all users whose password is expiring:

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "User.Read.All", "Mail.Send"

# Set the notification threshold - days before password expires
$NotificationThreshold = 7
$PasswordExpiryThreshold = 90 #By default 90 days Password expiry

# Get all users
$AllUsers = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName,Mail,UserType, AccountEnabled,PasswordPolicies,lastPasswordChangeDateTime

# Iterate through each user
ForEach ($User in $AllUsers) 
{
    # Skip disabled accounts and users with Password never Expire flag
    If (!$User.AccountEnabled -or $User.PasswordPolicies -contains "xDisablePasswordExpiration" -or $User.userType -eq "Guest") {
        continue
    }

    # Get user's password Expiry Date
    $PasswordExpiryDate = $User.lastPasswordChangeDateTime.AddDays($PasswordExpiryThreshold)

    # Calculate the remaining days
    $RemainingDays = ($PasswordExpiryDate - (Get-Date)).Days

    # Check if the remaining days are within the notification threshold
    If ($RemainingDays -le $NotificationThreshold) {
        # Send an email notification to the user
        $EmailBody = "
            Hello $($User.DisplayName),
            <br/><br/>
            Your Office 365 password will expire in $remainingDays days. Please change your password before it expires to avoid any disruption in accessing your account.
            <br/><br/>
            To change your password, follow these steps:<br/>
            <ol>
            <li>Sign in to Office 365 (https://www.office.com)</li>
            <li>Click on your profile picture in the top right corner.</li>
            <li>Select 'View account'.</li>
            <li> Click 'Password'.</li>
            <li> Follow the instructions to change your password.</li>
            </ol>
            <br/>
            Thank you,<br/>
            IT Support Team
        "

    $MailParams = @{
	    Message = @{
		    Subject = "Your Office 365 password will expire soon"
            Importance = "High"
		    Body = @{
			    ContentType = "html"
			    Content = $EmailBody
		    }
		    ToRecipients = @(
			    @{
				    EmailAddress = @{
					    Address = $User.Mail
				    }
                }
		    )
	    }
    }
    # Send the email using Microsoft Graph
    Send-MgUserMail -UserId $User.Mail -BodyParameter $MailParams
    }
}

This script will prompt for login credentials and iterate through all users in your organization, calculate the password expiration date, and send an email notification to users whose passwords will expire within the specified threshold (7 days, in our case). You can modify the $NotificationThreshold variable to adjust the number of days before the password expires when notifications are sent.

Here is the script in action:

office 365 password expiration notification

Automate the Password Expiry Reminder Emails

For an unattended automated scenario, follow these steps:

Step 1: Create an App ID and Grant Permissions

Create an app ID in Azure AD to connect with Microsoft Graph: How to Connect to the Microsoft Graph PowerShell module? Make sure you grant “User.Read.All” and “Mail.Send” permissions to the App ID.

Step 2: Update the Parameters in this PowerShell Script

Once ready with the App ID, update this PowerShell script’s parameters section. Here is how this script works:

  • Connects to Microsoft Graph using the given App ID and certificate thumbprint
  • Checks the password expiration date of all users – Excludes users with Password never expires, password already expired, guest users, etc.
  • Sends an Email to users whose password expires within the given threshold (15 days as in the script!) from a given Email ID (Make sure you have the Email created already. E.g., In our case, it’s: $SenderID = “Helpdesk@Crescent.com”).
  • Logs the message to a log file.
#Parameters
$TenantID = "c1c2b123-4240-4775-a19a-6351b30cx2f6"
$ClientID = "3c1541f7-9565-4abe-b398-f47ac91c2c06" #App ID
$CertThumbPrint = "63FDDD207DE01C0EB34C35647D086D3C1520FA7"
#Path for Log File
$LogFilePath = "C:\Scripts\PasswordExpiry.Log"
$SenderID = "Helpdesk@Crescent.com"
#Notification threshold - days before password expires
$NotificationThreshold = 15
$PasswordExpiryThreshold = 90 #By default 90 days Password expiry

#Function to Add Content to a Log File
Function Write-Log {
 
  [CmdletBinding()]
   
  Param ([Parameter(Mandatory=$true)][string]$Message)  

  Process{
        #Add Message to Log File with timestamp
        "$([datetime]::Now) : $Message" | Out-File -FilePath $LogFilePath -append;
   
        #Write the log message to the screen
        Write-host $([datetime]::Now) $Message
    }
}

#Connect to Microsoft Graph using App
Connect-MgGraph -ClientID $ClientID -TenantId $TenantID -CertificateThumbprint $CertThumbPrint

#Ensure the Parent Folder for Log File
$FolderPath= Split-Path $LogFilePath
If(!(Test-Path -path $FolderPath)) {  
    New-Item -ItemType directory -Path $FolderPath | Out-Null
}

# Get all users
$AllUsers = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName,Mail,UserType, AccountEnabled,PasswordPolicies,lastPasswordChangeDateTime

# Iterate through each user
ForEach ($User in $AllUsers) 
{
    # Skip disabled accounts and users with Password never Expire flag
    If (!$User.AccountEnabled -or $User.PasswordPolicies -contains "DisablePasswordExpiration" -or $User.userType -eq "Guest") {
        continue
    }

    # Get user's password Expiry Date
    $PasswordExpiryDate = $User.lastPasswordChangeDateTime.AddDays($PasswordExpiryThreshold)

    # Calculate the remaining days
    $RemainingDays = ($PasswordExpiryDate - (Get-Date)).Days

    # Check if the remaining days are within the notification threshold
    If ($RemainingDays -le $NotificationThreshold -and $RemainingDays -ge 0) {
        # Send an email notification to the user
        $EmailBody = "
            Hello $($User.DisplayName),
            <br/><br/>
            Your Office 365 password will expire in $remainingDays days. Please change your password before it expires to avoid any disruption in accessing your account.
            <br/><br/>
            To change your password, follow these steps:<br/>
            <ol>
            <li>Sign in to Office 365 (https://www.office.com)</li>
            <li>Click on your profile picture in the top right corner.</li>
            <li>Select 'View account'.</li>
            <li> Click 'Password'.</li>
            <li> Follow the instructions to change your password.</li>
            </ol>
            <br/>
            Thank you,<br/>
            IT Support Team
        "

    $MailParams = @{
	    Message = @{
		    Subject = "Your Office 365 password will expire soon"
            Importance = "High"
		    Body = @{
			    ContentType = "html"
			    Content = $EmailBody
		    }
		    ToRecipients = @(
			    @{
				    EmailAddress = @{
					    Address = $User.Mail
				    }
                }
		    )
	    }
    }
    # Send the email using Microsoft Graph
    Send-MgUserMail -UserId $SenderID -BodyParameter $MailParams
    
    #Write to Log File
    Write-Log "Password Expiration Notification sent to user $($User.Mail) - Password Expires on $PasswordExpiryDate"
    }
}

This script could be a huge productivity saver, Since without proper notifications, users may be caught off-guard when their passwords expire, leading to account lockouts and disruptions in productivity. These emails will notify users that their password is about to expire and should change it to avoid any disruption.

Step 3: Schedule the PowerShell Script

You can create an automated system for sending password expiration notification emails to your Office 365 users: Use Schedule the PowerShell Script using Windows Tasks Scheduler or Automate the PowerShell Execution with Azure Runbook.

By following our step-by-step instructions, you will be able to help users stay informed about their password expiration dates and maintain secure access to their Office 365 accounts.

Best Practices for Password Expiry Notification Emails

Here are some best practices that organizations should follow when implementing password expiry notification emails:

  1. Ensure that users receive notification emails well in advance of their password expiration date. This will give them enough time to change their password without disrupting their work.
  2. Provide clear instructions on how to change passwords. Include links to resources that users can use to change their passwords.
  3. Encourage users to create strong, complex passwords that are difficult to guess or crack.
  4. Consider implementing multi-factor authentication to add an extra layer of security to user accounts.
  5. Regularly review your password policy to ensure that it is up to date and aligned with industry best practices.

Conclusion:

With the Microsoft Graph PowerShell script for Office 365 password expiration notifications, you can automate the process of reminding users to update their passwords before they expire. This proactive approach helps users stay informed about their password expiration dates, reduces the risk of account lockouts, and promotes a secure environment for your organization. Implementing this Microsoft Graph PowerShell solution can enhance your organization’s security and maintain seamless access to Office 365 resources while promoting a secure environment for all users.

How to set the Office 365 account password to never expire?

To set the password to never expire for Office 365 user accounts, You can use the Azure Active Directory PowerShell or Microsoft Graph API PowerShell cmdlets. You can also disable the password expiration for your organization through the password expiration policy in Office 365 Admin Center (Settings >> then click “Org settings” >> “Security & Privacy” >> Select Password Expiration Policy >> Check the box next to “Set user passwords to never expire”).
More info: Disable password expiration in Office 365

Does Office 365 send password expiration emails?

Password expiration notifications are no longer supported in Microsoft 365! So, you must setup your workaround to send users Password Expiration Emails.

What is the default password expiration time in Office 365?

90 days from the last password change date (It’s disabled by default). However, Global Admin can configure the password expiry duration to meet the organization’s security requirements through the Office 365 Admin Center or PowerShell.

What happens when the O365 password expires?

When an Office 365 current password expires, the user will be prompted to change it the next time they sign in. If the user ignores the prompt after the actual expiration date and does not change their password, they will be locked out of their account until they reset their password (and the technical support at admin level can unlock it). However, Microsoft Office applications like Outlook may continue to work until the password expires in the cache.

Is password expiration no longer recommended?

Yes! Password expiration is no longer recommended as a security measure, as it does more harm than good. Changing passwords every 90 days could leave exposed weak/old passwords usable by a hacker/scammers/cybercriminals for up to three months, and it is far better to require strong passwords and use multi-factor authentication.

Salaudeen Rajack

Salaudeen Rajack - Information Technology Expert with Two-decades of hands-on experience, specializing in SharePoint, PowerShell, Microsoft 365, and related products. He has held various positions including SharePoint Architect, Administrator, Developer and consultant, has helped many organizations to implement and optimize SharePoint solutions. Known for his deep technical expertise, He's passionate about sharing the knowledge and insights to help others, through the real-world articles!

27 thoughts on “Microsoft 365: Password Expiration Notification Email

  • Would this script send emails to the “alternate email” for those accounts that do not have email enabled in the tenant but have an alternate email defined?

    Reply
  • how to get the $CertThumbPrint value?

    Reply
    • Do this:

      • Open the Certificate Manager console in your computer (Start >> Run >> certmgr.msc).
      • Find a certificate by navigating to the relevant folder, such as “Personal”. Double-click the certificate.
      • In the Certificate dialog box, click the Details tab. You’ll find “Thumbprint” at the bottom.
      Reply
  • This works perfect, thank you! Do you have a Graph API code example for how to insert an image into this email? This will help me with branding it as authentic email for our user base.

    Reply
  • Hello, thank you for posting this. Can this be tested with one user instead of -All or -Top 10? I’d like to tweak this and do the testing with just my account.

    Reply
      • Thank you very much! This works and I have tested/customized it to fit our organization’s needs.

        Reply
  • hi how do I send it to one user? I tried “Get-mguser -user upn but no email was sent

    Reply
  • i tried but i get below error. anyone can help?

    The ‘Connect-MgGraph’ command was found in the module ‘Microsoft.Graph.Authentication’, but the
    module could not be loaded.

    Reply
  • Hi Sir,
    Thank you for your topic. I run and tested that is ok. I have a issue with unicode UTF-8(I come from VietNam). Can you help me ? How can encoding with send email via Send-MgUserMail function.
    Thank you/

    Reply
  • I really want to implement this, I have testet the script locally using the APP ID method, it works just fine.
    Now I want to automate this and have created an automation runbook and uploadet the powershell script.
    On first run i failed with an error something like: the thumbprint does not exist or is missing.
    I then added my self signed cert created in the guide to the runbook under certificates.
    Now it fails with the error: ClientCertificateCredential authentication failed: The certificate certificate does not have a private key.
    How do I resolve this?

    Thanks in advance.

    Reply
  • Hi,

    When I’m trying to run the below command, I’m getting an error message on powershell. Can someone help please?
    PS C:\Windows\system32> # Get all users
    $AllUsers = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName,Mail,UserType, AccountEnabled,PasswordPolicies,lastPasswordChangeDateTime

    Unable to find type [Microsoft.WindowsAzure.Commands.Utilities.Common.AzurePSCmdlet].
    At C:\Program Files\WindowsPowerShell\Modules\Microsoft.Graph.Users\2.6.0\exports\ProxyCmdletDefinitions.ps1:16320 char:52
    + … ommand -and [Microsoft.WindowsAzure.Commands.Utilities.Common.AzurePS …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Microsoft.Windo…n.AzurePSCmdlet:TypeName) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : TypeNotFound

    Reply
  • Hi,
    I follow the guide and setup Automate the Password Expiry Reminder Emails but when ran the Powershell script and nothing happen, only show “Welcome To Microsoft Graph!”
    No log written and no notification email

    Reply
    • Well, that simply means: either the password expiration is disabled or no passwords expires on the given time frame.

      Reply
      • Hi,
        May I know if possible to extract the user password expiration from Active Directory instead of o365 ?

        Reply
  • This is great, and I tested it on my account with “Get-MgUser -UserID “myUPN” …..
    But the email content looks lame and many users will think it’s phishing. How can I improve the email content to include the company logo or picture?

    Reply
    • Instead of links in the Email, You can add something like; “Press Alt+Ctrl+Del to change your Password!”. Add/Embed Images to the Email to make it authentic.

      Reply
  • When I try to automate this, I get the following error in Automation Runbook when testing it out:

    System.Management.Automation.CommandNotFoundException: The term ‘Connect-MgGraph’… etc.

    I’ve installed the Msgraph module in the module section of the run book. What am I missing/doing wrong?

    Reply
  • Hello and thank you for the blog post. I am running into the following error when running this script

    Send-MgUserMail : Access is denied. Check credentials and try again.
    At line:55 char:5
    + Send-MgUserMail -UserId $User.Mail -BodyParameter $MailParams
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: ({ UserId = jcon…ionJsonSchema }:f__AnonymousType8`2) [Send-MgUserMail_Send1], RestException`1
    + FullyQualifiedErrorId : ErrorAccessDenied,Microsoft.Graph.PowerShell.Cmdlets.SendMgUserMail_Send1
    Send-MgUserMail : Access is denied. Check credentials and try again.
    At line:55 char:5
    + Send-MgUserMail -UserId $User.Mail -BodyParameter $MailParams
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: ({ UserId = jcou…ionJsonSchema }:f__AnonymousType8`2) [Send-MgUserMail_Send1], RestException`1
    + FullyQualifiedErrorId : ErrorAccessDenied,Microsoft.Graph.PowerShell.Cmdlets.SendMgUserMail_Send1

    Reply
    • Could you ensure you have granted “User.Read.All” and “Mail.Send” permissions to the App ID in Azure ad?

      Reply
  • Hi, thank for the script..
    I notice also that the local log add only the last user to the file, if more than one user password are expired, only the last user is written into the file log. Can we change the script to add all users expired to the logs? Can we also create a file everyday instead of rewrite the same?
    Thanks

    Reply
  • Thank you, how can I test this with few users?

    Reply

Leave a Reply

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