diff --git a/CHANGELOG.md b/CHANGELOG.md index 5313502..fa21bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,18 @@ release notes. `-Credential`), are unchanged. - A backtick line continuation in `Get-IslSetting`, the one left in the module since 0.6.0 said there were none. +- **`-Credential` did not find a Microsoft Entra account's session.** The launcher looked for + the credential's user name, taken apart as text, in `query user`. Windows calls an Entra + account `AzureAD\`, which is neither the + sign-in name nor a part of it, so a credential naming `user@domain` was refused ("No mapping + between account names and security IDs") or fell back to the stored-password task. The + launcher now asks Windows which account the credential means, finds the session by the name + Windows gives it, registers the interactive task for that name and grants the run folder by + SID. Verified on the joined lab device with the sign-in name, `AzureAD\` and + the Windows name (Findings, "The harness as another account"). +- Under `-ErrorAction Stop` on Windows PowerShell 5.1, a refused grant on the run folder ended + with `icacls`' bare line ("No mapping between account names and security IDs was done") + instead of the message naming the account and the folder. - **`Repair-IntuneScript` hid the mistake it was run on.** `return 1; exit 1` became `1; exit 0; exit 1`: the same behaviour, the exit the author wrote unreachable, and no finding left. A script-scope `return` with an exit other than 0 after it in the same block now carries diff --git a/Private/Grant-IslFolderAccess.ps1 b/Private/Grant-IslFolderAccess.ps1 index 4c382c5..e454d57 100644 --- a/Private/Grant-IslFolderAccess.ps1 +++ b/Private/Grant-IslFolderAccess.ps1 @@ -30,7 +30,15 @@ function Grant-IslFolderAccess { [string]$Account ) - $output = & icacls.exe $Path /grant "${Account}:(OI)(CI)M" 2>&1 + # By SID where Windows can resolve the name: a sign-in name of an Entra account is not a name + # icacls looks up + $resolved = Resolve-IslAccount -Name $Account + $trustee = if ($resolved) { "*$($resolved.Sid)" } else { $Account } + # Windows PowerShell turns a native command's redirected stderr into error records, and under + # a caller's -ErrorAction Stop the first one ends the function with icacls' bare line instead + # of the message below + $ErrorActionPreference = 'Continue' + $output = & icacls.exe $Path /grant "${trustee}:(OI)(CI)M" 2>&1 if ($LASTEXITCODE -ne 0) { throw "Could not grant $Account access to ${Path}: $($output -join ' ')" } diff --git a/Private/Invoke-IslProcess.ps1 b/Private/Invoke-IslProcess.ps1 index 03e2ba8..ea01f5d 100644 --- a/Private/Invoke-IslProcess.ps1 +++ b/Private/Invoke-IslProcess.ps1 @@ -140,7 +140,11 @@ } else { $userName = $Credential.UserName - $account = if ($userName -match '\\') { ($userName -split '\\')[-1] } + # "query user" lists the name Windows gives the account, which for an Entra account is + # neither the sign-in name nor a part of it; ask Windows before taking the name apart + $resolved = Resolve-IslAccount -Name $userName + $account = if ($resolved) { ($resolved.Name -split '\\')[-1] } + elseif ($userName -match '\\') { ($userName -split '\\')[-1] } elseif ($userName -match '@') { ($userName -split '@')[0] } else { $userName } $sessions = @(Get-IslLogonSession | Where-Object { $_.UserName -eq $account }) @@ -149,7 +153,9 @@ else { 'Password' } Write-Verbose "Task for ${userName}: logon type $logon, $($sessions.Count) session(s) found" if ($logon -eq 'Interactive') { - $principalSplat = @{ UserId = $userName; LogonType = 'Interactive'; RunLevel = 'Limited' } + # The scheduler takes an Entra account by its Windows name only + $principalName = if ($resolved) { $resolved.Name } else { $userName } + $principalSplat = @{ UserId = $principalName; LogonType = 'Interactive'; RunLevel = 'Limited' } $registerTaskSplat.Principal = New-ScheduledTaskPrincipal @principalSplat } else { diff --git a/Private/Resolve-IslAccount.ps1 b/Private/Resolve-IslAccount.ps1 new file mode 100644 index 0000000..8d5f212 --- /dev/null +++ b/Private/Resolve-IslAccount.ps1 @@ -0,0 +1,84 @@ +function ConvertTo-IslAccount { + <# + .SYNOPSIS + Asks Windows for the SID behind one account name and for the name it gives that SID. + + .DESCRIPTION + The two translations Resolve-IslAccount is built on, kept apart so the unit tests can stand + in for accounts a build machine does not have. Returns nothing for a name Windows cannot + resolve, and nothing off Windows. + + .PARAMETER Name + One account name, exactly as it is to be looked up. + + .EXAMPLE + ConvertTo-IslAccount -Name 'NT AUTHORITY\SYSTEM' + + Name NT AUTHORITY\SYSTEM, Sid S-1-5-18. + #> + [CmdletBinding()] + [OutputType('IntuneScriptLab.Account')] + param( + [Parameter(Mandatory)] + [string]$Name + ) + + try { + $sidType = [System.Security.Principal.SecurityIdentifier] + $sid = ([System.Security.Principal.NTAccount]$Name).Translate($sidType) + [pscustomobject]@{ + PSTypeName = 'IntuneScriptLab.Account' + Name = $sid.Translate([System.Security.Principal.NTAccount]).Value + Sid = $sid.Value + } + } + catch { Write-Verbose "Account name '$Name' not resolved: $($_.Exception.Message)" } +} + +function Resolve-IslAccount { + <# + .SYNOPSIS + Finds what Windows itself calls the account a credential names, and its SID. + + .DESCRIPTION + A credential can name an account several ways and Windows shows only one of them. For a + Microsoft Entra account on a joined device (lab device, 2026-10-05; Findings, "The harness + as another account"): + + signed in as isl-verylongusername-test01@4nlnm3.onmicrosoft.com + display name Isl Verylongdisplayname Testaccount + Windows name AzureAD\IslVerylongdisplayna + + The Windows name is the display name without its spaces, cut at 20 characters, and it is + what "query user" lists, what owns the session's processes and the only name a scheduled + task accepted for an interactive principal: the sign-in name and the SID were both refused + there. The sign-in name resolves to the account's SID only with the AzureAD\ prefix. + + So the name is looked up as given and, when it is a sign-in name without a domain part + that does not resolve, again as AzureAD\. The SID is then turned back into the name + Windows uses. Returns nothing when neither lookup resolves; the caller then works with + the name as it was given. + + .PARAMETER Name + The account as the credential names it: isl-user, MACHINE\isl-user, DOMAIN\user, + user@domain or AzureAD\user@domain. + + .EXAMPLE + Resolve-IslAccount -Name 'someone@contoso.com' + + Name AzureAD\SomeOne and the account's SID, on a device where that Entra user has signed in. + #> + [CmdletBinding()] + [OutputType('IntuneScriptLab.Account')] + param( + [Parameter(Mandatory)] + [string]$Name + ) + + $candidates = @($Name) + if ($Name -match '@' -and $Name -notmatch '\\') { $candidates += "AzureAD\$Name" } + foreach ($candidate in $candidates) { + $account = ConvertTo-IslAccount -Name $candidate + if ($account) { return $account } + } +} diff --git a/README.md b/README.md index 392b049..cdaa469 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,11 @@ profile loaded, and `RunAs` says `(Password)` so you know the session differs; t the "Log on as a batch job" right, which a standard user does not have by default, and the launcher reports the refusal with that hint within seconds, whether the scheduler answers `0x80070569` or simply never starts the task (`0x00041303`, "has not run yet", which is what the lab device does -today). Needs an elevated session. On +today). Needs an elevated session. A Microsoft Entra account can be named by its sign-in name +(`user@domain`) or by the name Windows gives it: Windows calls such an account +`AzureAD\`, which is neither the sign-in name nor +a part of it, so the launcher asks Windows which account the credential means and looks for its +session under that name. On the lab device the interactive path reproduced the agent's launch point for point (console session, `UserInteractive` true, the account's profile paths, system32; `Validation/Findings.md`, "The harness as another account"). The script copy and its output live under `ProgramData\IntuneScriptLab\Runs` with the diff --git a/Tests/Unit/Private/Get-IslLogonSession.Tests.ps1 b/Tests/Unit/Private/Get-IslLogonSession.Tests.ps1 index 3bc4561..588a9dc 100644 --- a/Tests/Unit/Private/Get-IslLogonSession.Tests.ps1 +++ b/Tests/Unit/Private/Get-IslLogonSession.Tests.ps1 @@ -43,6 +43,20 @@ Describe 'Get-IslLogonSession' -Tag 'Unit', 'Private' { $sessions[1].State | Should-Be 'Disc' } + It 'reads a 20-character name, the longest Windows gives an account, as printed on VM 125' { + # An Entra user named "Isl Verylongdisplayname Testaccount" who signs in as + # isl-verylongusername-test01@...: Windows calls it AzureAD\IslVerylongdisplayna, the display + # name without spaces cut at 20 characters, and the column still ends in two spaces + $sessions = @(Get-SessionFromText -Lines @( + ' USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME' + ' islverylongdisplayna console 2 Active none 10/5/2026 11:05 AM' + )) + $sessions.Count | Should-Be 1 + $sessions[0].UserName | Should-Be 'islverylongdisplayna' + $sessions[0].SessionName | Should-Be 'console' + $sessions[0].Id | Should-Be 2 + } + It 'returns nothing when nobody is logged on, the tool is missing, or only the header prints' { @(Get-SessionFromText -Lines @()).Count | Should-Be 0 @(Get-SessionFromText -Lines @(' USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME')).Count | diff --git a/Tests/Unit/Private/Grant-IslFolderAccess.Tests.ps1 b/Tests/Unit/Private/Grant-IslFolderAccess.Tests.ps1 new file mode 100644 index 0000000..a5be3c9 --- /dev/null +++ b/Tests/Unit/Private/Grant-IslFolderAccess.Tests.ps1 @@ -0,0 +1,72 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.2.0' } + +<# + The grant on the run folder another account's script is copied to. By SID where Windows can + resolve the account, because the sign-in name of an Entra account is not a name icacls looks up + (VM 125, 2026-10-05); by the name as given otherwise. +#> + +BeforeAll { + $script:ModuleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) + Import-Module (Join-Path $script:ModuleRoot 'IntuneScriptLab.psd1') -Force + $script:Me = [System.Security.Principal.WindowsIdentity]::GetCurrent() + + function Grant-Access { + param([string]$Path, [string]$Account, [string]$Preference = 'Continue') + $parameters = @{ Path = $Path; Account = $Account; Preference = $Preference } + InModuleScope IntuneScriptLab -Parameters $parameters { + # What a caller's -ErrorAction leaves in force inside the module + $ErrorActionPreference = $Preference + Grant-IslFolderAccess -Path $Path -Account $Account + } + } +} + +AfterAll { + Remove-Module IntuneScriptLab -Force -ErrorAction SilentlyContinue +} + +Describe 'Grant-IslFolderAccess' -Tag 'Unit', 'Private' { + + BeforeEach { + $script:Folder = Join-Path $TestDrive "run-$([guid]::NewGuid().ToString('N'))" + $null = New-Item -ItemType Directory -Path $script:Folder + } + + It 'grants Modify, inherited by files and folders, by the SID of an account Windows resolves' { + Mock Resolve-IslAccount -ModuleName IntuneScriptLab { + [pscustomobject]@{ Name = 'AzureAD\IslVerylongdisplayna'; Sid = 'S-1-5-32-545' } + } + # S-1-5-32-545 is BUILTIN\Users, standing in for the account: the grant has to land on the + # SID it was given, whatever the name passed in + Grant-Access $script:Folder 'isl-verylongusername-test01@4nlnm3.onmicrosoft.com' + $rule = (Get-Acl -Path $script:Folder).Access | Where-Object { + -not $_.IsInherited -and + $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -eq 'S-1-5-32-545' + } + @($rule).Count | Should-Be 1 + "$($rule.FileSystemRights)" | Should-BeLikeString '*Modify*' + "$($rule.InheritanceFlags)" | Should-Be 'ContainerInherit, ObjectInherit' + } + + It 'grants by the name as given when Windows does not resolve it' { + Mock Resolve-IslAccount -ModuleName IntuneScriptLab { } + Grant-Access $script:Folder $script:Me.Name + $rule = (Get-Acl -Path $script:Folder).Access | Where-Object { + -not $_.IsInherited -and "$($_.IdentityReference)" -eq $script:Me.Name + } + @($rule).Count | Should-Be 1 + } + + It 'names the account and the folder when icacls refuses, under error action ' -ForEach @( + @{ Preference = 'Continue' } + @{ Preference = 'Stop' } + ) { + # Under Stop, Windows PowerShell 5.1 ended the function at icacls' first stderr line with + # that line alone as the message (the CI runner and the lab device both showed it) + Mock Resolve-IslAccount -ModuleName IntuneScriptLab { } + $missing = "$env:COMPUTERNAME\no-such-account-for-isl" + $failure = { Grant-Access $script:Folder $missing $Preference } | Should-Throw + $failure.Exception.Message | Should-BeLikeString "Could not grant $missing access to *No mapping*" + } +} diff --git a/Tests/Unit/Private/Invoke-IslProcess.Tests.ps1 b/Tests/Unit/Private/Invoke-IslProcess.Tests.ps1 index df74ac2..290c605 100644 --- a/Tests/Unit/Private/Invoke-IslProcess.Tests.ps1 +++ b/Tests/Unit/Private/Invoke-IslProcess.Tests.ps1 @@ -168,6 +168,8 @@ Describe 'Invoke-IslProcess' -Tag 'Unit', 'Private' { Mock Unregister-ScheduledTask -ModuleName IntuneScriptLab { } Mock Stop-ScheduledTask -ModuleName IntuneScriptLab { } Mock Get-ScheduledTask -ModuleName IntuneScriptLab { [pscustomobject]@{ State = 'Ready' } } + # The accounts in these tests do not exist here: Windows resolves none of them + Mock Resolve-IslAccount -ModuleName IntuneScriptLab { } Mock Start-ScheduledTask -ModuleName IntuneScriptLab { $folder = Join-Path ([IO.Path]::GetTempPath()) $TaskName.Substring('IntuneScriptLab-'.Length) [IO.File]::WriteAllText((Join-Path $folder 'stdout.txt'), "user-out`r`n") @@ -237,6 +239,48 @@ Describe 'Invoke-IslProcess' -Tag 'Unit', 'Private' { $result.UserName | Should-Be 'isl-user@lab.local' } + It 'finds an Entra account''s session by the name Windows gives it, not by its sign-in name (VM 125)' { + # Signed in as isl-verylongusername-test01@..., listed by "query user" as + # islverylongdisplayna; the scheduler takes the Windows name and refuses the sign-in name + Mock Resolve-IslAccount -ModuleName IntuneScriptLab { + [pscustomobject]@{ + Name = 'AzureAD\IslVerylongdisplayna' + Sid = 'S-1-12-1-1497552185-1263987200-3276725654-805488699' + } + } + Mock Get-IslLogonSession -ModuleName IntuneScriptLab { + [pscustomobject]@{ + UserName = 'islverylongdisplayna'; SessionName = 'console'; Id = 2; State = 'Active' + } + } + $launchSplat = $script:LaunchSplat.Clone() + $signInName = 'isl-verylongusername-test01@4nlnm3.onmicrosoft.com' + $launchSplat.Credential = [pscredential]::new($signInName, $script:Credential.Password) + $result = Invoke-Process $launchSplat + $result.LogonType | Should-Be 'Interactive' + $result.UserName | Should-Be $signInName + Should-Invoke Resolve-IslAccount -ModuleName IntuneScriptLab -Exactly -Times 1 -ParameterFilter { + $Name -eq 'isl-verylongusername-test01@4nlnm3.onmicrosoft.com' + } + Should-Invoke Register-ScheduledTask -ModuleName IntuneScriptLab -Exactly -Times 1 -ParameterFilter { + $Principal.UserId -eq 'AzureAD\IslVerylongdisplayna' -and + "$($Principal.LogonType)" -eq 'Interactive' -and $null -eq $Password + } + } + + It 'does not take another account''s session for a sign-in name that only looks like it' { + # The part before the @ of one account can be the Windows name of another + Mock Resolve-IslAccount -ModuleName IntuneScriptLab { + [pscustomobject]@{ Name = 'AzureAD\SomeoneElse'; Sid = 'S-1-12-1-1-2-3-4' } + } + Mock Get-IslLogonSession -ModuleName IntuneScriptLab { + [pscustomobject]@{ UserName = 'isl-user'; SessionName = 'console'; Id = 2; State = 'Active' } + } + $launchSplat = $script:LaunchSplat.Clone() + $launchSplat.Credential = [pscredential]::new('isl-user@lab.local', $script:Credential.Password) + (Invoke-Process $launchSplat).LogonType | Should-Be 'Password' + } + It 'reports a logon the scheduler refuses instead of waiting for the timeout (VM 125, isl-user)' { Mock Get-IslLogonSession -ModuleName IntuneScriptLab { @() } Mock Start-ScheduledTask -ModuleName IntuneScriptLab { } diff --git a/Tests/Unit/Private/Resolve-IslAccount.Tests.ps1 b/Tests/Unit/Private/Resolve-IslAccount.Tests.ps1 new file mode 100644 index 0000000..f26d00b --- /dev/null +++ b/Tests/Unit/Private/Resolve-IslAccount.Tests.ps1 @@ -0,0 +1,79 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.2.0' } + +<# + What Windows calls the account a credential names. The lookups themselves run against this + machine's own accounts; the Entra case, which needs a joined device with the user signed in, is + played back from what the lab device answered on 2026-10-05. +#> + +BeforeAll { + $script:ModuleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) + Import-Module (Join-Path $script:ModuleRoot 'IntuneScriptLab.psd1') -Force + $script:Me = [System.Security.Principal.WindowsIdentity]::GetCurrent() + + function Resolve-Account { + param([string]$Name) + InModuleScope IntuneScriptLab -Parameters @{ Name = $Name } { Resolve-IslAccount -Name $Name } + } +} + +AfterAll { + Remove-Module IntuneScriptLab -Force -ErrorAction SilentlyContinue +} + +Describe 'Resolve-IslAccount' -Tag 'Unit', 'Private' { + + Context 'Against this machine' { + It 'returns the Windows name and the SID of the account running the tests' { + $account = Resolve-Account $script:Me.Name + $account.PSObject.TypeNames | Should-ContainCollection 'IntuneScriptLab.Account' + $account.Name | Should-Be $script:Me.Name + $account.Sid | Should-Be $script:Me.User.Value + } + + It 'turns a well-known name into the name Windows uses for it' { + $account = Resolve-Account 'SYSTEM' + $account.Sid | Should-Be 'S-1-5-18' + $account.Name | Should-BeLikeString '*\SYSTEM' + } + + It 'returns nothing for a name Windows cannot resolve' { + # Qualified with this machine's name, so the lookup stays local and quick + @(Resolve-Account "$env:COMPUTERNAME\no-such-account-for-isl").Count | Should-Be 0 + } + } + + Context 'A Microsoft Entra account (VM 125)' { + BeforeEach { + # What the joined device answered: the sign-in name resolves only behind AzureAD\, and + # the SID's own name is the display name without spaces, cut at 20 characters + Mock ConvertTo-IslAccount -ModuleName IntuneScriptLab { + if ($Name -in 'AzureAD\isl-verylongusername-test01@4nlnm3.onmicrosoft.com', + 'AzureAD\IslVerylongdisplayna') { + [pscustomobject]@{ + Name = 'AzureAD\IslVerylongdisplayna' + Sid = 'S-1-12-1-1497552185-1263987200-3276725654-805488699' + } + } + } + } + + It 'resolves the sign-in name by trying it behind AzureAD\ when it does not resolve as given' { + $account = Resolve-Account 'isl-verylongusername-test01@4nlnm3.onmicrosoft.com' + $account.Name | Should-Be 'AzureAD\IslVerylongdisplayna' + $account.Sid | Should-Be 'S-1-12-1-1497552185-1263987200-3276725654-805488699' + Should-Invoke ConvertTo-IslAccount -ModuleName IntuneScriptLab -Exactly -Times 2 + } + + It 'resolves the Windows name as given, in one lookup' { + (Resolve-Account 'AzureAD\IslVerylongdisplayna').Name | Should-Be 'AzureAD\IslVerylongdisplayna' + Should-Invoke ConvertTo-IslAccount -ModuleName IntuneScriptLab -Exactly -Times 1 + } + + It 'does not guess a domain for a name that already has one, or for a bare name' { + @(Resolve-Account 'CONTOSO\isl-verylongusername-test01').Count | Should-Be 0 + @(Resolve-Account 'isl-verylongusername-test01').Count | Should-Be 0 + Should-Invoke ConvertTo-IslAccount -ModuleName IntuneScriptLab -Exactly -Times 2 + } + } +} diff --git a/Validation/Findings.md b/Validation/Findings.md index 6da9c59..1008528 100644 --- a/Validation/Findings.md +++ b/Validation/Findings.md @@ -374,14 +374,25 @@ console session active): the stored-password task no longer comes back with `0x8 anywhere, and the launcher waited out its timeout. 0.26.0 treats five seconds of that after `Start-ScheduledTask` as the refusal and reports it with the same hint. -2026-10-05, the same device: `query user`, which `Get-IslLogonSession` parses to find the -account's session, printed ` USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME` -and ` isl-user console 1 Active none 10/4/2026 11:00 PM`: the -user name column is 22 characters wide. What it prints for a name that fills or exceeds the column -is **not measured**: a local account name stops at 20 characters, and no Entra account with a -longer one was signed in. Fed such a line by hand, the parser takes a 21-character name and the -session name as one field, so the account would not match and the launcher would fall back to the -stored-password task. +2026-10-05, the same device, a Microsoft Entra account at the console. The tenant user +`isl-verylongusername-test01@4nlnm3.onmicrosoft.com` (27 characters before the `@`), display name +"Isl Verylongdisplayname Testaccount", signed in at the "Other user" prompt: + +| Question | Observed | +|---|---| +| What Windows calls the account | `AzureAD\IslVerylongdisplayna`: the **display name** without its spaces, cut at 20 characters. Not the sign-in name and not a part of it (round 1 had the same shape: signed in as `betar@...`, running as `AzureAD\JeffStuhr`). `USERNAME`, the owner of the session's `explorer.exe` and the profile folder (`C:\Users\IslVerylongdisplayna`) all carry it; `whoami /upn` gives the sign-in name | +| What `query user` prints | ` islverylongdisplayna console 2 Active none 10/5/2026 11:05 AM`: the same name in lower case. The user name column is 22 characters wide and this name was cut at 20, so two spaces remained and the parser's split held. No name longer than 20 characters was seen | +| Name to SID (`NTAccount.Translate`, as SYSTEM) | `AzureAD\` and `AzureAD\IslVerylongdisplayna` resolve to the account's SID (`S-1-12-1-...`), and the SID translates back to `AzureAD\IslVerylongdisplayna`. The bare sign-in name, the bare Windows name, `AzureAD\` and `AzureAD\` do not resolve | +| Which name a scheduled task takes for an interactive principal | Only `AzureAD\IslVerylongdisplayna`. The sign-in name and the SID string were both refused at registration: "No mapping between account names and security IDs was done" | +| The module before the change, `-Credential` named three ways | Sign-in name: refused at once, `No mapping between account names and security IDs was done`. `AzureAD\`: no session found, fell back to the stored-password task, "The user name or password is incorrect". `AzureAD\IslVerylongdisplayna`: ran in the session | +| The module after it | All three ran the script inside the account's session: `who=AzureAD\IslVerylongdisplayna session=2 interactive=True`, `RunAs` naming the credential as given with `(Interactive)`; no task and no run folder left behind | + +**For the tool:** the launcher matched `query user` against the credential's user name taken +apart as text, which can only work when the Windows name happens to equal that text. That holds +for a local account and fails for an Entra account whatever its length: the Windows name comes +from the display name. `Resolve-IslAccount` now asks Windows for the account's SID (trying +`AzureAD\` in front of a sign-in name) and for the name behind that SID; the session is matched on +that name, the interactive task is registered for it, and the run folder is granted by SID. ### Reporting latency, re-measured (2026-09-29) diff --git a/docs/IntuneScriptLab/Invoke-IntuneDetectionTest.md b/docs/IntuneScriptLab/Invoke-IntuneDetectionTest.md index ad9ab2c..0b69dcd 100644 --- a/docs/IntuneScriptLab/Invoke-IntuneDetectionTest.md +++ b/docs/IntuneScriptLab/Invoke-IntuneDetectionTest.md @@ -4,7 +4,7 @@ external help file: IntuneScriptLab-Help.xml HelpUri: https://github.com/fadwen/IntuneScriptLab/blob/main/docs/IntuneScriptLab/Invoke-IntuneDetectionTest.md Locale: en-US Module Name: IntuneScriptLab -ms.date: 09/28/2026 +ms.date: 10/05/2026 PlatyPS schema version: 2024-05-01 title: Invoke-IntuneDetectionTest --- @@ -115,7 +115,7 @@ HelpMessage: '' ### -Credential -Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. +Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. ```yaml Type: System.Management.Automation.PSCredential diff --git a/docs/IntuneScriptLab/Invoke-IntunePlatformScriptTest.md b/docs/IntuneScriptLab/Invoke-IntunePlatformScriptTest.md index 882636e..15eccaf 100644 --- a/docs/IntuneScriptLab/Invoke-IntunePlatformScriptTest.md +++ b/docs/IntuneScriptLab/Invoke-IntunePlatformScriptTest.md @@ -4,7 +4,7 @@ external help file: IntuneScriptLab-Help.xml HelpUri: https://github.com/fadwen/IntuneScriptLab/blob/main/docs/IntuneScriptLab/Invoke-IntunePlatformScriptTest.md Locale: en-US Module Name: IntuneScriptLab -ms.date: 09/28/2026 +ms.date: 10/05/2026 PlatyPS schema version: 2024-05-01 title: Invoke-IntunePlatformScriptTest --- @@ -112,7 +112,7 @@ HelpMessage: '' ### -Credential -Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. +Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. ```yaml Type: System.Management.Automation.PSCredential diff --git a/docs/IntuneScriptLab/Invoke-IntuneRemediationTest.md b/docs/IntuneScriptLab/Invoke-IntuneRemediationTest.md index a1211d2..d79b031 100644 --- a/docs/IntuneScriptLab/Invoke-IntuneRemediationTest.md +++ b/docs/IntuneScriptLab/Invoke-IntuneRemediationTest.md @@ -4,7 +4,7 @@ external help file: IntuneScriptLab-Help.xml HelpUri: https://github.com/fadwen/IntuneScriptLab/blob/main/docs/IntuneScriptLab/Invoke-IntuneRemediationTest.md Locale: en-US Module Name: IntuneScriptLab -ms.date: 09/28/2026 +ms.date: 10/05/2026 PlatyPS schema version: 2024-05-01 title: Invoke-IntuneRemediationTest --- @@ -129,7 +129,7 @@ HelpMessage: '' ### -Credential -Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. +Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. ```yaml Type: System.Management.Automation.PSCredential diff --git a/docs/IntuneScriptLab/Invoke-IntuneRequirementTest.md b/docs/IntuneScriptLab/Invoke-IntuneRequirementTest.md index 27c0b20..f84ef43 100644 --- a/docs/IntuneScriptLab/Invoke-IntuneRequirementTest.md +++ b/docs/IntuneScriptLab/Invoke-IntuneRequirementTest.md @@ -4,7 +4,7 @@ external help file: IntuneScriptLab-Help.xml HelpUri: https://github.com/fadwen/IntuneScriptLab/blob/main/docs/IntuneScriptLab/Invoke-IntuneRequirementTest.md Locale: en-US Module Name: IntuneScriptLab -ms.date: 09/28/2026 +ms.date: 10/05/2026 PlatyPS schema version: 2024-05-01 title: Invoke-IntuneRequirementTest --- @@ -117,7 +117,7 @@ HelpMessage: '' ### -Credential -Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. +Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. ```yaml Type: System.Management.Automation.PSCredential diff --git a/docs/IntuneScriptLab/Invoke-IntuneWin32AppTest.md b/docs/IntuneScriptLab/Invoke-IntuneWin32AppTest.md index 346da2a..a3c763f 100644 --- a/docs/IntuneScriptLab/Invoke-IntuneWin32AppTest.md +++ b/docs/IntuneScriptLab/Invoke-IntuneWin32AppTest.md @@ -4,7 +4,7 @@ external help file: IntuneScriptLab-Help.xml HelpUri: https://github.com/fadwen/IntuneScriptLab/blob/main/docs/IntuneScriptLab/Invoke-IntuneWin32AppTest.md Locale: en-US Module Name: IntuneScriptLab -ms.date: 09/28/2026 +ms.date: 10/05/2026 PlatyPS schema version: 2024-05-01 title: Invoke-IntuneWin32AppTest --- @@ -210,7 +210,7 @@ HelpMessage: '' ### -Credential -Run every launch (the detection script, the related apps' detections, the install or uninstall command) as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. +Run every launch (the detection script, the related apps' detections, the install or uninstall command) as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. ```yaml Type: System.Management.Automation.PSCredential diff --git a/en-US/IntuneScriptLab-Help.xml b/en-US/IntuneScriptLab-Help.xml index c3f90e4..7468d1b 100644 --- a/en-US/IntuneScriptLab-Help.xml +++ b/en-US/IntuneScriptLab-Help.xml @@ -2777,7 +2777,7 @@ not that account's profile or rights. Credential - Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. + Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. System.Management.Automation.PSCredential @@ -2998,7 +2998,7 @@ hybrid-joined devices run them. Credential - Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. + Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. System.Management.Automation.PSCredential @@ -3234,7 +3234,7 @@ user-context remediations on Entra-registered devices. Credential - Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. + Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. System.Management.Automation.PSCredential @@ -3486,7 +3486,7 @@ User runs it as the account running this command. Credential - Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. + Run as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. System.Management.Automation.PSCredential @@ -3857,7 +3857,7 @@ install in the user's context but cannot impersonate the signed-in user the agen Credential - Run every launch (the detection script, the related apps' detections, the install or uninstall command) as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. + Run every launch (the detection script, the related apps' detections, the install or uninstall command) as this account instead of the current user, with -Context User: a one-shot scheduled task registered for the account, interactive inside the account's own session when it holds one (the way the agent runs user-context scripts inside the signed-in user's session, REM-PROBE-USER64), otherwise a stored-password logon in session 0, which needs the account to hold the "Log on as a batch job" right (a standard user does not; the scheduler then never starts the task, and the launcher reports that within seconds rather than at the timeout); the result's RunAs says which. Needs an elevated session, and is refused with -Context System. A Microsoft Entra account can be named by its sign-in name (user@domain, with or without AzureAD\ in front) or by the name Windows gives it (AzureAD\Name, the display name without spaces, cut at 20 characters): the launcher asks Windows which account is meant and finds its session by the Windows name. Validation\New-IslHarnessUser.ps1 creates a lab account with a stored credential to use here. System.Management.Automation.PSCredential