Review an account lifecycle snapshot
Find accounts needing review while respecting scope, missing history, and timestamp boundaries.
Review a defined population
The fictional policy covers enabled human accounts only. Disabled accounts and service accounts are out of scope for this particular inactivity review. They are not automatically safe or irrelevant; they need different administration rules.
CSV stores Enabled as text. [bool]'false' evaluates to true because it is a non-empty string. Compare against 'true' explicitly or parse the word with [bool]::Parse(). Do not use a direct boolean cast to decide which accounts are enabled.
Preserve uncertainty
At the fixed reference instant 2026-10-31T00:00:00Z, a last sign-in strictly earlier than 30 days ago produces Inactive. Exactly at the cutoff remains in policy. A missing sign-in produces NoSignIn; a future timestamp produces FutureTimestamp and may indicate a clock or export issue.
[bool]'false'
[bool]::Parse('false')
NoSignIn does not mean the account has never been used. It means this export supplies no sign-in timestamp. Directory replication, log retention, collection scope, and new accounts can affect what an export shows.
Emit a review list containing the source RecordId, account name, and reason. Do not disable accounts or remove access. That decision needs current ownership and business context in the real environment; the browser lab teaches the rule and its data handling.
Go deeper
Import-Csv '/home/user/accounts.csv' | Select-Object Account, Kind, Enabled, LastSignInUtc
Put it into practice.
At 2026-10-31T00:00:00Z, review enabled Human accounts in accounts.csv. Emit RecordId, Account, Reason for: a sign-in strictly older than 30 days → Inactive; an empty sign-in → NoSignIn; a future sign-in → FutureTimestamp. Exclude disabled/service accounts and exact-cutoff timestamps. Do not change account data.
See an explained solution
$now = [DateTimeOffset]::Parse('2026-10-31T00:00:00Z')
foreach ($account in Import-Csv '/home/user/accounts.csv') {
if ($account.Kind -ne 'Human' -or $account.Enabled -ne 'true') { continue }
$reason = $null
if (-not $account.LastSignInUtc) { $reason = 'NoSignIn' }
else {
$last = [DateTimeOffset]::Parse($account.LastSignInUtc)
if ($last -gt $now) { $reason = 'FutureTimestamp' }
elseif ($last -lt $now.AddDays(-30)) { $reason = 'Inactive' }
}
if ($reason) { [pscustomobject]@{ RecordId=$account.RecordId; Account=$account.Account; Reason=$reason } }
}
The scope filter handles CSV text explicitly. Missing and future timestamps stay separate from inactivity, and the exact cutoff is preserved instead of rounded into an older day.