When working with Windows system administration, you'll quickly discover that user account creation timestamps aren't directly exposed through standard GUI tools. The Win32_UserAccount
WMI class surprisingly doesn't include this fundamental piece of information, leaving many admins searching for alternative methods.
While examining the profile folder's creation date seems logical, this approach has significant limitations:
Get-ChildItem C:\Users | Select Name,CreationTime
This method fails because:
- Profile folders can be created before account creation during imaging
- Folders might be recreated during profile repairs
- System accounts often have mismatched dates
Windows actually stores account creation dates in the SAM registry hive, but direct access is blocked for security reasons. Here's how we can work around this:
$sid = (Get-WmiObject Win32_UserAccount -Filter "Name='Username'").SID
$regPath = "HKLM:\SAM\SAM\Domains\Account\Users\$($sid -replace '-500$','')"
try {
$binaryData = (Get-ItemProperty -Path $regPath -Name F).F
$fileTime = [BitConverter]::ToInt64($binaryData[0x38..0x3F],0)
[DateTime]::FromFileTime($fileTime)
} catch {
Write-Warning "Access denied - requires elevated permissions"
}
For quick checks without registry access, we can parse command output:
$userInfo = net user username | Select-String "Account active"
if ($userInfo -match "since (.*)") {
[DateTime]::Parse($matches[1])
}
For domain controllers or systems with detailed logging, we can query security events:
Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4720]]" |
Where-Object {$_.Properties[0].Value -eq "username"} |
Select-Object TimeCreated
For production environments, consider using the PSUserAccount
module:
Install-Module PSUserAccount -Force
Get-LocalUserCreationDate -UserName "TargetUser"
- Registry method requires SYSTEM privileges (use psexec -s)
- Event log method depends on audit policy configuration
- Some methods only work for active accounts
When managing Windows systems (including Windows 7 and newer), administrators often need to determine when local user accounts were originally created. While properties like last login time are readily available through WMI, the creation timestamp isn't exposed as a direct property in standard WMI classes.
Many developers initially check these approaches:
# Common but ineffective attempts:
Get-WmiObject Win32_UserAccount | Select-Object Name, Domain, SID
Get-WmiObject Win32_NetworkLoginProfile | Select-Object Name, LastLogin
The profile folder creation date isn't reliable because:
- Folders might be created after account creation
- Profile migration can change folder timestamps
- System restore points may affect dates
Method 1: Using SID History (Most Reliable)
Windows local accounts have SIDs where the third part (RID) encodes creation order:
function Get-LocalUserCreationDate {
param([string]$Username)
$user = Get-LocalUser -Name $Username -ErrorAction SilentlyContinue
if (-not $user) {
Write-Warning "User not found"
return
}
$sid = $user.SID
$rid = $sid.Value.Split('-')[-1]
$baseDate = [datetime]::Parse("01/01/2000")
return $baseDate.AddDays([int]$rid)
}
# Example usage:
Get-LocalUserCreationDate -Username "Administrator"
Method 2: Event Log Analysis
For accounts created recently (event logs still available):
Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4720]]" |
Where-Object {$_.Properties[0].Value -like "*$Username*"} |
Select-Object TimeCreated, @{n='TargetUser';e={$_.Properties[0].Value}}
Method 3: Registry Key Timestamps
Check SAM registry key modification dates (requires admin privileges):
$sidPattern = (Get-LocalUser $Username).SID.Value
$samKey = "HKLM:\\SAM\\SAM\\Domains\\Account\\Users"
Get-ChildItem $samKey |
Where-Object {$_.Name -match $sidPattern} |
Select-Object Name, LastWriteTime
The SID method works on:
- Windows 7 and newer
- Windows Server 2008 R2 and newer
For legacy systems, consider combining registry and event log approaches.
Here's a complete function with error handling:
function Get-UserCreationInfo {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Username,
[switch]$UseEventLog,
[switch]$UseRegistry
)
try {
$user = Get-LocalUser -Name $Username -ErrorAction Stop
# Primary method (SID-based)
$result = [ordered]@{
Username = $Username
SID = $user.SID.Value
CreationDate = $null
Method = $null
}
# Calculate from RID
$rid = [int]$user.SID.Value.Split('-')[-1]
$baseDate = [datetime]::Parse("01/01/2000")
$result.CreationDate = $baseDate.AddDays($rid)
$result.Method = "SID RID Calculation"
# Alternative methods if requested
if ($UseEventLog) {
$event = Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4720]]" -MaxEvents 1 |
Where-Object {$_.Properties[0].Value -eq $Username} |
Select-Object -First 1
if ($event) {
$result.EventLogDate = $event.TimeCreated
$result.Method += " + Event Log"
}
}
if ($UseRegistry) {
$sidPattern = $user.SID.Value
$samKey = "HKLM:\\SAM\\SAM\\Domains\\Account\\Users"
if (Test-Path $samKey) {
$key = Get-ChildItem $samKey -ErrorAction SilentlyContinue |
Where-Object {$_.Name -match $sidPattern}
if ($key) {
$result.RegistryDate = $key.LastWriteTime
$result.Method += " + Registry"
}
}
}
return [pscustomobject]$result
}
catch {
Write-Error $_.Exception.Message
}
}
- SID method has ~1 day precision
- Event logs rotate and get overwritten
- Registry method requires SYSTEM privileges
- Domain accounts require different approaches