Title
Set-IntuneWin32App -RequirementRule is unusable and silently terminates the caller (1.5.0)
Labels (suggested)
bug
Body
Summary
In IntuneWin32App 1.5.0, Set-IntuneWin32App -RequirementRule can never succeed, and its
failure silently terminates the calling script with exit code 0.
New-IntuneWin32AppRequirementRule is the only public function that builds a requirement rule,
and it never emits an @odata.type key. Set-IntuneWin32App requires that key and responds to
its absence with Write-Warning followed by a bare break. So the parameter cannot be used at
all, and the way it fails hides the failure.
This bit us in CI: a GitHub Actions job that updates a Win32 app finished green while nothing
had been updated in Intune.
Repro
No tenant or network needed — the validation runs before any Graph call:
Import-Module IntuneWin32App -RequiredVersion 1.5.0
$rule = New-IntuneWin32AppRequirementRule -Architecture "AllWithARM64" -MinimumSupportedWindowsRelease "W10_1607"
$rule.Contains("@odata.type") # False
Connect-MSIntuneGraph -TenantID $TenantId -ClientID $ClientId -ClientSecret $ClientSecret
Set-IntuneWin32App -ID $AppId -AppVersion "2.0.0" -RequirementRule $rule
Write-Host "THIS LINE IS NEVER REACHED"
Expected: the app is updated, or an error is raised.
Actual: WARNING: RequirementRule is missing required '@odata.type' property. and then the script
stops. Write-Host never runs, the app is not updated, $? stays $true and the exit code is 0.
The break behaviour on its own, without the module:
# Fake.psm1
function Set-FakeApp {
param([hashtable]$RequirementRule)
if (-not $RequirementRule.ContainsKey("@odata.type")) {
Write-Warning -Message "RequirementRule is missing required '@odata.type' property."
break
}
return "patched"
}
$ErrorActionPreference = "Stop"
Import-Module ./Fake.psm1 -Force
Set-FakeApp -RequirementRule @{ arch = "x64" }
Write-Host "NEVER REACHED"
break is not inside a loop, so PowerShell unwinds until it finds an enclosing loop anywhere in
the call stack; with none, it ends the calling script. No exception, exit code 0.
Why it matters beyond the missing key
- The whole update is skipped. The requirement-rule validation in
Set-IntuneWin32App runs
before the rest of the request body is assembled, so metadata, detection rules and command
lines passed in the same call are not patched either. No PATCH reaches Graph.
- Automation cannot detect the failure. Because of the bare
break, a pipeline sees success.
In our case Update-IntuneWin32AppPackageFile had already run, so the new .intunewin was
uploaded while the app metadata and detection rule stayed on the previous version — the worst
possible half-state, reported as success.
- Even a valid rule would be mostly ignored. With
@odata.type present,
Set-IntuneWin32App reads only minimumSupportedOperatingSystem,
minimumFreeDiskSpaceInMB, minimumMemoryInMB, minimumNumberOfProcessors and
minimumCpuSpeedInMHz from the rule. allowedArchitectures, applicableArchitectures and
minimumSupportedWindowsRelease never make it into the body, and
minimumSupportedOperatingSystem is exactly the key that
New-IntuneWin32AppRequirementRule stopped producing in 1.0.3 (2022). Compare
New-IntuneWin32AppBody, which handles the modern properties correctly on the create path.
Suggested fix
- Either have
New-IntuneWin32AppRequirementRule include
@odata.type = "#microsoft.graph.win32LobApp", or drop that validation in
Set-IntuneWin32App for the base requirement rule.
- Have
Set-IntuneWin32App put the architecture and minimumSupportedWindowsRelease
properties into the PATCH body, the way New-IntuneWin32AppBody does for
Add-IntuneWin32App.
- Replace
Write-Warning + break with throw or Write-Error -ErrorAction Stop so a
validation failure does not silently unwind the caller. The pattern appears in many places,
including Add-IntuneWin32App, Set-IntuneWin32App and the
Add-IntuneWin32AppAssignment* functions — for anyone driving the module from CI, every one
of them is a potential silent exit.
Two related findings
Found while tracing the above; happy to split these into separate issues if preferred.
Test-IntuneWin32AppAssignment (private) reads $Win32AppAssignments.value, but 1.5.0's
Invoke-MSGraphOperation returns collections directly without the .value wrapper (per the
1.5.0 release notes). The duplicate check therefore never finds an existing assignment, so
Add-IntuneWin32AppAssignmentAllDevices attempts a second assignment on every run.
Add-IntuneWin32AppAssignmentGroup calls Test-IntuneWin32AppAssignment -Target "Group"
without passing -GroupID, so the -like "" comparison is always false.
Environment
IntuneWin32App 1.5.0
- PowerShell 7.4 on
windows-latest (GitHub Actions), authenticated with
Connect-MSIntuneGraph client credentials
- Also reproduced on PowerShell 7.4.6 on Linux with a stub module for the
break behaviour
Generated by Claude Code
Title
Set-IntuneWin32App -RequirementRuleis unusable and silently terminates the caller (1.5.0)Labels (suggested)
bug
Body
Summary
In
IntuneWin32App1.5.0,Set-IntuneWin32App -RequirementRulecan never succeed, and itsfailure silently terminates the calling script with exit code 0.
New-IntuneWin32AppRequirementRuleis the only public function that builds a requirement rule,and it never emits an
@odata.typekey.Set-IntuneWin32Apprequires that key and responds toits absence with
Write-Warningfollowed by a barebreak. So the parameter cannot be used atall, and the way it fails hides the failure.
This bit us in CI: a GitHub Actions job that updates a Win32 app finished green while nothing
had been updated in Intune.
Repro
No tenant or network needed — the validation runs before any Graph call:
Expected: the app is updated, or an error is raised.
Actual:
WARNING: RequirementRule is missing required '@odata.type' property.and then the scriptstops.
Write-Hostnever runs, the app is not updated,$?stays$trueand the exit code is 0.The
breakbehaviour on its own, without the module:breakis not inside a loop, so PowerShell unwinds until it finds an enclosing loop anywhere inthe call stack; with none, it ends the calling script. No exception, exit code 0.
Why it matters beyond the missing key
Set-IntuneWin32Apprunsbefore the rest of the request body is assembled, so metadata, detection rules and command
lines passed in the same call are not patched either. No PATCH reaches Graph.
break, a pipeline sees success.In our case
Update-IntuneWin32AppPackageFilehad already run, so the new.intunewinwasuploaded while the app metadata and detection rule stayed on the previous version — the worst
possible half-state, reported as success.
@odata.typepresent,Set-IntuneWin32Appreads onlyminimumSupportedOperatingSystem,minimumFreeDiskSpaceInMB,minimumMemoryInMB,minimumNumberOfProcessorsandminimumCpuSpeedInMHzfrom the rule.allowedArchitectures,applicableArchitecturesandminimumSupportedWindowsReleasenever make it into the body, andminimumSupportedOperatingSystemis exactly the key thatNew-IntuneWin32AppRequirementRulestopped producing in 1.0.3 (2022). CompareNew-IntuneWin32AppBody, which handles the modern properties correctly on the create path.Suggested fix
New-IntuneWin32AppRequirementRuleinclude@odata.type = "#microsoft.graph.win32LobApp", or drop that validation inSet-IntuneWin32Appfor the base requirement rule.Set-IntuneWin32Appput the architecture andminimumSupportedWindowsReleaseproperties into the PATCH body, the way
New-IntuneWin32AppBodydoes forAdd-IntuneWin32App.Write-Warning+breakwiththroworWrite-Error -ErrorAction Stopso avalidation failure does not silently unwind the caller. The pattern appears in many places,
including
Add-IntuneWin32App,Set-IntuneWin32Appand theAdd-IntuneWin32AppAssignment*functions — for anyone driving the module from CI, every oneof them is a potential silent exit.
Two related findings
Found while tracing the above; happy to split these into separate issues if preferred.
Test-IntuneWin32AppAssignment(private) reads$Win32AppAssignments.value, but 1.5.0'sInvoke-MSGraphOperationreturns collections directly without the.valuewrapper (per the1.5.0 release notes). The duplicate check therefore never finds an existing assignment, so
Add-IntuneWin32AppAssignmentAllDevicesattempts a second assignment on every run.Add-IntuneWin32AppAssignmentGroupcallsTest-IntuneWin32AppAssignment -Target "Group"without passing
-GroupID, so the-like ""comparison is always false.Environment
IntuneWin32App1.5.0windows-latest(GitHub Actions), authenticated withConnect-MSIntuneGraphclient credentialsbreakbehaviourGenerated by Claude Code