Private/Logic/ConvertTo-ExtractedSettings.ps1
|
# Copyright (c) 2026 Sandy Zeng. All rights reserved. # Source-available. All rights reserved. See LICENSE file. <# ConvertTo-ExtractedSettings.ps1 — Extracts a flat setting map from a Settings Catalog or Security Baseline policy. Author: Sandy Zeng Project: IntuneDiff Version History: 1.0.0 Initial release. #> function ConvertTo-ExtractedSettings { <# .SYNOPSIS Extracts a flat map of settings from a Settings Catalog or Security Baseline policy. .DESCRIPTION PowerShell port of backend/src/utils/settingsExtractor.ts (Settings Catalog path). Returns an ordered hashtable: settingDefinitionId -> { Name, Value, Description, Category }. .PARAMETER SettingsArray Array of raw setting instances from Graph (.settings on configurationPolicies or .settingTemplates on configurationPolicyTemplates). #> [CmdletBinding()] [OutputType([System.Collections.Specialized.OrderedDictionary])] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [object]$SettingsArray ) $map = [ordered]@{} if (-not $SettingsArray) { return $map } foreach ($setting in $SettingsArray) { # Handle pre-extracted intent settings if ($setting -is [hashtable] -and $setting['__intent']) { $sid = [string]$setting['settingId'] $map[$sid] = @{ Name = $setting['displayName'] Value = $setting['value'] Description = $setting['description'] Category = $setting['category'] Raw = $setting['rawValue'] } continue } Convert-SingleSetting -Setting $setting -Map $map -Root $setting } return $map } function Convert-SingleSetting { <# .SYNOPSIS Processes one raw Graph setting entry and adds its resolved key-value pair to the output map. #> [CmdletBinding()] param( [Parameter(Mandatory)] $Setting, [Parameter(Mandatory)] [System.Collections.Specialized.OrderedDictionary]$Map, $Root = $null ) if ($null -eq $Root) { $Root = $Setting } # Detect kind $isSecurityBaseline = $null -ne $Setting.settingInstanceTemplate if ($isSecurityBaseline) { $settingInstance = $Setting.settingInstanceTemplate $settingDefinitions = @($Setting.settingDefinitions) $settingDefId = $null if ($settingInstance -and $settingInstance.settingDefinitionId) { $settingDefId = [string]$settingInstance.settingDefinitionId } elseif ($Setting.id) { $settingDefId = [string]$Setting.id } } else { $settingInstance = $Setting.settingInstance $settingDefinitions = @($Setting.settingDefinitions) $settingDefId = if ($settingInstance) { [string]$settingInstance.settingDefinitionId } else { $null } } if (-not $settingInstance -or $settingDefinitions.Count -eq 0) { return } $definition = $settingDefinitions | Where-Object { $_.id -eq $settingDefId } | Select-Object -First 1 if (-not $definition) { $definition = $settingDefinitions[0] } # Special case: Defender ASR rules $asrId = 'device_vendor_msft_policy_config_defender_attacksurfacereductionrules' if ($settingDefId -and $settingDefId.ToLowerInvariant() -eq $asrId) { if ($isSecurityBaseline) { $collectionTemplate = @($settingInstance.groupSettingCollectionValueTemplate) foreach ($tmpl in $collectionTemplate) { foreach ($child in @($tmpl.children)) { $childDefId = [string]$child.settingDefinitionId if (-not $childDefId) { continue } $childDef = $settingDefinitions | Where-Object { $_.id -eq $childDefId } | Select-Object -First 1 $dv = $child.choiceSettingValueTemplate.defaultValue $ruleVal = if ($dv -and $dv.settingDefinitionOptionId) { [string]$dv.settingDefinitionOptionId } else { '[Not Configured]' } if ($childDef -and $ruleVal) { $ruleVal = Format-SettingValue -Value $ruleVal -Definition $childDef } $ruleName = if ($childDef -and $childDef.displayName) { [string]$childDef.displayName } else { '' } $Map[$childDefId] = [pscustomobject]@{ Name = "Defender > $ruleName" Value = $ruleVal Description = if ($childDef) { [string]$childDef.description } else { '' } Category = [string]$definition.categoryId Raw = $Root } } } } else { $rules = Get-AsrRulesFromGroupCollection ` -GroupCollection @($settingInstance.groupSettingCollectionValue) ` -SettingDefinitions $settingDefinitions foreach ($rule in $rules) { $Map[$rule.Id] = [pscustomobject]@{ Name = $rule.Name Value = $rule.Value Description = '' Category = [string]$definition.categoryId Raw = $Root } } } return } # Special case: Local Users And Groups $localGroupsId = 'device_vendor_msft_policy_config_localusersandgroups_configure' if ($settingDefId -and $settingDefId.ToLowerInvariant() -eq $localGroupsId) { $collection = if ($isSecurityBaseline) { @($settingInstance.groupSettingCollectionValueTemplate) } else { @($settingInstance.groupSettingCollectionValue) } foreach ($groupItem in $collection) { $topChildren = @($groupItem.children) # The actual access group entries may be nested inside a GroupSettingCollectionInstance child $accessGroupChild = $topChildren | Where-Object { $_.'@odata.type' -like '*GroupSettingCollectionInstance' } | Select-Object -First 1 $accessGroups = if ($accessGroupChild -and $accessGroupChild.groupSettingCollectionValue) { @($accessGroupChild.groupSettingCollectionValue) } else { # Fallback: treat topChildren as a single access group @($groupItem) } foreach ($accessGroup in $accessGroups) { $children = @($accessGroup.children) $groupName = '' $action = '' $userType = '' $users = @() foreach ($child in $children) { $cid = [string]$child.settingDefinitionId if (-not $cid) { continue } $cidLower = $cid.ToLowerInvariant() $childDef = $settingDefinitions | Where-Object { $_.id -eq $cid } | Select-Object -First 1 if ($cidLower.Contains('_desc')) { # Can be choiceSettingValue or choiceSettingCollectionValue if ($child.choiceSettingCollectionValue) { $vals = @($child.choiceSettingCollectionValue) $friendlyVals = foreach ($cv in $vals) { if ($cv.value) { Format-SettingValue -Value $cv.value -Definition $childDef } } $friendlyVals = @($friendlyVals | Where-Object { $null -ne $_ }) if ($friendlyVals.Count -gt 0) { $groupName = $friendlyVals -join ', ' } } elseif ($child.choiceSettingValue -and $child.choiceSettingValue.value) { $groupName = Format-SettingValue -Value $child.choiceSettingValue.value -Definition $childDef } } elseif ($cidLower.Contains('_action')) { if ($child.choiceSettingValue -and $child.choiceSettingValue.value) { $action = Format-SettingValue -Value $child.choiceSettingValue.value -Definition $childDef } } elseif ($cidLower.Contains('_userselectiontype')) { if ($child.choiceSettingValue -and $child.choiceSettingValue.value) { $userType = Format-SettingValue -Value $child.choiceSettingValue.value -Definition $childDef # Check for nested users in children of choiceSettingValue if ($child.choiceSettingValue.children) { foreach ($nestedChild in @($child.choiceSettingValue.children)) { $ncId = [string]$nestedChild.settingDefinitionId if ($ncId -and $ncId.ToLowerInvariant().Contains('_users')) { if ($nestedChild.simpleSettingCollectionValue) { $users = @($nestedChild.simpleSettingCollectionValue | ForEach-Object { $_.value } | Where-Object { $_ }) } elseif ($nestedChild.simpleSettingValue -and $nestedChild.simpleSettingValue.value) { $users = @([string]$nestedChild.simpleSettingValue.value) } } } } } } elseif ($cidLower.Contains('_users')) { if ($child.simpleSettingCollectionValue) { $users = @($child.simpleSettingCollectionValue | ForEach-Object { $_.value } | Where-Object { $_ }) } elseif ($child.simpleSettingValue -and $child.simpleSettingValue.value) { $users = @([string]$child.simpleSettingValue.value) } } } $parts = @() if ($groupName) { $parts += "Local Group: $groupName" } if ($action) { $parts += "Action: $action" } if ($userType) { $parts += "Type: $userType" } $parts += "Users/Groups: $(if ($users.Count -gt 0) { $users -join ', ' } else { 'None' })" $displayValue = $parts -join ' | ' $uniqueKey = "${settingDefId}:${groupName}" $Map[$uniqueKey] = [pscustomobject]@{ Name = 'Local Users And Groups > Group Configuration' Value = $displayValue Description = if ($definition) { [string]$definition.description } else { '' } Category = if ($definition) { [string]$definition.categoryId } else { '' } Raw = $Root } } } return } $odataType = [string]$settingInstance.'@odata.type' $configuredValue = $null switch -Wildcard ($odataType) { '*deviceManagementConfigurationChoiceSettingInstance' { if ($settingInstance.choiceSettingValue) { $configuredValue = $settingInstance.choiceSettingValue.value } if ($configuredValue) { $configuredValue = Format-SettingValue -Value $configuredValue -Definition $definition } # Recurse into child settings if ($settingInstance.choiceSettingValue -and $settingInstance.choiceSettingValue.children) { foreach ($child in $settingInstance.choiceSettingValue.children) { Convert-SingleSetting -Setting @{ settingInstance = $child settingDefinitions = $settingDefinitions } -Map $Map -Root $Root } } } '*deviceManagementConfigurationSimpleSettingInstance' { if ($settingInstance.simpleSettingValue -and $null -ne $settingInstance.simpleSettingValue.value) { $configuredValue = [string]$settingInstance.simpleSettingValue.value } } '*deviceManagementConfigurationGroupSettingCollectionInstance' { $collection = @($settingInstance.groupSettingCollectionValue) $hasChildren = $collection | Where-Object { $_.children -and ($_.children -is [array]) -and $_.children.Count -gt 0 } | Select-Object -First 1 if ($hasChildren) { # Check if this is a key-value collection (like Hardened UNC Paths) $isKeyValue = $collection | Where-Object { $children = @($_.children) ($children | Where-Object { $_.settingDefinitionId -and $_.settingDefinitionId.ToLowerInvariant().Contains('_key') }) -and ($children | Where-Object { $_.settingDefinitionId -and $_.settingDefinitionId.ToLowerInvariant().Contains('_value') }) } | Select-Object -First 1 # Check if this is a firewall rule or device control entity collection $isFirewallRule = $settingDefId -and $settingDefId.ToLowerInvariant().Contains('firewallrule') $isDeviceControl = $settingDefId -and $settingDefId.ToLowerInvariant().Contains('devicecontrol') if ($isKeyValue) { # Format as key-value pairs (one entry for the whole collection) $configuredValue = Format-CollectionValue -Collection $collection } elseif ($isDeviceControl) { # Device Control: each collection item has a nested _ruledata GroupSettingCollection $entityIndex = 0 foreach ($groupItem in $collection) { $children = @($groupItem.children) # Find the _ruledata nested GroupSettingCollectionInstance $ruleDataChild = $children | Where-Object { $_.settingDefinitionId -and $_.settingDefinitionId.ToLowerInvariant().Contains('_ruledata') -and $_.'@odata.type' -like '*GroupSettingCollectionInstance' } | Select-Object -First 1 # Also try to find name at top level first $topNameChild = $children | Where-Object { $_.settingDefinitionId -and $_.settingDefinitionId.ToLowerInvariant().Contains('_name') } | Select-Object -First 1 $ruleName = if ($topNameChild -and $topNameChild.simpleSettingValue -and $topNameChild.simpleSettingValue.value) { [string]$topNameChild.simpleSettingValue.value } else { $null } if ($ruleDataChild -and $ruleDataChild.groupSettingCollectionValue) { foreach ($ruleData in @($ruleDataChild.groupSettingCollectionValue)) { $ruleChildren = @($ruleData.children) # Find name inside ruledata if not found at top level if (-not $ruleName) { $innerNameChild = $ruleChildren | Where-Object { $_.settingDefinitionId -and $_.settingDefinitionId.ToLowerInvariant().Contains('_name') } | Select-Object -First 1 $ruleName = if ($innerNameChild -and $innerNameChild.simpleSettingValue -and $innerNameChild.simpleSettingValue.value) { [string]$innerNameChild.simpleSettingValue.value } else { "Rule $($entityIndex + 1)" } } foreach ($child in $ruleChildren) { $childDefId = [string]$child.settingDefinitionId if (-not $childDefId) { continue } $childDefIdLower = $childDefId.ToLowerInvariant() # Skip _id and _name fields if ($childDefIdLower.Contains('_id') -or $childDefIdLower.Contains('_name')) { continue } $childDef = $settingDefinitions | Where-Object { $_.id -eq $childDefId } | Select-Object -First 1 $childDisplayName = if ($childDef -and $childDef.displayName) { [string]$childDef.displayName } else { $childDefId.Split('_')[-1] } # Handle _entry as nested GroupSettingCollection (access entries) if ($childDefIdLower.Contains('_entry') -and $child.'@odata.type' -like '*GroupSettingCollectionInstance' -and $child.groupSettingCollectionValue) { $entryIdx = 0 foreach ($entry in @($child.groupSettingCollectionValue)) { $entryChildren = @($entry.children) $entryParts = @() foreach ($ec in $entryChildren) { $ecDefId = [string]$ec.settingDefinitionId if (-not $ecDefId) { continue } $ecDef = $settingDefinitions | Where-Object { $_.id -eq $ecDefId } | Select-Object -First 1 $ecName = if ($ecDef -and $ecDef.displayName) { [string]$ecDef.displayName } else { $ecDefId.Split('_')[-1] } $ecValue = $null if ($ec.choiceSettingValue -and $ec.choiceSettingValue.value) { $ecValue = Format-SettingValue -Value $ec.choiceSettingValue.value -Definition $ecDef } elseif ($ec.simpleSettingValue -and $null -ne $ec.simpleSettingValue.value) { $ecValue = [string]$ec.simpleSettingValue.value } elseif ($ec.choiceSettingCollectionValue) { $vals = @($ec.choiceSettingCollectionValue) | ForEach-Object { if ($_.value) { Format-SettingValue -Value $_.value -Definition $ecDef } } | Where-Object { $_ } $ecValue = if ($vals.Count -gt 0) { $vals -join ', ' } else { $null } } if ($ecValue) { $entryParts += "${ecName}: $ecValue" } } $entryValue = if ($entryParts.Count -gt 0) { $entryParts -join ' | ' } else { '[Empty Entry]' } $entryKey = "${settingDefId}:${ruleName}:Entry_$($entryIdx + 1)" $Map[$entryKey] = [pscustomobject]@{ Name = "Device Control > $ruleName > Access Entry $($entryIdx + 1)" Value = $entryValue Description = '' Category = [string]$definition.categoryId Raw = $Root } $entryIdx++ } continue } # Handle IncludedIdList/ExcludedIdList (GroupSettingCollectionInstance with groupid children) if ($child.'@odata.type' -like '*GroupSettingCollectionInstance' -and $child.groupSettingCollectionValue) { $groupIds = @() foreach ($grp in @($child.groupSettingCollectionValue)) { foreach ($gc in @($grp.children)) { if ($gc.simpleSettingValue -and $gc.simpleSettingValue.value) { $groupIds += [string]$gc.simpleSettingValue.value } } } if ($groupIds.Count -gt 0) { $uniqueKey = "${settingDefId}:${ruleName}:${childDefId}" $Map[$uniqueKey] = [pscustomobject]@{ Name = "Device Control > $ruleName > $childDisplayName" Value = $groupIds -join ', ' Description = if ($childDef) { [string]$childDef.description } else { '' } Category = [string]$definition.categoryId Raw = $Root } } continue } # Regular child settings $childValue = $null if ($child.choiceSettingValue -and $child.choiceSettingValue.value) { $childValue = Format-SettingValue -Value $child.choiceSettingValue.value -Definition $childDef } elseif ($child.simpleSettingValue -and $null -ne $child.simpleSettingValue.value) { $childValue = [string]$child.simpleSettingValue.value } elseif ($child.choiceSettingCollectionValue) { $vals = @($child.choiceSettingCollectionValue) | ForEach-Object { if ($_.value) { Format-SettingValue -Value $_.value -Definition $childDef } } | Where-Object { $_ } if ($vals.Count -gt 0) { $childValue = $vals -join ', ' } } if ($null -ne $childValue) { $uniqueKey = "${settingDefId}:${ruleName}:${childDefId}" $Map[$uniqueKey] = [pscustomobject]@{ Name = "Device Control > $ruleName > $childDisplayName" Value = $childValue Description = if ($childDef) { [string]$childDef.description } else { '' } Category = [string]$definition.categoryId Raw = $Root } } } } } else { # No _ruledata found - process children directly (fallback) if (-not $ruleName) { $ruleName = "Rule $($entityIndex + 1)" } foreach ($child in $children) { $childDefId = [string]$child.settingDefinitionId if (-not $childDefId) { continue } $childDefIdLower = $childDefId.ToLowerInvariant() if ($childDefIdLower.Contains('_id') -or $childDefIdLower.Contains('_name')) { continue } $childDef = $settingDefinitions | Where-Object { $_.id -eq $childDefId } | Select-Object -First 1 $childDisplayName = if ($childDef -and $childDef.displayName) { [string]$childDef.displayName } else { $childDefId.Split('_')[-1] } $childValue = $null if ($child.choiceSettingValue -and $child.choiceSettingValue.value) { $childValue = Format-SettingValue -Value $child.choiceSettingValue.value -Definition $childDef } elseif ($child.simpleSettingValue -and $null -ne $child.simpleSettingValue.value) { $childValue = [string]$child.simpleSettingValue.value } if ($null -ne $childValue) { $uniqueKey = "${settingDefId}:${ruleName}:${childDefId}" $Map[$uniqueKey] = [pscustomobject]@{ Name = "Device Control > $ruleName > $childDisplayName" Value = $childValue Description = if ($childDef) { [string]$childDef.description } else { '' } Category = [string]$definition.categoryId Raw = $Root } } } } $entityIndex++ } return # Don't create parent entry } elseif ($isFirewallRule) { # Each collection item is a named entity - process per child $entityIndex = 0 foreach ($groupItem in $collection) { $children = @($groupItem.children) # Find entity name $nameChild = $children | Where-Object { $_.settingDefinitionId -and $_.settingDefinitionId.ToLowerInvariant().Contains('_name') } | Select-Object -First 1 $entityName = if ($nameChild -and $nameChild.simpleSettingValue -and $nameChild.simpleSettingValue.value) { [string]$nameChild.simpleSettingValue.value } else { "Item $($entityIndex + 1)" } foreach ($child in $children) { $childDefId = [string]$child.settingDefinitionId if (-not $childDefId) { continue } $childDef = $settingDefinitions | Where-Object { $_.id -eq $childDefId } | Select-Object -First 1 $childDisplayName = if ($childDef -and $childDef.displayName) { [string]$childDef.displayName } else { $childDefId.Split('_')[-1] } $childValue = $null if ($child.choiceSettingValue -and $child.choiceSettingValue.value) { $childValue = Format-SettingValue -Value $child.choiceSettingValue.value -Definition $childDef } elseif ($child.choiceSettingCollectionValue) { $choiceItems = @($child.choiceSettingCollectionValue) $vals = foreach ($ci in $choiceItems) { if ($ci.value) { Format-SettingValue -Value $ci.value -Definition $childDef } } $vals = @($vals | Where-Object { $null -ne $_ }) if ($vals.Count -eq 1) { $childValue = $vals[0] } elseif ($vals.Count -gt 1) { $childValue = "[$($vals -join ', ')]" } } elseif ($child.simpleSettingValue -and $null -ne $child.simpleSettingValue.value) { $childValue = [string]$child.simpleSettingValue.value } elseif ($child.simpleSettingCollectionValue) { $childValue = Format-CollectionValue -Collection @($child.simpleSettingCollectionValue) } if ($null -ne $childValue) { $uniqueKey = "${settingDefId}:${entityName}:${childDefId}" $Map[$uniqueKey] = [pscustomobject]@{ Name = "Firewall Rule > $entityName > $childDisplayName" Value = $childValue Description = if ($childDef) { [string]$childDef.description } else { '' } Category = [string]$definition.categoryId Raw = $Root } } } $entityIndex++ } return # Don't create parent entry } else { # Regular group collection - recurse children as separate settings foreach ($groupItem in $collection) { $children = @($groupItem.children) foreach ($child in $children) { Convert-SingleSetting -Setting @{ settingInstance = $child settingDefinitions = $settingDefinitions } -Map $Map -Root $Root } } return # Don't create parent entry } } else { $configuredValue = Format-CollectionValue -Collection $collection } } '*deviceManagementConfigurationSimpleSettingCollectionInstance' { $collection = @($settingInstance.simpleSettingCollectionValue) $configuredValue = Format-CollectionValue -Collection $collection } '*deviceManagementConfigurationChoiceSettingCollectionInstance' { $collection = @($settingInstance.choiceSettingCollectionValue) $choiceValues = foreach ($item in $collection) { if ($item.value) { Format-SettingValue -Value $item.value -Definition $definition } } $choiceValues = @($choiceValues | Where-Object { $null -ne $_ }) if ($choiceValues.Count -eq 1) { $configuredValue = $choiceValues[0] } elseif ($choiceValues.Count -gt 1) { $configuredValue = "[$(($choiceValues) -join ', ')]" } } '*deviceManagementConfigurationChoiceSettingInstanceTemplate' { if ($settingInstance.choiceSettingValueTemplate -and $settingInstance.choiceSettingValueTemplate.defaultValue) { $configuredValue = $settingInstance.choiceSettingValueTemplate.defaultValue.settingDefinitionOptionId } if ($configuredValue) { $configuredValue = Format-SettingValue -Value $configuredValue -Definition $definition } if ($settingInstance.choiceSettingValueTemplate -and $settingInstance.choiceSettingValueTemplate.defaultValue -and $settingInstance.choiceSettingValueTemplate.defaultValue.children) { foreach ($child in $settingInstance.choiceSettingValueTemplate.defaultValue.children) { Convert-SingleSetting -Setting @{ settingInstanceTemplate = $child settingDefinitions = $settingDefinitions } -Map $Map -Root $Root } } } '*deviceManagementConfigurationSimpleSettingInstanceTemplate' { if ($settingInstance.simpleSettingValueTemplate -and $settingInstance.simpleSettingValueTemplate.defaultValue) { $dv = $settingInstance.simpleSettingValueTemplate.defaultValue if ($null -ne $dv.constantValue) { $configuredValue = [string]$dv.constantValue } elseif ($null -ne $dv.value) { $configuredValue = [string]$dv.value } } } '*deviceManagementConfigurationSimpleSettingCollectionInstanceTemplate' { $configuredValue = Format-CollectionValue -Collection @($settingInstance.simpleSettingCollectionValueTemplate) } '*deviceManagementConfigurationGroupSettingCollectionInstanceTemplate' { $configuredValue = Format-CollectionValue -Collection @($settingInstance.groupSettingCollectionValueTemplate) } '*deviceManagementConfigurationChoiceSettingCollectionInstanceTemplate' { $collection = @($settingInstance.choiceSettingCollectionValueTemplate) $choiceValues = foreach ($item in $collection) { if ($item.defaultValue -and $item.defaultValue.settingDefinitionOptionId) { Format-SettingValue -Value $item.defaultValue.settingDefinitionOptionId -Definition $definition } } $choiceValues = @($choiceValues | Where-Object { $null -ne $_ }) if ($choiceValues.Count -eq 1) { $configuredValue = $choiceValues[0] } elseif ($choiceValues.Count -gt 1) { $configuredValue = "[$(($choiceValues) -join ', ')]" } } default { } } if (-not $configuredValue -and $isSecurityBaseline) { $configuredValue = '[Template Default]' } $displayName = $definition.displayName if (-not $displayName) { $displayName = $definition.name } if (-not $displayName) { $displayName = 'Unknown' } $formattedName = Format-SettingNameWithCategory ` -SettingName ([string]$displayName) ` -CategoryId ([string]$definition.categoryId) ` -SettingDefId ([string]$settingDefId) $Map[$settingDefId] = [pscustomobject]@{ Name = $formattedName Value = $configuredValue Description = [string]$definition.description Category = [string]$definition.categoryId Raw = $Root } } |