Repository navigation
fix(harness): sessions from the desktop processes' owners, matched by SID - #10
Conversation
… SID -Credential parsed 'query user' to tell whether the account holds a session. Windows Home editions do not ship query.exe, so every run there fell back to the stored-password task, and the tool prints localized text. The session list now comes from the owners of each desktop's explorer.exe and sihost.exe through CIM, with the owner's SID, and the account is matched by the SID its name resolves to, by the owner's name as text otherwise. Verified on a Home machine and on the joined lab device with the Entra test user signed in.
24aaa59 to
22e2ef8
Compare
fadwen
left a comment
There was a problem hiding this comment.
Notes on the lines whose reason the diff does not show.
| $filter = "Name='explorer.exe' OR Name='sihost.exe'" | ||
| $shells = @(Get-CimInstance -ClassName Win32_Process -Filter $filter -ErrorAction SilentlyContinue) | ||
| $seen = @{} | ||
| foreach ($shell in ($shells | Sort-Object -Property SessionId, Name)) { |
There was a problem hiding this comment.
CIM returns processes in no fixed order. Sorted so the output order is the same on both hosts and, where a session has both processes, the explorer.exe row is the one kept by the check below.
| $owner = Invoke-CimMethod -InputObject $shell -MethodName GetOwner -ErrorAction SilentlyContinue | ||
| $sid = (Invoke-CimMethod -InputObject $shell -MethodName GetOwnerSid -ErrorAction SilentlyContinue).Sid | ||
| if (-not $sid -or -not $owner.User) { continue } | ||
| $key = "$($shell.SessionId)|$sid" |
There was a problem hiding this comment.
One session can hold several of these processes: sihost.exe next to explorer.exe, and a second explorer.exe when a folder window runs as its own process. One row per session and owner is what the caller counts.
| else { $userName } | ||
| $sessions = @(Get-IslLogonSession | Where-Object { $_.UserName -eq $account }) | ||
| $sessions = @(Get-IslLogonSession | Where-Object { | ||
| if ($resolved) { $_.Sid -eq $resolved.Sid } else { $_.UserName -eq $account } |
There was a problem hiding this comment.
The text match is the path for a name Windows cannot translate, where Resolve-IslAccount returns nothing. It keeps the behaviour from before #6 there, and the scheduler then reports the real error for the name itself.
| ' isl-user 3 Disc 1:23 9/28/2026 1:02 AM' | ||
| It 'lists one session per desktop user, from the owner of its shell processes' { | ||
| $sessions = Get-Session @( | ||
| (New-Shell 'sihost.exe' 1 101), (New-Shell 'explorer.exe' 1 100), (New-Shell 'explorer.exe' 2 200) |
There was a problem hiding this comment.
sihost.exe is listed before explorer.exe, and with the higher process id, on purpose: the result must not depend on the order CIM hands the processes back.
Summary
-Credentialon the harness commands decided between an interactive task and a stored-password task by parsingquery user. Windows Home editions do not shipquery.exe, so every run there fell back to the stored-password task, and the tool's output is localized, so the parse depended on the device's language. The session list now comes from the owners of each desktop'sexplorer.exeandsihost.exethrough CIM, and the account is matched by its SID.Follows #6, which resolved the credential's name to the account Windows means but still matched the session on the resulting name.
Changes
Get-IslLogonSession. Lists one session per desktop user fromWin32_Process(explorer.exe, orsihost.exewhile the desktop is still starting), with the owner's name and domain fromGetOwner, the SID fromGetOwnerSid, and the session id. A process whose owner cannot be read is left out. Reading another account's process owner needs an elevated session, which the credential launch needs anyway.Invoke-IslProcess. Matches the session by SID whenResolve-IslAccountresolves the credential's name, by the owner's name as text otherwise.Get-IslQueryUserOutputremoved with its only caller.Get-IslLogonSessionover mocked CIM: one session per user from several shell processes, a session withsihost.exealone, an unreadable owner left out, no desktop at all, and the real machine for shape. Launcher tests carry the new session shape (Sid,Domain).Verification
query.exe):Get-IslLogonSessionlists the console session with its SID, where the previous version returned nothing.Get-IslLogonSessionlistedAzureAD\IslVerylongdisplayna, SIDS-1-12-1-...-805488699, session 2, the SID the sign-in name resolves to;-Credentialwith the sign-in name,AzureAD\<sign-in name>and the Windows name each ran the script inside session 2 withinteractive=True, and left no task and no run folder behind.Notes