AD Account Expiration Review: Decide Before the Deadline
An AD account expiration review turns upcoming access end dates into decisions that someone owns. An expiring account may belong to a contractor whose engagement is ending, an employee with temporary access, or a user identity used by an application. The immediate question is whether the planned end date is still correct. Extending every account that appears on a report defeats the purpose of assigning an end date.
A review needs the expiration instant, the accountable owner, the continuing business need and the dependencies that must be retired or transferred. It should cover all relevant user populations. Privileged membership affects the urgency and the rights to remove, but it is not a prerequisite for reviewing an upcoming deadline.
An account approaching expiration is not evidence of compromise. Account expiration is also separate from password expiration: these are different account properties and different operational problems. This guide proposes a review workflow; it does not report results from a live domain or prescribe a universal renewal policy.
How accountExpires Represents an End Date
The AD attribute accountExpires stores Windows FILETIME: intervals of 100 nanoseconds counted from January 1, 1601 UTC. Microsoft defines two values for an account that never expires: 0 and 9223372036854775807, also written 0x7FFFFFFFFFFFFFFF. Neither value is a future deadline to put on an expiration calendar. Microsoft Learn: Account-Expires attribute
Keep the stored instant distinct from a business instruction such as "access ends Friday." Ask whether the owner means the start of that day, the end of a shift, or a particular UTC instant. Record the agreed timezone and exact time before making a change. A date without that context can end access earlier than the owner intended.
There is an acknowledged overlap with our article on disabled, expired and locked accounts remaining in privileged groups. That article covers accounts already in an invalid state while privileged membership persists. This review acts before expiration, across all user populations, to assign the decision and coordinate the access changes. If the account is already expired, put it in the existing offboarding and membership review rather than treating it as an upcoming deadline.
The Kerberos ticket lifetime and connection containment guide explains why account state, issued tickets and authenticated connections require separate evidence. Use that distinction when planning an access end date. An ordinary expiration review is not an incident finding and does not justify assuming ticket theft.
Detection: Build an Expiration Inventory
1. Set the scope and use an explicit horizon
Start with the domain, directory server, search base and reference time. Record the review horizon as a local operational choice. The examples below require the ActiveDirectory PowerShell module and an authorized directory context. They are illustrative commands adapted from documentation, not commands executed against an AD environment. Replace every example server, DN and identity with verified values.
Microsoft documents Search-ADAccount -AccountExpiring with either -TimeSpan for a period from the current time or -DateTime for a deadline. -UsersOnly narrows the object population to users. The following example chooses a thirty-day reporting horizon; this choice is not a Microsoft requirement. Microsoft Learn: Search-ADAccount
$server = 'dc01.example.com'
$searchBase = 'OU=People,DC=example,DC=com'
Search-ADAccount -AccountExpiring -UsersOnly -TimeSpan '30.00:00:00' -Server $server -SearchBase $searchBase |
Select-Object DistinguishedName, Name, Enabled, AccountExpirationDate
For a scheduled review, an absolute deadline can be easier to reproduce. This separate example constructs a UTC DateTime rather than relying on a locale-dependent date string:
$deadlineUtc = [datetime]::new(2026, 11, 3, 18, 0, 0, [DateTimeKind]::Utc)
Search-ADAccount -AccountExpiring -UsersOnly -DateTime $deadlineUtc -Server $server -SearchBase $searchBase |
Select-Object DistinguishedName, Name, Enabled, AccountExpirationDate
Use one horizon form per query. Preserve the query time as well as its deadline: a relative query run later has a different window. These cmdlet searches are investigation inputs, not a claimed reproduction of EtcSec's strict boundary comparisons. Inspect the returned dates and enabled state before assigning action.
2. Read the raw attribute and normalize dates
Retrieve accountExpires explicitly with Get-ADUser -Properties. Microsoft documents additional LDAP attributes through this parameter, along with -SearchBase and -Server for selecting the directory context. Microsoft Learn: Get-ADUser
This illustrative inventory keeps the raw value and reports conversion status. It deliberately distinguishes an unavailable or invalid value from a documented never-expiring sentinel:
Get-ADUser -Filter * -Server $server -SearchBase $searchBase -Properties accountExpires,manager,description |
ForEach-Object {
$raw = $_.accountExpires
$expiryUtc = $null
$state = 'Missing or invalid'
if ($null -ne $raw -and [string]$raw -ne '') {
try {
$value = [long]$raw
if ($value -eq 0 -or $value -eq [long]::MaxValue) {
$state = 'Never expires'
} else {
$expiryUtc = [datetime]::FromFileTimeUtc($value)
$state = 'Dated'
}
} catch {
$state = 'Missing or invalid'
}
}
[pscustomobject]@{
DistinguishedName = $_.DistinguishedName
Enabled = $_.Enabled
RawAccountExpires = $raw
ExpirationUtc = $expiryUtc
DateState = $state
Manager = $_.Manager
Description = $_.Description
}
}
FromFileTimeUtc converts a file time to a UTC DateTime and rejects out-of-range input. The two never-expiring sentinels are handled before conversion. This example's error labels are proposed review labels, not output from the collector parser. Microsoft Learn: DateTime.FromFileTimeUtc
The directory's manager and description fields are leads for finding an owner, not proof that the named person authorized access. Confirm the accountable owner through the current contract, access request or service record. Leave unresolved ownership visible instead of using an empty field as permission to renew.
3. Turn dates into an actionable register
For each in-scope identity, preserve its stable identifier, raw attribute, normalized expiration, enabled state, review timestamp and directory source. Add the business end date, owner, dependencies, decision and change reference. Keep already expired accounts, never-expiring accounts and unreadable dates distinguishable from the upcoming-deadline queue.
| Review item | Evidence to obtain | Decision it supports |
|---|---|---|
| Expiration instant | Raw attribute plus UTC and agreed business timezone | Confirm the intended deadline |
| Business owner | Current contract or access request | Confirm who may authorize continuation |
| Application dependency | Service owner's inventory of jobs and integrations | Transfer or retire use before access ends |
| Access scope | Current rights and relevant memberships | Assign explicit removal tasks |
| Exception | Approved reason, bounded new date and review owner | Record a targeted extension |
An application dependency should become a task for its service owner. It should not silently convert a time-limited identity into a permanent one. Decide how work will continue, who owns the replacement or transition, and what evidence closes that task.
Remediation: Confirm the End Date or Authorize a Bounded Extension
1. Decide before editing the account
Confirm the reference date and population first, then send each deadline to its owner. Ask whether the original end date remains valid and whether the user still needs the same access. Record the answer with its approval source. A missing response is an unresolved review, not an authorization to extend.
When access should end as planned, coordinate rights removal, membership review and application handover with the responsible teams. When work continues, require a new bounded end date and an authorized reason. Separate these two outcomes in the register so that a bulk renewal cannot be mistaken for completed remediation.
2. Apply only the approved date
Microsoft documents Set-ADAccountExpiration for setting an exact expiration time. Its -DateTime parameter assumes local time unless otherwise specified; omitting the time means midnight local time. Use the exact approved instant and record it explicitly. Microsoft Learn: Set-ADAccountExpiration
The following illustrative targeted extension uses a placeholder identity and UTC deadline. Run the preview first in an authorized environment. Apply the separate write only after checking that the identity and date match the recorded approval. These commands have not been executed here.
$identity = 'CN=Example User,OU=People,DC=example,DC=com'
$approvedExpiryUtc = [datetime]::new(2026, 12, 1, 18, 0, 0, [DateTimeKind]::Utc)
Set-ADAccountExpiration -Identity $identity -DateTime $approvedExpiryUtc -Server $server -WhatIf
Set-ADAccountExpiration -Identity $identity -DateTime $approvedExpiryUtc -Server $server
Do not feed the entire expiration report into this write. A query result establishes a date condition; it supplies no approval to continue access. Likewise, clearing expiration is a separate policy decision, not a routine fix for an upcoming-expiration finding.
3. Re-read and retain the decision
Read the same account from the chosen server after the change. Preserve the old and new raw values, normalized dates, server, timestamp, approval and change reference:
Get-ADUser -Identity $identity -Server $server -Properties accountExpires,AccountExpirationDate |
Select-Object DistinguishedName, Enabled, accountExpires, AccountExpirationDate
Normalize the raw value with the inventory method and compare it with the approved UTC instant. This readback verifies the selected server's value; where the workflow depends on other domain controllers, arrange their verification through the directory team's process. A saved approval without a readback does not establish that the attribute changed as intended.
For planned departures, retain evidence that the assigned rights-removal and service-transition tasks completed. For extensions, schedule the next review around the new deadline. In both cases, distinguish the account attribute from tickets, connections and application access. The record should say what was verified and what remains with another owner.
How EtcSec Detects This
EtcSec's etc-collector implements ACCOUNT_EXPIRE_SOON in public/internal/audit/detectors/ad/accounts/status/account-expire-soon.go. Static inspection of Detect() shows the following logic; no customer audit was executed for this article:
- Take the reference time from
data.Now. - Calculate the upper boundary with
now.AddDate(0, 0, 30). - Skip any user whose
Disabledflag is true. - Skip any user whose parsed
AccountExpiresis the zero time. - Select only dates strictly after the reference time and strictly before the upper boundary.
The lower and upper bounds are both excluded. Already expired accounts are excluded, as is an expiration exactly equal to the audit reference time. Count is the number of selected users, not the number of domains, approvals or missing offboarding tasks. The detector returns a Medium finding with that count; it attaches affected-user details only when IncludeDetails is enabled and the count is positive.
There is no privileged-group, service-account or secret-related condition in this check. It covers all collected users meeting the date and enabled-state conditions. The thirty-day window is a declared product choice, not a Microsoft, ANSSI or CIS requirement. The implementation uses calendar-date addition; retain the audit reference and boundaries when explaining a specific result.
The LDAP parser populates AccountExpires through parseFileTime in public/internal/providers/ldap/parser.go. Empty input, 0, the maximum signed 64-bit sentinel and integer-parsing errors become zero time without a per-value diagnostic in that helper. Values before the Unix-epoch conversion boundary also become zero. Consequently, a zero parsed date can represent either never-expiring data or unavailable/unusable input. The detector skips both.
Use ACCOUNT_EXPIRE_SOON to start the owner-and-deadline review. A positive count does not establish an attack, password expiration, an unauthorized extension or a need to renew. Close the review with a documented end-date decision, verified attribute state and separately owned access-removal or dependency tasks.
Explore the identity security pages that support this topic
