Skip to content

Code Quality: inconsistent module-scope var usage, unprotected Graph calls, duplicated AppProtection URI logic, oversized functions #123

@ugurkocde

Description

@ugurkocde

Review of the v4.0 module in Module/IntuneAssignmentChecker/ (introduced in b2378c5). Findings are ordered by priority.

1. Error handling — unprotected Invoke-MgGraphRequest calls in main category loops

Several mandatory category fetches run bare Invoke-MgGraphRequest without a try/catch. A single transient network blip or throttling response aborts the entire user/device/group run partway through, leaving no partial results.

Representative locations:

  • Module/IntuneAssignmentChecker/Public/Get-IntuneUserAssignment.ps1:227-233 — Applications fetch + @odata.nextLink pagination.
  • Module/IntuneAssignmentChecker/Public/Get-IntuneUserAssignment.ps1:246-247 — per-app assignments fetch.
  • Same pattern repeats in Get-IntuneAllPolicies.ps1, Get-IntuneAllUsersAssignment.ps1, Get-IntuneAllDevicesAssignment.ps1, Get-IntuneDeviceAssignment.ps1, Get-IntuneGroupAssignment.ps1, Test-IntuneGroupMembership.ps1, Test-IntuneGroupRemoval.ps1, Search-IntunePolicy.ps1.

Compare with the app-protection block at Get-IntuneUserAssignment.ps1:162-207, which already wraps its request in try/catch and reports a per-policy error without aborting the run. The applications block should follow the same pattern (continue to the next category on failure, log the error).

2. Inconsistent $script:GraphEndpoint scoping — fragile

IntuneAssignmentChecker.psm1:5 declares $script:GraphEndpoint. Some files correctly reference $script:GraphEndpoint (Connect-IntuneAssignmentChecker.ps1, Set-Environment.ps1, Update-IntuneSettingDefinition.ps1), but many files read the bare $GraphEndpoint and rely on the scope-chain fallback to find the module-scoped value:

  • Module/IntuneAssignmentChecker/Private/Get-IntuneEntities.ps1:18,22
  • Module/IntuneAssignmentChecker/Public/Compare-IntuneGroupAssignment.ps1 — 22 occurrences
  • Module/IntuneAssignmentChecker/Public/Test-IntuneGroupMembership.ps1 — 12 occurrences
  • Module/IntuneAssignmentChecker/Public/Test-IntuneGroupRemoval.ps1 — 12 occurrences
  • Module/IntuneAssignmentChecker/Public/Get-IntuneAllDevicesAssignment.ps1 — 11
  • Module/IntuneAssignmentChecker/Public/Get-IntuneAllUsersAssignment.ps1 — 11
  • Plus 10 other Public/Private files.

This works today, but a local $GraphEndpoint = … introduced anywhere up the call stack would silently shadow the module value and quietly route traffic to the wrong cloud. Make all reads explicit: $script:GraphEndpoint.

3. Quality — $totalCategories magic number drifts from actual step count

Module/IntuneAssignmentChecker/Public/Get-IntuneUserAssignment.ps1:82 sets $totalCategories = 16. The $relevantPolicies hashtable immediately below (lines 86–107) declares 20 named categories, and there are 16 $currentCategory++ calls, so the progress bar is internally consistent but the relationship is invisible — exactly how commit b2378c5 recently had to re-adjust from 17 → 16.

The numbers also disagree between cmdlets that do essentially the same work:

  • Get-IntuneUserAssignment.ps1:8216
  • Test-IntuneGroupMembership.ps1:13918
  • Test-IntuneGroupRemoval.ps1:14018
  • Search-IntunePolicy.ps1:3318

Either derive the total from the list of steps ($steps.Count) or assemble the category list once and iterate, so the counter can't drift.

4. Quality — App Protection assignment URI switch duplicated across 11 files

The 3-case switch that maps @odata.typeandroid/ios/windowsManagedAppProtections/.../assignments appears in every cmdlet that walks App Protection policies:

  • Public/Get-IntuneUserAssignment.ps1:154-159
  • Public/Get-IntuneDeviceAssignment.ps1
  • Public/Get-IntuneGroupAssignment.ps1
  • Public/Get-IntuneAllPolicies.ps1
  • Public/Get-IntuneAllUsersAssignment.ps1
  • Public/Get-IntuneAllDevicesAssignment.ps1
  • Public/Get-IntuneEmptyGroup.ps1:106-108
  • Public/Get-IntuneUnassignedPolicy.ps1
  • Public/Search-IntunePolicy.ps1
  • Public/Test-IntuneGroupMembership.ps1
  • Public/Test-IntuneGroupRemoval.ps1
  • html-export.ps1:684-690

Extract a single Get-AppProtectionAssignmentUri -PolicyType -PolicyId private helper. Any future AppProtection subtype (e.g. mdmWindowsInformationProtection) then needs a one-place change.

5. Quality — oversized functions

PowerShell style guides commonly flag functions >200 lines as candidates for decomposition. Current Public/ sizes:

File Lines
Get-IntuneUserAssignment.ps1 1422
Get-IntuneDeviceAssignment.ps1 990
Test-IntuneGroupMembership.ps1 955
Test-IntuneGroupRemoval.ps1 947
Get-IntuneGroupAssignment.ps1 685
Get-IntuneAllUsersAssignment.ps1 685
Get-IntuneAllDevicesAssignment.ps1 668
Get-IntuneAllPolicies.ps1 667
Compare-IntuneGroupAssignment.ps1 665
Search-IntunePolicy.ps1 515

These files implement near-identical "walk all 16–18 category endpoints and collect matches" pipelines with small differences in the match predicate. Consider extracting a shared pipeline (list-of-categories → per-category Invoke-CategoryScan -Predicate {…}) into Private/ so each public cmdlet shrinks to the bit that differs: its predicate, its printing, and its export columns. This will also remove most of the duplicated $totalCategories/App-Protection URI/bare $GraphEndpoint instances in one pass.


Nothing here is a user-visible bug today, but the combination (fragile scoping, duplicated switches, unprotected network calls) is exactly what made the hardcoded graph.microsoft.com / totalCategories drift easy to miss until the USGov fix in #115.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions