diff --git a/CHANGELOG.md b/CHANGELOG.md index 381c5bf..1b63836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ The format follows [Conventional Commits](https://www.conventionalcommits.org/) ## Unreleased +### Fixes + +- **Deprecated settings carry the configured value by its Intune name** (`False` instead of `com.apple.applicationaccess_allowgenmoji_false`). The Maester pack shows it; the HTML report does not show this column. + ## [1.3.0] - 2026-10-26 Release for the Intune Anomalies report. diff --git a/Tests/IntuneAnomalyCore.Tests.ps1 b/Tests/IntuneAnomalyCore.Tests.ps1 index 3c7df50..366f735 100644 --- a/Tests/IntuneAnomalyCore.Tests.ps1 +++ b/Tests/IntuneAnomalyCore.Tests.ps1 @@ -501,6 +501,27 @@ Describe 'Resolvers' { @($r.Rows).SettingDisplayName | Should -Be @('Old thing (Deprecated)') @($r.Rows)[0].DetectionSource | Should -Be 'CatalogDisplayName' } + It 'Deprecated: shows the option label as the configured value, else the part after the definition id, and keeps simple values' { + $r = InModuleScope RKSolutions { + $choice = '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance' + $policy = [pscustomobject]@{ id = 'p1'; name = 'Pol'; platforms = 'macOS'; settings = @( + [pscustomobject]@{ + settingInstance = [pscustomobject]@{ '@odata.type' = $choice; settingDefinitionId = 'def_a'; choiceSettingValue = [pscustomobject]@{ value = 'def_a_false' } } + settingDefinitions = @([pscustomobject]@{ id = 'def_a'; displayName = 'A (Deprecated)'; options = @([pscustomobject]@{ itemId = 'def_a_false'; displayName = 'False' }) }) + } + [pscustomobject]@{ + settingInstance = [pscustomobject]@{ '@odata.type' = $choice; settingDefinitionId = 'def_b'; choiceSettingValue = [pscustomobject]@{ value = 'def_b_1' } } + settingDefinitions = @([pscustomobject]@{ id = 'def_b'; displayName = 'B (Deprecated)' }) + } + [pscustomobject]@{ + settingInstance = [pscustomobject]@{ '@odata.type' = '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance'; settingDefinitionId = 'def_c'; simpleSettingValue = [pscustomobject]@{ value = 'def_c_custom' } } + settingDefinitions = @([pscustomobject]@{ id = 'def_c'; displayName = 'C (Deprecated)' }) + }) } + $d = [pscustomobject]@{ TenantName = 'T'; SecurityKeyContext = [pscustomobject]@{ ConfigurationPolicies = @($policy) }; Warnings = @() } + Resolve-IntuneDeprecatedAnomalies -Data $d + } + @($r.Rows).ConfiguredValue | Should -Be @('False', '1', 'def_c_custom') + } } Describe 'Unreadable keys, settings fallback and null children' { diff --git a/module/Core/IntuneAnomalyResolvers.ps1 b/module/Core/IntuneAnomalyResolvers.ps1 index 6e73ec6..7541fd2 100644 --- a/module/Core/IntuneAnomalyResolvers.ps1 +++ b/module/Core/IntuneAnomalyResolvers.ps1 @@ -454,6 +454,25 @@ function Get-IntuneSettingValueString { } } +function Get-IntuneSettingValueLabel { + <# + Turns a choice value such as com.apple.applicationaccess_allowgenmoji_false into its label from the + expanded setting definition ("False"), or into the part after the definition id ("false") when the + definition has no label for it. Other values come back as they are. Collection values are mapped one by one. + #> + [CmdletBinding()] + [OutputType([string])] + param([string] $DefinitionId, [string] $Value, [hashtable] $Catalog) + + $options = if ($Catalog -and $DefinitionId -and $Catalog.ContainsKey($DefinitionId)) { $Catalog[$DefinitionId].Options } + $labels = foreach ($v in ($Value -split '; ')) { + if ($options -and $options[$v]) { $options[$v] } + elseif ($DefinitionId -and $v.StartsWith("${DefinitionId}_")) { $v.Substring($DefinitionId.Length + 1) } + else { $v } + } + return (@($labels) -join '; ') +} + function Get-IntuneSettingDeprecations { <# Recursively walk a settingInstance (and any nested group children) and @@ -476,7 +495,8 @@ function Get-IntuneSettingDeprecations { $hits.Add([PSCustomObject]@{ DefinitionId = $defId DisplayName = if ([string]::IsNullOrWhiteSpace($check.DisplayName)) { $defId } else { $check.DisplayName } - Value = $value + # Only choice values are option ids; a simple value is what the admin typed and stays as it is. + Value = if ([string]$SettingInstance.'@odata.type' -like '*choiceSetting*') { Get-IntuneSettingValueLabel -DefinitionId $defId -Value $value -Catalog $Catalog } else { $value } Source = $check.Source }) } @@ -513,7 +533,13 @@ function Resolve-IntuneDeprecatedAnomalies { $catalog = @{} foreach ($policy in $Context.ConfigurationPolicies) { foreach ($s in @($policy.settings)) { - foreach ($def in @($s.settingDefinitions)) { if ($def.id -and -not $catalog.ContainsKey([string]$def.id)) { $catalog[[string]$def.id] = [pscustomobject]@{ DisplayName = [string]$def.displayName } } } + foreach ($def in @($s.settingDefinitions)) { + if (-not $def.id -or $catalog.ContainsKey([string]$def.id)) { continue } + # Choice definitions carry their options: itemId (the configured value) -> displayName (its label). + $options = @{} + foreach ($o in @($def.options)) { if ($o.itemId) { $options[[string]$o.itemId] = [string]$o.displayName } } + $catalog[[string]$def.id] = [pscustomobject]@{ DisplayName = [string]$def.displayName; Options = $options } + } } } $out = [System.Collections.Generic.List[PSObject]]::new() diff --git a/module/Private/IntuneAnomalies.ps1 b/module/Private/IntuneAnomalies.ps1 index af49608..cfb8d95 100644 --- a/module/Private/IntuneAnomalies.ps1 +++ b/module/Private/IntuneAnomalies.ps1 @@ -188,10 +188,9 @@ function New-IntuneAnomaliesHTMLReport { } # Generate table rows for deprecated Settings Catalog settings. - # Customer / ConfiguredValue / DetectionSource intentionally omitted - they're - # either constant (Customer) or redundant with the Setting Definition ID for - # the common choice-setting case where Value == DefId + "_". The full - # record still carries those fields for downstream consumers. + # Customer / ConfiguredValue / DetectionSource intentionally omitted - Customer + # is constant here. The full record still carries those fields for downstream + # consumers such as the Maester pack, which shows ConfiguredValue. $deprecatedSettingsRows = "" foreach ($item in $Report_DeprecatedSettings) { $deprecatedSettingsRows += @"