We use expiry dates on Active Directory accounts for contractors so they switch off automatically when a project ends. Two problems: a contractor whose extension was approved got locked out because their account expired at midnight, and separately our security team wants a full list of every account that has already expired but is still sitting in AD. How do I extend or reset an expired account, and how do I pull a list of all expired accounts?
Expired User Accounts in Active Directory: How to Find and Extend Them
Worth being precise about terms first, because AD has three different "off" states people mix up. An expired account is one whose accountExpires date has passed, it stops allowing sign-in automatically on that date, which is exactly what you want for contractors. That is different from a disabled account (switched off manually) and a locked out account (auto-locked from bad passwords). It is also different from an expired password, the account is fine, the user just needs to set a new password. Your contractor hit account expiration, so the fix is to move or clear the expiry date.
One handy detail: in the GUI "Account expires" means the end of the date you pick, so the account stays active for that entire day and switches off at midnight after it.
To get the contractor back in, set a new expiration date with Set-ADAccountExpiration:
Set-ADAccountExpiration -Identity jsmith -DateTime "12/31/2026"
Use the universal format YYYY-MM-DD or match your system's regional date format, a mismatched format is the most common error with this cmdlet. If the extension is permanent, or you expired an account by mistake, clear the date entirely so it never expires:
Clear-ADAccountExpiration -Identity jsmith
You can also do it in Active Directory Users and Computers under the user's Properties, Account tab, "Account expires" section. Note you cannot edit the accountExpires attribute directly in the Attribute Editor, it is protected, use the Account tab or the cmdlet.
For the security team's list, Search-ADAccount has a built-in switch for exactly this:
Search-ADAccount -AccountExpired -UsersOnly | Select-Object Name,SamAccountName,AccountExpirationDate,Enabled
That returns every user account whose expiry date has already passed. Pipe it to Export-Csv to hand it over:
Search-ADAccount -AccountExpired -UsersOnly | Select-Object Name,SamAccountName,AccountExpirationDate | Export-Csv "C:\Reports\ExpiredAccounts.csv" -NoTypeInformation
Even better, get ahead of the problem by finding accounts about to expire so no one gets caught out at midnight again. Use AccountExpiring with a TimeSpan, for example the next 10 days:
Search-ADAccount -AccountExpiring -TimeSpan 10.00:00:00 -UsersOnly | Select-Object Name,SamAccountName,AccountExpirationDate
Run that on a schedule and you get an early-warning list, so approved extensions are applied before the account cuts off rather than after the person is already locked out.
To see the expiry date on specific accounts (not just the expired ones), Get-ADUser exposes AccountExpirationDate:
Get-ADUser -Filter * -Properties AccountExpirationDate | Where-Object {$_.AccountExpirationDate -ne $null} | Select-Object SamAccountName,AccountExpirationDate
That lists everyone who has any expiry set, which is useful for auditing your contractor accounts in one view and spotting dates that were fat-fingered.
Two important cautions from the security angle. First, an expired-but-still-present account is a real risk, because anyone with rights can simply run Set-ADAccountExpiration or Clear-ADAccountExpiration and revive it as a backdoor. So for genuinely finished contractors, disable or remove the account rather than leaving it expired, expiry stops login but the object lingers. Second, and this catches hybrid environments out: the accountExpires attribute does not sync to Microsoft Entra ID. An account that has expired on-premises can still be enabled in the cloud, so a contractor could lose their PC login but keep Microsoft 365 access. In hybrid setups, pair the expiry with disabling the account, since the disabled state does sync up to the cloud.