Skip to content

v4.3: Fix issues #123, #124, #125, #126 - #127

Merged
ugurkocde merged 20 commits into
mainfrom
version-4.3
Jul 3, 2026
Merged

v4.3: Fix issues #123, #124, #125, #126#127
ugurkocde merged 20 commits into
mainfrom
version-4.3

Conversation

@ugurkocde

Copy link
Copy Markdown
Owner

Resolves #123, #124, #126. Addresses #125 (see the verification note below on why the intents fallback is retained).

Summary

#126 - Excluded apps now shown in group checks

Get-IntuneGroupAssignment collected exclusion reasons for apps but dropped the app when no inclusion assignment supplied an intent. The excluding assignment's intent is now used as a fallback, so exclusion-only apps appear in their intent bucket with reason "Group Exclusion" / "Inherited Exclusion (via X)". Compare-IntuneGroupAssignment marks excluded apps with the [EXCLUDED] suffix used by its other sections. Verified against live tenant data: a group excluded from a required app now appears under Required Apps.

#123 - All 30 findings addressed, including the decomposition

  • The ten near-identical category-walk cmdlets now run on a shared scan engine (Private/Invoke-IntuneCategoryScan.ps1 plus a per-audience category registry). Combined size of the ten cmdlets: 8409 lines down to 3172, with each file reduced to target resolution, a per-cmdlet predicate callback, display, and export.
  • Cross-cutting fixes now live once in shared code: entity sets fetched once per run (configurationPolicies dropped from up to 14 fetches to 1 per run), per-category failures continue the run and populate the error stream, progress totals derive from the registry, the App Protection URI switch exists exactly once, pagination accumulates via List instead of array +=.
  • Bug fixes: Switch-Tenant connected with empty permission scopes; group memberships, comparison categories, and assignment failures silently truncated past 100 items; App Protection policies shown for every user regardless of membership; app intent misclassification from a stale loop variable; multi-device input collapsed into a single bogus lookup; nested export calls produced junk CSV rows; Get-IntuneEmptyGroup never actually tested App Protection groups for emptiness and never populated its six endpoint security categories; EDR policies missing from HTML reports due to a template mapping typo; Set-Environment killed the host session; guest UPNs with # broke lookups.
  • Security hardening: HTML-encoding for all Graph-derived report values, OData filter escaping for group names, -ClientSecretCredential (PSCredential) as the preferred client secret input (plain-string -ClientSecret retained for compatibility), register script no longer leaves a temporary client secret behind on failed certificate upload.

#124 - Docs and register script aligned

README auth options (adds pre-fetched token and client secret credential), user-and/or-device wording for options 12/13, register script grants all nine validated permissions plus documented DeviceManagementServiceConfig.Read.All, and its next-steps output uses the module cmdlets. Permission GUIDs were verified against the Microsoft Graph service principal appRoles via live Graph calls; two GUIDs previously suggested in the issue comments (DeviceManagementScripts.Read.All, DeviceManagementRBAC.Read.All) were wrong and are corrected here.

#125 - Intents fallback retained deliberately

Live verification before removal found an assigned macOS FileVault policy that exists only as a deviceManagement/intent, with no configurationPolicies counterpart: the MC955748 auto-migration covered Windows endpoint security policies only. Removing the fallback would silently drop live macOS legacy policies from every report. Instead, the fallback is centralized in the scan engine (intents and templates fetched once per run instead of six-plus times per cmdlet), which delivers the API-call reduction the issue aimed for without the regression. Removal can be revisited when Microsoft migrates the remaining macOS intents.

Verification

  • Pester: 157/157 (44 baseline plus 113 new engine/cmdlet tests)
  • PSScriptAnalyzer: clean with repo settings (Error severity)
  • Every migrated cmdlet verified against a stubbed old-vs-new harness with byte-identical console and CSV output (only accepted difference: numbered progress lines)
  • The [FEATURE] Support for Exclude Groups in applications #126 fix verified with real captured tenant payloads before and after the engine migration
  • Every distinct Graph URI family the module builds was executed against a live tenant (entity lists, per-entity assignments for configurationPolicies, managed app protections, and intents, transitive membership, and the Cloud PC 403 path that the engine treats as an optional-feature skip)

ugurkocde added 20 commits July 3, 2026 00:24
…exit, caching, UPN encoding (#123)

- Promote required permissions to module scope; fix Switch-Tenant connecting with empty scopes (F11)
- Refresh assignment filter lookup and clear group cache on tenant switch (F24)
- Paginate Get-GroupMemberships transitiveMemberOf results (F12)
- Set-Environment returns null instead of killing the PowerShell session (F15)
- Cache Get-GroupInfo lookups per tenant (F26)
- URL-encode UPNs so guest accounts with # resolve (F27)
- Paginate Get-AssignmentFailures policy and status fetches (F20)
- Export-ResultsIfRequested takes ExportToCSV/ParameterMode as explicit parameters (F17)
…tent misclassification, broken exports, empty-group checks, pagination (#123)

- Escape single quotes in group-name OData filters (F9)
- App Protection policies now filtered by actual group membership instead of undefined GroupId (F13)
- Use tracked winning assignment for app intent classification (F14)
- Fix nested Add-ExportData parenthesization producing junk CSV rows in three cmdlets; fix wrong Device entry and invalid -GetExtra argument in group export (F22)
- Get-IntuneEmptyGroup: App Protection section now actually tests for empty groups; populate the six endpoint security categories that were displayed but never fetched; add missing Account Protection display section (F18, F19)
- Compare-IntuneGroupAssignment: paginate Settings Catalog, Compliance, and Apps fetches (F25)
- Update-IntuneSettingDefinition writes to LocalApplicationData instead of the module install directory; Search-IntuneSetting prefers the user copy (F28)
Enables common parameters (-Verbose, -ErrorAction) and PSCmdlet error
handling in private helpers. Verified no caller-scope variable reads
break under advanced-function scoping.
…es missing from HTML report (#123)

- Add -ClientSecretCredential (PSCredential) to Connect/Invoke, preferred over plain-string -ClientSecret which is retained for compatibility (F6)
- Derive version banner from module metadata instead of a duplicated literal (F29)
- HTML-encode all Graph-derived values in HTML report table cells (F21)
- Remove html-export.ps1 redefinitions of module functions and variables; rename its divergent Get-AssignmentInfo to Get-HtmlAssignmentInfo; the removed local template mapping carried a typo that silently dropped EDR policies from HTML reports (F23)
- Register script: cross-platform temp path and cleanup of the temporary client secret when certificate upload fails (F30)
Get-IntuneGroupAssignment collected exclusion reasons for apps but only
added an app to the output when an inclusion assignment supplied the
intent, so exclusion-only apps were silently dropped. The intent of the
excluding assignment is now used as a fallback. Compare-IntuneGroupAssignment
now marks excluded apps with the [EXCLUDED] suffix used by its other
sections.

Verified against live tenant data: a group excluded from a required app
now appears under Required Apps with reason Group Exclusion.
- Replace stale auto-update claim with the actual PSGallery version check
- Document -AccessToken (Option 4: pre-fetched token) and -ClientSecretCredential auth paths
- Options 12/13 and cmdlet reference now say user and/or device; add -DeviceNames examples
- Register script grants all 9 validated permissions (GUIDs verified against the Graph service principal) plus documented DeviceManagementServiceConfig.Read.All
- Register script next-steps output uses the module cmdlets instead of the removed v3 script
…n URI helper (#123)

Foundations for decomposing the ten near-identical category-walk cmdlets:
- Get-IntuneCategoryDefinition: per-audience category registry; progress totals derive from the list, audience-specific settings catalog filters preserved verbatim
- Invoke-IntuneCategoryScan: shared fetch/error/progress engine; per-category failures continue the run and populate the error stream; entity sets cached once per run including the endpoint security intents fallback (retained deliberately: live verification found assigned macOS intents with no configurationPolicies counterpart)
- Get-AppProtectionAssignmentUri: single home for the per-type assignments URI switch, now also used by Get-IntuneAssignments
- 27 new Pester tests covering caching, dedup, error contracts, and label duality
613 -> 144 lines. Adds Add-CategoryExportData, the registry-driven export
helper. Legacy output quirks reproduced verbatim (ES intent wording,
Autopilot/ESP summary format, App Protection All Devices drop). Entity
sets now fetched once per run instead of up to 7 times.
544 -> 285 lines. Name matching runs as an entity prefilter so assignment
fetches still happen only for matched policies (mock-verified: identical
fetch sets old vs new). Console and CSV output byte-identical on fixtures.
…ne (#123)

699 -> 188 lines. All Users predicate via Get-AllTargetReason preserved,
including single-intent app copy and ES-in-Settings-Catalog duplication.
CSV output byte-identical on fixtures; progress lines now numbered.
…gine (#123)

682 -> 158 lines. All Devices predicate preserved including first-match
intent for apps and legacy double-listing of ES policies under Settings
Catalog. CSV byte-identical on fixtures.
…123)

688 -> 257 lines. Adds Show-CategoryResultTable, the shared table renderer
with the legacy color rules. The excluded-apps behavior from #126 is
preserved and re-verified against captured live tenant payloads (identical
CSV before and after migration). 13 new tests.
…123)

1440 -> 381 lines. Entity sets now fetched once per run instead of per
user (configurationPolicies: 14 fetches -> 1 for two users). App
Protection membership filtering (F13), winning-assignment intent (F14),
and excluded-app display preserved and regression-tested. Export rows
byte-identical on fixtures. 9 new tests.
…#123)

1015 -> 395 lines. Platform compatibility filtering, Windows-conditional
Autopilot/ESP/Windows 365 fetching, F14 intent selection, and excluded-app
display all preserved and fixture-verified (byte-identical output for
Windows and macOS devices). Entity fetches now once per run.

Also fixes multi-device input, which was broken: the local split result
was assigned into the type-constrained DeviceNames parameter variable,
collapsing 'PC-1,PC-2' into a single bogus lookup. 12 new tests.
…ine (#123)

699 -> 435 lines. Legacy semantics preserved exactly: intents-only
endpoint security scope (prefiltered), per-assignment app entries with
EXCLUDED/INHERITED/filter suffixes, shell scripts kept as a local fetch
for their divergent groupAssignments endpoint. CSV and matrix output
byte-identical on fixtures; Graph calls 58 -> 40 for a two-group run.
15 new tests.
…#123)

1035 -> 449 lines. Simulated-membership delta logic unchanged; user-only,
device-only, combined, and scope-tag runs all byte-identical on fixtures.
Entity sets fetched once per run (configurationPolicies 7 -> 1). 12 new
tests.
)

1040 -> 480 lines. Removal-simulation delta logic unchanged; user, device,
and combined runs byte-identical on fixtures. Entity fetches drop from 46
to 35 Graph calls in the smoke run. 18 new tests.
Sweeps the non-migrated files (Get-IntuneEmptyGroup, Get-IntuneUnassignedPolicy,
Get-IntuneUserDeviceAssignment, Test cmdlet group lookup, html-export) so no
bare GraphEndpoint interpolation remains anywhere in the module.
Get-IntuneUnassignedPolicy, Get-IntuneUserDeviceAssignment, and
html-export.ps1 now use Get-AppProtectionAssignmentUri, List-based
pagination, and protected category fetches that continue on failure.
Get-IntuneUserDeviceAssignment fetches configurationPolicies and intents
once per run instead of 13 times.
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

PSScriptAnalyzer Results

Found 0 error(s) and 5 warning(s).

See the workflow run for details.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant