Policies: Bulk-Zuweisung von Gruppen inkl. Alle Geräte/Alle Benutzer

- Neuer Toolbar-Button "Gruppen zuweisen" im Policy-Tab: mehreren
  ausgewählten Policies (typübergreifend) in einem Schritt dieselben
  Include-/Exclude-Gruppen zuweisen.
- Zwei Modi: "Hinzufügen" (bestehende Zuweisungen werden gelesen und
  gemergt, da Graph /assign Full-Replace ist) und "Ersetzen".
- Integrierte Include-Ziele "Alle Geräte" (allDevicesAssignmentTarget)
  und "Alle Benutzer" (allLicensedUsersAssignmentTarget), dedupliziert
  und mit Exclude-Gruppen kombinierbar.
- Invoke-PolicyAssignEndpoint um mode/allDevices/allUsers erweitert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 09:18:55 +02:00
co-authored by Claude Opus 4.8
parent af301925e9
commit 7bfcfc18ad
4 changed files with 322 additions and 2 deletions
+57 -2
View File
@@ -706,27 +706,82 @@ function Invoke-PolicyAssignEndpoint {
$name = [string](Get-PolicyProp $it 'policyName')
$include = @(Get-PolicyProp $it 'include') | Where-Object { $_ }
$exclude = @(Get-PolicyProp $it 'exclude') | Where-Object { $_ }
# Integrierte (virtuelle) Include-Ziele: Alle Geraete / Alle Benutzer.
$allDevices = [bool](Get-PolicyProp $it 'allDevices')
$allUsers = [bool](Get-PolicyProp $it 'allUsers')
# Modus: 'replace' (Default, Full-Replace wie bisher) oder 'add' (bestehende
# Zuweisungen erhalten und die neuen Gruppen dazu mergen). Die Graph-/assign-
# Action ersetzt IMMER die komplette Liste -> fuer 'add' muessen die
# bestehenden Zuweisungen vorher gelesen und mitgeschickt werden.
$mode = ([string](Get-PolicyProp $it 'mode')).ToLower()
if ($mode -ne 'add') { $mode = 'replace' }
$cfg = Get-PolicyTypeConfigByExportType $exportType
if (-not $cfg) { $results += @{ id = $id; policyName = $name; success = $false; error = "Unbekannter exportType: $exportType" }; continue }
if (-not $id) { $results += @{ policyName = $name; success = $false; error = 'Policy-Id fehlt' }; continue }
if (@($include).Count -eq 0 -and @($exclude).Count -eq 0) {
if (@($include).Count -eq 0 -and @($exclude).Count -eq 0 -and -not $allDevices -and -not $allUsers) {
$results += @{ id = $id; policyName = $name; success = $true; skipped = $true }
continue
}
$assignments = @()
$seen = @{} # Dedup-Key "odataType|groupId" -> $true
if ($mode -eq 'add') {
# Bestehende Zuweisungen lesen und 1:1 uebernehmen (inkl. evtl. Filter/
# allDevices/allLicensedUsers). Schlaegt das Lesen fehl, brechen wir fuer
# diese Policy ab, statt versehentlich bestehende Zuweisungen zu loeschen.
try {
$existing = Invoke-MgGraphRequestRetry -Uri "https://graph.microsoft.com/beta/deviceManagement/$($cfg.Collection)/$id/assignments" -Method GET
foreach ($a in @($existing.value)) {
$t = $a.target
if (-not $t) { continue }
$ot = [string]$t.'@odata.type'
$gid = [string]$t.groupId
$key = "$ot|$gid"
if ($seen[$key]) { continue }
$seen[$key] = $true
$tgt = @{ '@odata.type' = $ot }
if ($gid) { $tgt['groupId'] = $gid }
foreach ($fld in @('deviceAndAppManagementAssignmentFilterId','deviceAndAppManagementAssignmentFilterType')) {
$v = $t.$fld
if ($null -ne $v -and [string]$v -ne '') { $tgt[$fld] = $v }
}
$assignments += @{ target = $tgt }
}
} catch {
$em = $_.Exception.Message
try { if ($_.ErrorDetails.Message) { $em = $_.ErrorDetails.Message } } catch {}
$results += @{ id = $id; policyName = $name; success = $false; error = "Bestehende Zuweisungen nicht lesbar (Hinzufuegen-Modus): $em" }
continue
}
}
foreach ($g in $include) {
$key = "#microsoft.graph.groupAssignmentTarget|$g"
if ($seen[$key]) { continue }
$seen[$key] = $true
$assignments += @{ target = @{ '@odata.type' = '#microsoft.graph.groupAssignmentTarget'; groupId = [string]$g } }
}
foreach ($g in $exclude) {
$key = "#microsoft.graph.exclusionGroupAssignmentTarget|$g"
if ($seen[$key]) { continue }
$seen[$key] = $true
$assignments += @{ target = @{ '@odata.type' = '#microsoft.graph.exclusionGroupAssignmentTarget'; groupId = [string]$g } }
}
if ($allDevices) {
$key = '#microsoft.graph.allDevicesAssignmentTarget|'
if (-not $seen[$key]) { $seen[$key] = $true; $assignments += @{ target = @{ '@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget' } } }
}
if ($allUsers) {
$key = '#microsoft.graph.allLicensedUsersAssignmentTarget|'
if (-not $seen[$key]) { $seen[$key] = $true; $assignments += @{ target = @{ '@odata.type' = '#microsoft.graph.allLicensedUsersAssignmentTarget' } } }
}
$json = @{ assignments = @($assignments) } | ConvertTo-Json -Depth 10
$uri = "https://graph.microsoft.com/beta/deviceManagement/$($cfg.Collection)/$id/assign"
try {
Invoke-MgGraphRequestRetry -Uri $uri -Method POST -Body $json -ContentType 'application/json' | Out-Null
$results += @{ id = $id; policyName = $name; success = $true; includeCount = @($include).Count; excludeCount = @($exclude).Count }
$results += @{ id = $id; policyName = $name; success = $true; mode = $mode; includeCount = @($include).Count; excludeCount = @($exclude).Count }
} catch {
$m = $_.Exception.Message
try { if ($_.ErrorDetails.Message) { $m = $_.ErrorDetails.Message } } catch {}