Policies: Admin-Vorlagen-Export gehärtet (resiliente Liste + sichtbare Fehler)
- Get-GraphPolicyList: $expand=assignments pro Typ absichern. groupPolicyConfigurations (Administrative Vorlagen) lehnt den Expand teils mit 400 ab -> dann scheiterte das Laden und der Export ließ die Policies still weg. Jetzt Fallback auf Laden ohne Zuweisungen, damit der Typ erscheint und exportierbar bleibt. - Export-PoliciesEndpoint: Detail-Fehler nicht mehr verschlucken, sondern pro Policy in 'errors' zurückgeben (+ Log). - Frontend: Export-Fehler werden im Toast gemeldet (kein stiller Teil-Export mehr). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+20
-5
@@ -154,8 +154,17 @@ function Get-GraphPolicyList {
|
|||||||
$cfg = Get-PolicyTypeConfig $Type
|
$cfg = Get-PolicyTypeConfig $Type
|
||||||
if (-not $cfg) { throw "Unbekannter Policy-Typ: $Type" }
|
if (-not $cfg) { throw "Unbekannter Policy-Typ: $Type" }
|
||||||
# Zuweisungen gleich mitladen ($expand=assignments), damit die Liste je Policy
|
# Zuweisungen gleich mitladen ($expand=assignments), damit die Liste je Policy
|
||||||
# Anzahl + aufgeloeste Ziele zeigen kann.
|
# Anzahl + aufgeloeste Ziele zeigen kann. Nicht jede deviceManagement-Collection
|
||||||
$raw = Get-GraphPaged -Uri "https://graph.microsoft.com/beta/deviceManagement/$($cfg.Collection)?`$expand=assignments"
|
# unterstuetzt $expand=assignments (z.B. groupPolicyConfigurations lehnt es teils
|
||||||
|
# mit 400 ab) -> im Fehlerfall ohne Zuweisungen laden, damit der Typ ueberhaupt
|
||||||
|
# erscheint (und exportierbar bleibt).
|
||||||
|
$listBase = "https://graph.microsoft.com/beta/deviceManagement/$($cfg.Collection)"
|
||||||
|
try {
|
||||||
|
$raw = Get-GraphPaged -Uri ($listBase + '?$expand=assignments')
|
||||||
|
} catch {
|
||||||
|
Write-Host " [POLICIES] $($cfg.Key): `$expand=assignments nicht unterstuetzt -> lade ohne Zuweisungen ($($_.Exception.Message))" -ForegroundColor DarkYellow
|
||||||
|
$raw = Get-GraphPaged -Uri $listBase
|
||||||
|
}
|
||||||
$items = @()
|
$items = @()
|
||||||
$needGroups = @{} # eindeutige Gruppen-Ids ueber alle Policies (fuer 1 Bulk-Lookup)
|
$needGroups = @{} # eindeutige Gruppen-Ids ueber alle Policies (fuer 1 Bulk-Lookup)
|
||||||
foreach ($r in $raw) {
|
foreach ($r in $raw) {
|
||||||
@@ -592,10 +601,16 @@ function Export-PoliciesEndpoint {
|
|||||||
|
|
||||||
$files = @()
|
$files = @()
|
||||||
$exports = @()
|
$exports = @()
|
||||||
|
$errors = @()
|
||||||
foreach ($id in $ids) {
|
foreach ($id in $ids) {
|
||||||
$policy = $null
|
$policy = $null
|
||||||
try { $policy = Get-GraphPolicyDetail -Type $type -Id ([string]$id) } catch {}
|
try { $policy = Get-GraphPolicyDetail -Type $type -Id ([string]$id) }
|
||||||
if (-not $policy) { continue }
|
catch {
|
||||||
|
$errors += @{ id = [string]$id; error = $_.Exception.Message }
|
||||||
|
Write-Host " [EXPORT] $type/$id fehlgeschlagen: $($_.Exception.Message)" -ForegroundColor DarkYellow
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (-not $policy) { $errors += @{ id = [string]$id; error = 'Leere Antwort von Graph' }; continue }
|
||||||
|
|
||||||
$displayName = Get-PolicyDisplayName $policy
|
$displayName = Get-PolicyDisplayName $policy
|
||||||
$exportData = [ordered]@{
|
$exportData = [ordered]@{
|
||||||
@@ -623,7 +638,7 @@ function Export-PoliciesEndpoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return @{ ok = $true; exportedCount = $exports.Count; files = @($files); exports = @($exports) }
|
return @{ ok = $true; exportedCount = $exports.Count; files = @($files); exports = @($exports); errors = @($errors) }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Liste des Server-Archivs (fuer optionalen Server-seitigen Re-Import).
|
# Liste des Server-Archivs (fuer optionalen Server-seitigen Re-Import).
|
||||||
|
|||||||
+12
-2
@@ -6467,11 +6467,17 @@ async function polExportSelected() {
|
|||||||
setLoading('Exportiere Policies…');
|
setLoading('Exportiere Policies…');
|
||||||
try {
|
try {
|
||||||
let allExports = [];
|
let allExports = [];
|
||||||
|
let allErrors = [];
|
||||||
for (const [type, ids] of Object.entries(byType)) {
|
for (const [type, ids] of Object.entries(byType)) {
|
||||||
const res = await api('/api/policies/export', { method: 'POST', body: { type, ids }, timeoutMs: 120000 });
|
const res = await api('/api/policies/export', { method: 'POST', body: { type, ids }, timeoutMs: 120000 });
|
||||||
if (res.exports) allExports = allExports.concat(res.exports);
|
if (res.exports) allExports = allExports.concat(res.exports);
|
||||||
|
if (res.errors) allErrors = allErrors.concat(res.errors);
|
||||||
|
}
|
||||||
|
if (!allExports.length) {
|
||||||
|
const hint = allErrors.length ? ` (${allErrors.length} Fehler, z.B. ${escapeHtml(allErrors[0].error || '')})` : '';
|
||||||
|
toast('Nichts exportiert.' + hint, 'err', 'Export');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!allExports.length) { toast('Nichts exportiert.', 'warn'); return; }
|
|
||||||
// Jede ausgewählte Policy als eigene JSON-Datei herunterladen.
|
// Jede ausgewählte Policy als eigene JSON-Datei herunterladen.
|
||||||
const usedNames = new Set();
|
const usedNames = new Set();
|
||||||
for (let i = 0; i < allExports.length; i++) {
|
for (let i = 0; i < allExports.length; i++) {
|
||||||
@@ -6484,7 +6490,11 @@ async function polExportSelected() {
|
|||||||
// Kurze Pause, damit der Browser mehrere aufeinanderfolgende Downloads nicht verwirft.
|
// Kurze Pause, damit der Browser mehrere aufeinanderfolgende Downloads nicht verwirft.
|
||||||
if (i < allExports.length - 1) await new Promise(r => setTimeout(r, 150));
|
if (i < allExports.length - 1) await new Promise(r => setTimeout(r, 150));
|
||||||
}
|
}
|
||||||
toast(`${allExports.length} Policy(s) als Einzeldatei(en) exportiert (auch im Server-Archiv abgelegt).`, 'ok');
|
if (allErrors.length) {
|
||||||
|
toast(`${allExports.length} exportiert, ${allErrors.length} fehlgeschlagen (z.B. ${escapeHtml(allErrors[0].error || 'Fehler')}).`, 'warn', 'Export');
|
||||||
|
} else {
|
||||||
|
toast(`${allExports.length} Policy(s) als Einzeldatei(en) exportiert (auch im Server-Archiv abgelegt).`, 'ok');
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast('Export fehlgeschlagen: ' + e.message, 'err');
|
toast('Export fehlgeschlagen: ' + e.message, 'err');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user