Skip to content

Set-IntuneWin32App -RequirementRule is unusable and silently terminates the caller (1.5.0) #236

Description

@Jonasaaaa

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

  1. 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.
  2. 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.
  3. 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

  1. Either have New-IntuneWin32AppRequirementRule include
    @odata.type = "#microsoft.graph.win32LobApp", or drop that validation in
    Set-IntuneWin32App for the base requirement rule.
  2. Have Set-IntuneWin32App put the architecture and minimumSupportedWindowsRelease
    properties into the PATCH body, the way New-IntuneWin32AppBody does for
    Add-IntuneWin32App.
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions