How to Create a Password Expiration Report

But Will a Password Expiration Report be Obsolete Soon?

The advice not to force users to change passwords regularly comes from both Microsoft and independent security agencies. Forcing people to change passwords creates friction for people without delivering better security. The consensus is that better security is attained by moving away from passwords to protect accounts with stronger authentication methods like multifactor authentication or passkeys. Evidence of progress in this direction is Microsoft’s recent announcement of support in Entra ID for device-bound passkeys based on the Authenticator app.

The direction of travel seems clear, but progress is slow. The percentage of Entra ID connections using multifactor authentication reached 38% in early 2024. It takes time to change, which is why I still receive requests for how to create a report showing when Entra ID accounts last updated passwords and details of when the next password change is scheduled.

Setting the Password Expiration Policy

My tenant doesn’t force password changes. The password expiration policy for the tenant is set to never expire. This is easily done through the Org settings section of Microsoft 365 admin center (Figure 1).

Setting the password expiration policy for a Microsoft 365 tenant.
Figure 1: Setting the password expiration policy for a Microsoft 365 tenant

The accounts in the tenant are not a great test case for reporting password changes. I’m more concerned about how to report the multifactor authentication status for accounts. With that thought in mind, let’s examine how to approach creating a report with PowerShell.

Steps to Create a Password Expiration Report

Generating a password expiration report is straightforward. In this discussion, I used the Microsoft Graph PowerShell SDK to create a script to:

  • Connect to the Graph endpoint by running the Connect-MgGraph cmdlet. Three permissions are needed (If you wish, Directory.Read.All is a higher privileged permission that can be used instead of the first three permissions).
    • Domain.Read.All to read the domain information.
    • User.Read.All to read account information.
    • Organization.Read.All to read information about the tenant (fetch the display name).
    • AuditLog.Read.All to read the sign-in activity information for user accounts.
  • Find the password expiration policy for the tenant. This can be done by using the Get-MgDomain cmdlet to fetch details of the default domain and retrieving the password validity period from it. If the value is 2147483647, the tenant does not expire passwords. Date calculations won’t work with 2147483647, so the script adjusts the value to 20000 to calculate a notional password expiration date.
  • Find the set of licensed member accounts in the tenant. It’s important to use a server-side filter here to maximize performance. Running a command like Get-MgUser -All fetches all the known accounts in a tenant, but a client-side filter will be necessary to remove guest accounts and unlicensed member accounts such as those used for room and shared mailboxes. Master the art of filtering to make sure that scripts that work with Entra ID accounts perform well. I’ll cover filtering in some depth during my Microsoft Graph PowerShell SDK session at the M365 Conference in Orlando.
  • For each account, retrieve details like the date and time of the last password change, the password profile for the account, and to compute a date when the password should be renewed. In tenants that don’t force password renewal, this date will be somewhere long after you retire.
  • Generate a report.

A good case exists for using the beta version of the Get-MgUser cmdlet in the script. Apart from fetching a wider set of properties by default, the Get-MgBetaUser cmdlet returns an additional timestamp for the last successful interactive sign-in (which might be different than the last sign-in).

Figure 2 shows a sample password expiration report generated by the script. In this case, the tenant password expiration policy sets password to never expire, so the reported expiration dates are years into the future and no warnings about impending expiration appear in the status column.

An example of a password expiration report for a Microsoft 365 tenant.
Figure 2: An example of a password expiration report for a Microsoft 365 tenant

You can download the script from GitHub. Remember, the code is intended to illustrate a principle. Use it as you see fit.

Onward to a Passwordless Future

I don’t think there is any doubt but that the time will come when passwords disappear, and we will use more phishing-resistant technologies to prove our identities and sign into applications. Until then, perhaps some will want to report password expiration, and now you have a script to do the job.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work. The PowerShell chapter includes hundreds of examples of using the Microsoft Graph PowerShell SDK.

3 Replies to “How to Create a Password Expiration Report”

  1. Hi Tony,

    Getting below error.

    Get-MgDomain : One or more errors occurred.
    At line:1 char:1
    + Get-MgDomain
    + ~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-MgDomain_List], AggregateException
    + FullyQualifiedErrorId : System.AggregateException,Microsoft.Graph.PowerShell.Cmdlets.GetMgDomain_List

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.