02 / FILES, ACCOUNTS, AND SERVICES · LESSON 07

Review an account lifecycle snapshot

Find accounts needing review while respecting scope, missing history, and timestamp boundaries.

12 min · learn at your pace

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

Read the official reference.

Try an example.ps1
Import-Csv '/home/user/accounts.csv' | Select-Object Account, Kind, Enabled, LastSignInUtc
YOUR TURN

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
One way to solve it.ps1
$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.