diff --git a/CHANGELOG.md b/CHANGELOG.md index e274c0b..efb6dd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,17 @@ release notes. silence, was already right. Its file `doesNotExist` finding cited `W32-FILE-NOTEXIST-PRESENT`; the experiment is `W32-FILE-NOTEXIST-FALSE`. +### Changed + +- Validation kit: `Invoke-ValidationRound.ps1 -Action Trigger` stops the agent's process and starts + the service, and prints the process start time before and after, since `Restart-Service` reported + the service running and restarted nothing (round 11). `New-IntuneLabVm.ps1` makes its guest calls + through `GuestAgent.ps1` instead of its own synchronous `qm guest exec`, whose results Windows' + process id reuse mixed up. `Invoke-LabGuestScript.ps1`'s help describes the file-write transfer + and the rerun after a lost result, and it and the two probe scripts require PowerShell 7.6 like + the driver. The kit's README no longer points at the monorepo paths the module came from, and the + experiment catalog's header says what it holds. + ## [0.33.0] - 2026-10-15 Fixes to the CI pieces, found by running the gate script, the workflow template, the suite template diff --git a/Validation/Experiments.psd1 b/Validation/Experiments.psd1 index cede8a9..f5786b3 100644 --- a/Validation/Experiments.psd1 +++ b/Validation/Experiments.psd1 @@ -1,4 +1,4 @@ -# Round 1 experiments. Each script body is appended to Probe.ps1 before upload. +# The experiment catalog, rounds 1 to 11. Each script body is appended to Probe.ps1 before upload. # Names are prefixed with ISL- in Intune so Invoke-ValidationRound can find and remove them. # # Keys: diff --git a/Validation/Invoke-AssignmentProbe.ps1 b/Validation/Invoke-AssignmentProbe.ps1 index 2850ac4..584b142 100644 --- a/Validation/Invoke-AssignmentProbe.ps1 +++ b/Validation/Invoke-AssignmentProbe.ps1 @@ -1,4 +1,4 @@ -#Requires -Version 7.4 +#Requires -Version 7.6 #Requires -Modules Microsoft.Graph.Authentication <# diff --git a/Validation/Invoke-FilterProbe.ps1 b/Validation/Invoke-FilterProbe.ps1 index fcb8b6b..f6265dc 100644 --- a/Validation/Invoke-FilterProbe.ps1 +++ b/Validation/Invoke-FilterProbe.ps1 @@ -1,4 +1,4 @@ -#Requires -Version 7.4 +#Requires -Version 7.6 #Requires -Modules Microsoft.Graph.Authentication <# diff --git a/Validation/Invoke-LabGuestScript.ps1 b/Validation/Invoke-LabGuestScript.ps1 index 1a9d53b..cd44657 100644 --- a/Validation/Invoke-LabGuestScript.ps1 +++ b/Validation/Invoke-LabGuestScript.ps1 @@ -1,15 +1,15 @@ -#Requires -Version 7.4 +#Requires -Version 7.6 <# .SYNOPSIS Runs a local PowerShell script inside a Proxmox lab VM through the QEMU guest agent, as SYSTEM. .DESCRIPTION - The guest agent's command line fails silently above a few KB, so the script is delivered as - base64 in numbered part files (Set-Content, so a chunk retried after a host-side timeout - rewrites the same part rather than appending it twice), joined and decoded on the VM, then - run with Windows PowerShell under a process-scoped execution policy bypass. Talks to the - Proxmox host over key-based SSH; the VM needs the guest agent. + The guest agent's command line fails silently above a few KB, so the script is written to + the VM through the agent's file-write call (Send-GuestFile in GuestAgent.ps1: numbered + base64 part files, joined and hashed on the VM and the hash compared here), then run with + Windows PowerShell under a process-scoped execution policy bypass. Talks to the Proxmox host + over key-based SSH; the VM needs the guest agent. .PARAMETER VmId The Proxmox VM id. @@ -52,9 +52,9 @@ .NOTES Author: Jeffrey Stuhr. The guest agent calls are GuestAgent.ps1's: each command is started - once and its result asked for by process id, so a status call that times out is asked - again without running the script a second time. A start call that times out is retried and - can run it twice. + once and its result asked for by process id. A status call that times out on the host can + take the finished result with it, and the command is then run again, up to three times, so + the script has to be safe to run twice. #> [CmdletBinding()] # ProxmoxHost is read inside Invoke-GuestPowerShell (GuestAgent.ps1); the analyzer cannot see that diff --git a/Validation/Invoke-ValidationRound.ps1 b/Validation/Invoke-ValidationRound.ps1 index 3d682a9..46cdd4c 100644 --- a/Validation/Invoke-ValidationRound.ps1 +++ b/Validation/Invoke-ValidationRound.ps1 @@ -615,8 +615,23 @@ function Invoke-Trigger { [CmdletBinding(SupportsShouldProcess)] param() if ($PSCmdlet.ShouldProcess("VM $VmId", 'Restart IntuneManagementExtension')) { - Invoke-GuestPowerShell -Script ('Restart-Service -Name IntuneManagementExtension -Force; (Get-Service ' + - 'IntuneManagementExtension).Status') + # Restart-Service reports Running and restarts nothing: the stop fails and the process keeps + # its start time (round 11, VM 126). Stopping the service's process and then starting the + # service fetched new assignments within ten minutes. Both start times are printed, so a + # restart that did not happen shows as an unchanged Before/After + Invoke-GuestPowerShell -Script @' +$name = 'IntuneManagementExtension' +$service = Get-CimInstance -ClassName Win32_Service -Filter "Name='$name'" +$before = if ($service.ProcessId) { (Get-Process -Id $service.ProcessId).StartTime.ToString('o') } +Stop-Service -Name $name -Force -ErrorAction SilentlyContinue +if ($service.ProcessId -and (Get-Process -Id $service.ProcessId -ErrorAction SilentlyContinue)) { + Stop-Process -Id $service.ProcessId -Force +} +Start-Service -Name $name +$service = Get-CimInstance -ClassName Win32_Service -Filter "Name='$name'" +$after = if ($service.ProcessId) { (Get-Process -Id $service.ProcessId).StartTime.ToString('o') } +"Status=$((Get-Service -Name $name).Status) Before=$before After=$after" +'@ } } diff --git a/Validation/New-IntuneLabVm.ps1 b/Validation/New-IntuneLabVm.ps1 index 1b657fb..006af27 100644 --- a/Validation/New-IntuneLabVm.ps1 +++ b/Validation/New-IntuneLabVm.ps1 @@ -42,14 +42,15 @@ function Invoke-Qm { $out } +# The guest agent calls are GuestAgent.ps1's; Invoke-GuestPowerShell reads $ProxmoxHost and $VmId +# from this scope +. (Join-Path -Path $PSScriptRoot -ChildPath 'GuestAgent.ps1') + function Invoke-Guest { - # Runs PowerShell inside the VM via the guest agent; returns stdout or $null if the agent isn't up. + # Runs PowerShell inside the VM; $null while the agent is not up, which the OOBE wait polls on param([Parameter(Mandatory)][string]$Script) - $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($Script)) - $raw = ssh -o BatchMode=yes $ProxmoxHost ("qm guest exec $VmId --timeout 60 -- powershell -NoProfile " + - "-NonInteractive -EncodedCommand $encoded") 2>$null - if ($LASTEXITCODE -ne 0 -or -not $raw) { return $null } - (($raw -join "`n") | ConvertFrom-Json).'out-data' + try { Invoke-GuestPowerShell -Script $Script -TimeoutSeconds 60 -WarningAction SilentlyContinue } + catch { $null } } function Wait-GuestAtOobe { diff --git a/Validation/README.md b/Validation/README.md index 47ecc36..6854522 100644 --- a/Validation/README.md +++ b/Validation/README.md @@ -35,8 +35,7 @@ with the documented behaviour next to the observed one. - An app registration with certificate auth and these application permissions: `DeviceManagementScripts.ReadWrite.All`, `DeviceManagementConfiguration.ReadWrite.All`, `DeviceManagementApps.ReadWrite.All`, `DeviceManagementManagedDevices.Read.All`, - `Group.ReadWrite.All`, `Device.Read.All`. - `Scripts\Entra\Grant-ClaudeDevTenantPermissions.ps1` grants them. + `Group.ReadWrite.All`, `Device.Read.All`, granted as application permissions with admin consent. - A Proxmox host reachable over key-based SSH, a sysprepped Windows 11 Enterprise/Pro template with the QEMU guest agent installed, and a licensed test user (Windows E3/E5 for remediations). - `Tools\IntuneWinAppUtil.exe` (only for the Win32 experiments). `Deploy` downloads it from @@ -50,7 +49,7 @@ remediations are assigned hourly and will keep running on whatever is in the gro ```powershell $conn = @{ TenantId = ''; ClientId = ''; CertificateThumbprint = '' } -cd Scripts\Intune\IntuneScriptLab\Validation +cd Validation # 1. Test VM. Ends at the OOBE account screen with a clean-oobe snapshot. .\New-IntuneLabVm.ps1 -TemplateId 123 -VmId 125 -Name INTUNE-TEST-02