Private/Graph/Get-IntunePolicyList.ps1

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

<#
    Get-IntunePolicyList.ps1 — Fetches all Intune Settings Catalog and Security Baseline policies for the tenant.
 
    Author: Sandy Zeng
    Project: IntuneDiff
 
    Version History:
    1.0.0 Initial release.
#>


function Get-IntunePolicyList {
    <#
    .SYNOPSIS
        Returns all Intune Settings Catalog and Security Baseline policies for the signed-in tenant.
 
    .DESCRIPTION
        Uses Graph JSON Batching to fetch Settings Catalog policies, Security Baseline templates,
        and Windows Hello intents in a single batch request for faster loading.
    #>

    [CmdletBinding()]
    param()

    $results = New-Object System.Collections.Generic.List[object]

    # Policy IDs known to cause persistent 504 errors - skip them
    $skipPolicyIdPrefixes = @(
        'c64bf257-bce5-4c4d-8ad8-03222f13d84c'
    )

    # Baseline template names that always fail to load settings
    $skipBaselineNames = @(
        'Microsoft Windows 11 Security Technical Implementation Guide'
    )

    # Build batch requests for all 3 policy sources
    $catalogFilter = "(platforms eq 'windows10') and (technologies has 'mdm' or technologies has 'windows10XManagement') and (templateReference/templateFamily le 'baseline' or templateReference/templateFamily eq 'deviceConfigurationPolicies')"
    $catalogSelect = 'id,name,description,platforms,technologies,templateReference,settingCount,createdDateTime,lastModifiedDateTime,isAssigned'
    $whfbTemplateId = '0f2b5d70-d4e9-4156-8c16-1397eb6c54a5'
    $baselineFilter = "(lifecycleState eq 'draft' or lifecycleState eq 'active') and (templateFamily eq 'Baseline')"

    $batchRequests = @(
        @{
            id     = 'catalog'
            method = 'GET'
            url    = "/deviceManagement/configurationPolicies?`$top=100&`$filter=$catalogFilter&`$select=$catalogSelect"
        },
        @{
            id     = 'baselines'
            method = 'GET'
            url    = "/deviceManagement/configurationPolicyTemplates?`$top=500&`$filter=$baselineFilter"
        },
        @{
            id     = 'intents'
            method = 'GET'
            url    = "/deviceManagement/intents?`$filter=templateId eq '$whfbTemplateId'"
        }
    )

    $batchResponses = Invoke-GraphBatchRequest -Requests $batchRequests

    # Process Settings Catalog response
    $catalogResp = $batchResponses['catalog']
    if ($catalogResp -and $catalogResp.value) {
        # Handle pagination for catalog (if > 100 policies)
        $catalogValues = @($catalogResp.value)
        $nextLink = $catalogResp.'@odata.nextLink'
        while ($nextLink) {
            if ($nextLink -notmatch '^https://graph\.microsoft\.com/') { break }
            $pageResp = Invoke-IntuneDiffRequest -Method GET -Uri $nextLink
            if ($pageResp.value) { $catalogValues += $pageResp.value }
            $nextLink = $pageResp.'@odata.nextLink'
        }

        foreach ($p in $catalogValues) {
            $type = 'Settings Catalog'
            $typeKey = 'settingsCatalog'
            $templateFamily = $null
            if ($p.templateReference -and $p.templateReference.templateFamily) {
                $templateFamily = [string]$p.templateReference.templateFamily
                $lf = $templateFamily.ToLowerInvariant()
                if ($lf -eq 'baseline') {
                    $type = 'Security Baseline Policy'
                    $typeKey = 'customSecurityBaseline'
                } elseif ($lf -like '*endpointsecurity*') {
                    $type = 'Endpoint Security'
                    $typeKey = 'endpointSecurity'
                }
            }

            $results.Add([pscustomobject]@{
                Id           = $p.id
                Name         = $p.name
                Description  = $p.description
                Type         = $type
                TypeKey      = $typeKey
                SettingCount = $p.settingCount
                IsAssigned   = [bool]$p.isAssigned
                Platforms    = $p.platforms
            })
        }
    } else {
        # Fallback: fetch catalog sequentially if batch failed
        try {
            $catalogUri = "/beta/deviceManagement/configurationPolicies?`$top=100&`$filter=$catalogFilter&`$select=$catalogSelect"
            $catalogResponse = Invoke-IntuneDiffRequest -Method GET -Uri $catalogUri -All
            foreach ($p in $catalogResponse.value) {
                $type = 'Settings Catalog'
                $typeKey = 'settingsCatalog'
                if ($p.templateReference -and $p.templateReference.templateFamily) {
                    $lf = ([string]$p.templateReference.templateFamily).ToLowerInvariant()
                    if ($lf -eq 'baseline') { $type = 'Security Baseline Policy'; $typeKey = 'customSecurityBaseline' }
                    elseif ($lf -like '*endpointsecurity*') { $type = 'Endpoint Security'; $typeKey = 'endpointSecurity' }
                }
                $results.Add([pscustomobject]@{
                    Id = $p.id; Name = $p.name; Description = $p.description; Type = $type; TypeKey = $typeKey
                    SettingCount = $p.settingCount; IsAssigned = [bool]$p.isAssigned; Platforms = $p.platforms
                })
            }
        } catch { Write-Verbose "Failed catalog fetch: $($_.Exception.Message)" }
    }

    # Process Security Baseline templates response
    $baselineResp = $batchResponses['baselines']
    if ($baselineResp -and $baselineResp.value) {
        foreach ($b in $baselineResp.value) {
            if ($skipPolicyIdPrefixes | Where-Object { $b.id -like "$_*" }) { continue }
            $displayName = $b.displayName
            if (-not $displayName) { $displayName = $b.name }
            if (-not $displayName) { $displayName = 'Unknown Baseline' }
            if ($skipBaselineNames | Where-Object { $displayName -like "$_*" }) { continue }

            if ($b.displayVersion -and ($displayName -notlike "*$($b.displayVersion)*")) {
                $displayName = "$displayName (Version $($b.displayVersion))"
            }

            $results.Add([pscustomobject]@{
                Id           = $b.id
                Name         = $displayName
                Description  = $b.description
                Type         = 'Security Baseline Template'
                TypeKey      = 'securityBaseline'
                SettingCount = $null
                IsAssigned   = $false
                Platforms    = $b.platforms
            })
        }
    } else {
        # Fallback
        try {
            $baselineUri = "/beta/deviceManagement/configurationPolicyTemplates?`$top=500&`$filter=$baselineFilter"
            $baselineResponse = Invoke-IntuneDiffRequest -Method GET -Uri $baselineUri -All
            foreach ($b in $baselineResponse.value) {
                if ($skipPolicyIdPrefixes | Where-Object { $b.id -like "$_*" }) { continue }
                $displayName = if ($b.displayName) { $b.displayName } elseif ($b.name) { $b.name } else { 'Unknown Baseline' }
                if ($skipBaselineNames | Where-Object { $displayName -like "$_*" }) { continue }
                if ($b.displayVersion -and ($displayName -notlike "*$($b.displayVersion)*")) { $displayName = "$displayName (Version $($b.displayVersion))" }
                $results.Add([pscustomobject]@{
                    Id = $b.id; Name = $displayName; Description = $b.description; Type = 'Security Baseline Template'
                    TypeKey = 'securityBaseline'; SettingCount = $null; IsAssigned = $false; Platforms = $b.platforms
                })
            }
        } catch { Write-Verbose "Skipping Security Baseline templates: $($_.Exception.Message)" }
    }

    # Process Windows Hello intents response
    $intentsResp = $batchResponses['intents']
    if ($intentsResp -and $intentsResp.value) {
        foreach ($intent in $intentsResp.value) {
            $results.Add([pscustomobject]@{
                Id           = $intent.id
                Name         = $intent.displayName
                Description  = $intent.description
                Type         = 'Endpoint Security'
                TypeKey      = 'intent'
                SettingCount = $null
                IsAssigned   = [bool]$intent.isAssigned
                Platforms    = 'windows10'
            })
        }
    } else {
        # Fallback
        try {
            $intentsUri = "/beta/deviceManagement/intents?`$filter=templateId eq '$whfbTemplateId'"
            $intentsResponse = Invoke-IntuneDiffRequest -Method GET -Uri $intentsUri -All
            foreach ($intent in $intentsResponse.value) {
                $results.Add([pscustomobject]@{
                    Id = $intent.id; Name = $intent.displayName; Description = $intent.description
                    Type = 'Endpoint Security'; TypeKey = 'intent'; SettingCount = $null
                    IsAssigned = [bool]$intent.isAssigned; Platforms = 'windows10'
                })
            }
        } catch { Write-Verbose "Skipping Windows Hello intents: $($_.Exception.Message)" }
    }

    return $results
}