Private/Graph/Get-PolicySettingsData.ps1

# Copyright (c) 2026 Sandy Zeng. All rights reserved.
# Source-available. All rights reserved. See LICENSE file.

<#
    Get-PolicySettingsData.ps1 — Fetches raw settings for a single Intune policy by ID and type.
 
    Author: Sandy Zeng
    Project: IntuneDiff
 
    Version History:
    1.0.0 Initial release.
#>


function Get-PolicySettingsData {
    <#
    .SYNOPSIS
        Fetches the raw settings array for a single policy by ID and type.
 
    .DESCRIPTION
        Returns a hashtable with .id, .name, .settings (array of raw setting instances
        from Graph) ready to be passed into ConvertTo-ExtractedSettings.
 
        Supported PolicyTypeKey values: settingsCatalog, customSecurityBaseline,
        endpointSecurity (configurationPolicies-backed), securityBaseline
        (configurationPolicyTemplates).
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PolicyId,

        [Parameter(Mandatory)]
        [ValidateSet('settingsCatalog', 'customSecurityBaseline', 'endpointSecurity', 'securityBaseline', 'intent')]
        [string]$PolicyTypeKey,

        [string]$PolicyName
    )

    if ($PolicyId -notmatch '^[0-9a-fA-F\-]{36}(_\d+)?$') {
        throw "Invalid PolicyId format: '$PolicyId'. Expected a GUID (with optional version suffix)."
    }

    if ($PolicyTypeKey -eq 'intent') {
        # DeviceManagementIntent-based Endpoint Security policies
        $meta = Invoke-IntuneDiffRequest -Method GET -Uri "/beta/deviceManagement/intents('$PolicyId')"
        $intentName = if ($PolicyName) { $PolicyName } elseif ($meta.displayName) { $meta.displayName } else { $meta.name }
        $settingsUri = "/beta/deviceManagement/intents('$PolicyId')/settings?`$top=1000"
        $settingsResponse = Invoke-IntuneDiffRequest -Method GET -Uri $settingsUri -All

        # Load setting definitions from the template for display names
        $templateId = $meta.templateId
        $definitions = @{}
        if ($templateId) {
            try {
                $catsUri = "/beta/deviceManagement/templates('$templateId')/categories"
                $catsResponse = Invoke-IntuneDiffRequest -Method GET -Uri $catsUri -All
                foreach ($cat in $catsResponse.value) {
                    $catId = $cat.id
                    $defsUri = "/beta/deviceManagement/templates('$templateId')/categories('$catId')/settingDefinitions"
                    $defsResponse = Invoke-IntuneDiffRequest -Method GET -Uri $defsUri -All
                    foreach ($d in $defsResponse.value) {
                        $definitions[[string]$d.id] = $d
                    }
                }
            } catch { }
        }

        # Convert intent settings to settingsCatalog-like format for ConvertTo-ExtractedSettings compatibility
        $convertedSettings = New-Object System.Collections.Generic.List[object]
        foreach ($s in $settingsResponse.value) {
            $defId = [string]$s.definitionId
            # Intent settings can have value directly or as valueJson
            $rawValue = $s.value
            if ($null -eq $rawValue -and $s.valueJson) {
                try { $rawValue = $s.valueJson | ConvertFrom-Json } catch { $rawValue = $s.valueJson }
            }

            $def = if ($definitions.ContainsKey($defId)) { $definitions[$defId] } else { $null }
            $displayName = if ($def -and $def.displayName) { [string]$def.displayName } else { $defId -replace '^.*--', '' -replace '_', ' > ' }
            $description = if ($def -and $def.description) { [string]$def.description } else { '' }

            # Format value display based on @odata.type
            $odataType = [string]$s.'@odata.type'
            $valueDisplay = $null
            switch -Wildcard ($odataType) {
                '*BooleanSettingInstance' {
                    $valueDisplay = if ($rawValue -eq $true) { 'Enabled' } elseif ($rawValue -eq $false) { 'Disabled' } else { $null }
                }
                '*IntegerSettingInstance' {
                    $valueDisplay = if ($null -ne $rawValue) { [string]$rawValue } else { $null }
                }
                '*StringSettingInstance' {
                    $valueDisplay = if ($rawValue) { [string]$rawValue } else { $null }
                }
                default {
                    $valueDisplay = if ($null -ne $rawValue) { [string]$rawValue } else { $null }
                }
            }

            # Try to resolve friendly value from definition constraints/options
            if ($def -and $null -ne $rawValue) {
                if ($def.constraints) {
                    $enumConstraint = $def.constraints | Where-Object { $_.'@odata.type' -like '*EnumConstraint*' -or $_.values } | Select-Object -First 1
                    if ($enumConstraint -and $enumConstraint.values) {
                        $match = $enumConstraint.values | Where-Object { $_.value -eq [string]$rawValue } | Select-Object -First 1
                        if ($match -and $match.displayName) { $valueDisplay = [string]$match.displayName }
                    }
                }
                if ($def.options) {
                    $optionMatch = $def.options | Where-Object { $_.itemId -like "*_$rawValue" -or $_.name -eq [string]$rawValue } | Select-Object -First 1
                    if ($optionMatch -and $optionMatch.displayName) { $valueDisplay = [string]$optionMatch.displayName }
                }
            }

            # Build a synthetic setting that ConvertTo-ExtractedSettings can handle
            $convertedSettings.Add(@{
                __intent         = $true
                settingId        = $defId
                displayName      = $displayName
                description      = $description
                value            = $valueDisplay
                rawValue         = $rawValue
                category         = if ($def -and $def.categoryPath) { [string]$def.categoryPath } else { '' }
            })
        }

        return @{
            id       = $meta.id
            name     = $intentName
            settings = $convertedSettings.ToArray()
            __kind   = 'intent'
        }
    }

    if ($PolicyTypeKey -eq 'securityBaseline') {
        # configurationPolicyTemplates -> settingTemplates
        if ($PolicyName) {
            $baselineName = $PolicyName
        } else {
            $meta = Invoke-IntuneDiffRequest -Method GET -Uri "/beta/deviceManagement/configurationPolicyTemplates('$PolicyId')"
            $baselineName = if ($meta.displayName) { $meta.displayName } else { $meta.name }
        }
        $settingsUri = "/beta/deviceManagement/configurationPolicyTemplates('$PolicyId')/settingTemplates?`$expand=settingDefinitions&`$top=1000"
        $settingsResponse = Invoke-IntuneDiffRequest -Method GET -Uri $settingsUri -All

        return @{
            id              = $PolicyId
            name            = $baselineName
            settingTemplates = $settingsResponse.value
            __kind          = 'securityBaseline'
        }
    }

    # All configurationPolicies-backed kinds use the same shape
    if ($PolicyName) {
        $policyDisplayName = $PolicyName
    } else {
        $meta = Invoke-IntuneDiffRequest -Method GET -Uri "/beta/deviceManagement/configurationPolicies('$PolicyId')"
        $policyDisplayName = $meta.name
    }
    $settingsUri = "/beta/deviceManagement/configurationPolicies('$PolicyId')/settings?`$expand=settingDefinitions&`$top=1000"
    $settingsResponse = Invoke-IntuneDiffRequest -Method GET -Uri $settingsUri -All

    return @{
        id       = $PolicyId
        name     = $policyDisplayName
        settings = $settingsResponse.value
        __kind   = 'settingsCatalog'
    }
}