From 0e66e268f63903ffa75e003822928669562b34bf Mon Sep 17 00:00:00 2001 From: Marco Wende Date: Wed, 23 Sep 2026 12:56:15 +0200 Subject: [PATCH] Policies: ADMX-Presentation-Matching robuster (Typ normalisieren + Fallbacks) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - @odata.type-Vergleich normalisiert (fuehrendes '#' egal) -> Text-Presentations wurden sonst wegen Formatunterschied nicht zugeordnet. - Zusaetzlicher Fallback: hat die Ziel-Definition genau eine Presentation, wird sie genutzt (deckt Ein-Feld-Settings wie Textboxen ab). - Definitions-Auflösung: classType zuerst (User/Device nicht verwechseln), dann categoryPath bevorzugen. Co-Authored-By: Claude Opus 4.8 --- src/PolicyIO.ps1 | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/PolicyIO.ps1 b/src/PolicyIO.ps1 index ca77500..4e8723b 100644 --- a/src/PolicyIO.ps1 +++ b/src/PolicyIO.ps1 @@ -510,12 +510,12 @@ function Resolve-TargetGpDefinition { } catch { $cands = @() } } if (@($cands).Count -eq 0) { return $null } - $narrow = @($cands) | Where-Object { - ((-not $ct) -or ([string](Get-PolicyProp $_ 'classType') -eq $ct)) -and - ((-not $cat) -or ([string](Get-PolicyProp $_ 'categoryPath') -eq $cat)) - } - $match = @($narrow) | Select-Object -First 1 - if (-not $match) { $match = @($cands) | Select-Object -First 1 } + # classType zuerst eingrenzen (User- vs. Device-Variante nie verwechseln), + # dann innerhalb dessen categoryPath bevorzugen; sonst erster Treffer. + $pool = @($cands) | Where-Object { (-not $ct) -or ([string](Get-PolicyProp $_ 'classType') -eq $ct) } + if (@($pool).Count -eq 0) { $pool = @($cands) } + $match = @($pool) | Where-Object { $cat -and ([string](Get-PolicyProp $_ 'categoryPath') -eq $cat) } | Select-Object -First 1 + if (-not $match) { $match = @($pool) | Select-Object -First 1 } return $match } @@ -524,19 +524,28 @@ function Resolve-TargetGpDefinition { # (in Definitions-Reihenfolge; $Counter zaehlt je Typ mit). function Resolve-TargetPresentationId { param($TargetPres, $SrcPresentation, [hashtable]$Counter) + # @odata.type-Format normalisieren: Graph liefert mal '#microsoft.graph.X', + # mal 'microsoft.graph.X' -> fuehrendes '#' weg, klein. Sonst schlaegt der + # String-Vergleich fehl und Text-Presentations werden nicht zugeordnet. + $norm = { param($t) ([string]$t).TrimStart('#').ToLower() } $srcLabel = [string](Get-PolicyProp $SrcPresentation 'label') - $srcType = [string](Get-PolicyProp $SrcPresentation '@odata.type') + $srcType = & $norm (Get-PolicyProp $SrcPresentation '@odata.type') + # 1) label + Typ if ($srcLabel) { $m = @($TargetPres) | Where-Object { ([string](Get-PolicyProp $_ 'label') -eq $srcLabel) -and - ([string](Get-PolicyProp $_ '@odata.type') -eq $srcType) + ((& $norm (Get-PolicyProp $_ '@odata.type')) -eq $srcType) } | Select-Object -First 1 if ($m) { return [string](Get-PolicyProp $m 'id') } } + # 2) i-te Ziel-Presentation gleichen Typs (in Reihenfolge) $idx = 0; if ($Counter.ContainsKey($srcType)) { $idx = [int]$Counter[$srcType] } - $sameType = @($TargetPres) | Where-Object { [string](Get-PolicyProp $_ '@odata.type') -eq $srcType } + $sameType = @($TargetPres) | Where-Object { (& $norm (Get-PolicyProp $_ '@odata.type')) -eq $srcType } $Counter[$srcType] = $idx + 1 if ($idx -lt @($sameType).Count) { return [string](Get-PolicyProp $sameType[$idx] 'id') } + # 3) Letzter Fallback: hat die Ziel-Definition genau EINE Presentation, nimm sie + # (deckt Ein-Feld-Settings wie Textboxen zuverlaessig ab). + if (@($TargetPres).Count -eq 1) { return [string](Get-PolicyProp $TargetPres[0] 'id') } return $null }