Private/Graph/Get-PolicySettingsBatch.ps1
|
# Copyright (c) 2026 Sandy Zeng. All rights reserved. # Source-available. All rights reserved. See LICENSE file. <# Get-PolicySettingsBatch.ps1 — Batch-fetches settings for multiple policies using Graph JSON Batching. Author: Sandy Zeng Project: IntuneDiff Version History: 1.0.0 Initial release. #> function Get-PolicySettingsBatch { <# .SYNOPSIS Fetches settings for multiple policies in parallel using Graph JSON Batching. .DESCRIPTION Accepts an array of policy objects and fetches their settings using the /$batch endpoint. Policies are grouped by type and batched in groups of up to 20. Intent policies are excluded from batching (they require multi-step calls) and fetched sequentially as a fallback. .PARAMETER Policies Array of objects with: Id, TypeKey, Name properties. .OUTPUTS Hashtable keyed by PolicyId. Each value is a hashtable with: - settings or settingTemplates (array) - name (string) - __kind (string) Returns $null for a policy if the batch request failed. #> [CmdletBinding()] param( [Parameter(Mandatory)] [object[]]$Policies ) $results = @{} # Deduplicate by Id (same policy can appear multiple times in input) $seen = @{} $uniquePolicies = @($Policies | Where-Object { if ($seen.ContainsKey($_.Id)) { $false } else { $seen[$_.Id] = $true; $true } }) # Separate intent policies (can't be batched due to multi-step dependency) $batchable = @($uniquePolicies | Where-Object { $_.TypeKey -ne 'intent' }) $intents = @($uniquePolicies | Where-Object { $_.TypeKey -eq 'intent' }) # Build batch requests for batchable policies if ($batchable.Count -gt 0) { $requests = @($batchable | ForEach-Object { $policyId = $_.Id $typeKey = $_.TypeKey $url = switch ($typeKey) { 'securityBaseline' { "/deviceManagement/configurationPolicyTemplates('$policyId')/settingTemplates?`$expand=settingDefinitions&`$top=1000" } default { # settingsCatalog, customSecurityBaseline, endpointSecurity "/deviceManagement/configurationPolicies('$policyId')/settings?`$expand=settingDefinitions&`$top=1000" } } @{ id = $policyId method = 'GET' url = $url } }) $batchResponses = Invoke-GraphBatchRequest -Requests $requests # Process responses foreach ($p in $batchable) { $policyId = $p.Id $resp = $batchResponses[$policyId] if ($null -eq $resp) { # Batch item failed - try individual fallback Write-IDLog "Batch fallback for $policyId ($($p.Name))" try { $fallback = Get-PolicySettingsData -PolicyId $policyId -PolicyTypeKey $p.TypeKey -PolicyName $p.Name $results[$policyId] = $fallback } catch { # Both batch and fallback failed - likely unsupported policy type (certificates, update policies) # Return empty settings instead of null so it's not counted as "failed" Write-IDLog "Skipping unsupported policy $policyId ($($p.Name)): $($_.Exception.Message)" $results[$policyId] = @{ id = $policyId name = $p.Name settings = @() __kind = 'skipped' } } continue } # Extract the value array from the response (body is a hashtable from -OutputType Hashtable) $settingsArray = if ($resp -is [System.Collections.IDictionary]) { $resp['value'] } else { $resp.value } if (-not $settingsArray) { $settingsArray = @() } if ($p.TypeKey -eq 'securityBaseline') { $results[$policyId] = @{ id = $policyId name = $p.Name settingTemplates = $settingsArray __kind = 'securityBaseline' } } else { $results[$policyId] = @{ id = $policyId name = $p.Name settings = $settingsArray __kind = 'settingsCatalog' } } } } # Fetch intent policies sequentially (multi-step dependency) foreach ($p in $intents) { try { $results[$p.Id] = Get-PolicySettingsData -PolicyId $p.Id -PolicyTypeKey 'intent' -PolicyName $p.Name } catch { $results[$p.Id] = $null } } return $results } |