PowerShell – Locate Inactive Users in Active Directory / Exchange On-Prem

Another short article with little context today.

The PowerShell script below will produce a report showing the Last Logon time of all enabled Active Directory users, it will also connect to Exchange On-Premise and request the last logon date of the mailbox. This can be useful to identify user accounts which are no longer in active use, even in organisations where mailboxes may have been retained once a user left.

There’s also a nifty status update built in that will tell you exactly how far the script has progressed and show an estimated time to completion.

The Script

# Locate all users that have not logged in within 12 months
$InactiveMonths = 12
$ExchangePowerShellEndpoint = "https://*Server*/PowerShell/"
$ExchangeAuthenticationModel = "Kerberos"
$UserCredential = Get-Credential -Message "Enter your credentials used to log into the Exchange Admin Portal"

$Months = (Get-Date).Addmonths(-($InactiveMonths))
$Users = Get-ADUser -Filter {LastLogonTimeStamp -lt $Months -and enabled -eq $true} -Properties LastLogonTimeStamp, mail |
select-object Name,@{Name="LastLogon"; Expression={[DateTime]::FromFileTime($_.lastLogonTimestamp).ToString('dd-MM-yyyy')}},SamAccountName,UserPrincipalName, mail

Write-Host "Located $($Users.count) users"

# Connect to Exchange
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangePowerShellEndpoint -Authentication $ExchangeAuthenticationModel -Credential $UserCredential
Import-PSSession $Session -DisableNameChecking -AllowClobber

# For each user, get the last mailbox login time
$currentIndex = 1
$startTime = Get-Date
foreach ($user in $users) {

    # Estimate time to completion
    $estimation = " "
    $now = Get-Date
    if ($currentIndex -gt 0) {
        $elapsed = $now - $startTime # how much time has been spent
        $average = $elapsed.TotalSeconds / $currentIndex # how many seconds per site
        $totalSecondsToGo = ($users.Count - $currentIndex) * $average # seconds left
        $span = New-TimeSpan -Seconds $totalSecondsToGo # time left
        $estimatedCompletion = $now + $span # when it will be complete
        $estimation = $estimatedCompletion.ToString() # readable estimation

    # Show progress and estimated completion
    Write-Progress -Id 0 -Activity "Retrieving Exchange Mailbox Stats" -Status "$currentIndex of $($Users.Count), Est Completion - $($estimation)" -PercentComplete (($currentIndex / $Users.Count) * 100)

    # Get the mailboxes last logon time
    try {
        $mailboxStats = Get-MailboxStatistics -Identity $user.mail -ErrorAction Stop
        $lastLogon = $mailboxStats.LastLogonTime
        if($lastLogon -ne $null) {
            $lastLogon = (Get-Date $lastLogon).ToShortDateString()
            $user | Add-Member -Name 'mailboxLastAccess' -Type NoteProperty -Value $lastLogon
    } catch {
        $user | Add-Member -Name 'mailboxLastAccess' -Type NoteProperty -Value $null
Write-Progress -Id 0 -Activity " " -Status " " -Completed

# Export to CSV
$Users | export-csv C:\temp\LastLogOn_Users.csv -notypeinformation -noclobber

One thought on “PowerShell – Locate Inactive Users in Active Directory / Exchange On-Prem

  1. Hello, thank you very much for your script it save my life. But I have a little problem.

    if a select 26 months for example I have 250 results and in the csv file I have all informations.

    But if I select 24 months I have 900 results and in the csv file I dont have last exchange login column exported.

    Like if too many results is not supported by the export function.

    Have u got a idea about this?

    PS: sorry english is not my native language


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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