Neu: - Administrative Vorlagen (ADMX / groupPolicyConfigurations) als vierter Policy-Typ fuer Export/Import (GET /api/policies/administrativetemplate) - Zuweisung direkt nach Import: pro Policy Include-/Exclude-Gruppen (POST /api/policies/assign, typ-spezifische /assign-Action) - Git-Snapshot: versioniertes Policy-Backup in lokalen Git-Ordner (Settings-Sektion policyBackup) - Import akzeptiert Git-Snapshot-Dateien (policyType neben exportType) Behoben: - Settings-Catalog-Import schema-konform: Collection-Properties immer als Array (repariert PS-5.1-Roundtrip), null -> [], Wrapper-@odata.type, read-only id entfernt - Import-Phantom-Fehler (@($null)-Geisterdurchlauf im Archiv-Pfad) - Frontend-Cache-Reset beim Tenant-Wechsel (resetClientState) - WAM-Anmeldefenster zuverlaessig im Vordergrund (AttachThreadInput) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
321 lines
15 KiB
Markdown
321 lines
15 KiB
Markdown
# Intune Manager – Web Edition
|
||
|
||
Modernes Web-Frontend fuer den Intune Manager.
|
||
PowerShell startet einen lokalen HTTP-Server, der das Frontend ausliefert
|
||
und die Microsoft Graph-Aufrufe ausfuehrt.
|
||
|
||
## Voraussetzungen
|
||
|
||
- Windows mit PowerShell 5.1 oder PowerShell 7+
|
||
- Modul `Microsoft.Graph.Authentication` (wird beim ersten Connect benoetigt):
|
||
```powershell
|
||
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Force
|
||
```
|
||
- Browser (Edge, Chrome, Firefox)
|
||
|
||
## Start
|
||
|
||
```powershell
|
||
.\Start.ps1
|
||
```
|
||
|
||
Optionen:
|
||
|
||
| Parameter | Standard | Beschreibung |
|
||
|---------------|----------|-----------------------------------------------------------|
|
||
| `-Port` | `8077` | Port des lokalen HTTP-Servers |
|
||
| `-TenantId` | — | Ueberschreibt Tenant-ID aus Settings (nur dieser Start) |
|
||
| `-ClientId` | — | Ueberschreibt Client-ID aus Settings (nur dieser Start) |
|
||
| `-NoBrowser` | aus | Browser nicht automatisch oeffnen |
|
||
|
||
Beispiele:
|
||
|
||
```powershell
|
||
.\Start.ps1 -Port 8088
|
||
.\Start.ps1 -NoBrowser
|
||
```
|
||
|
||
Beenden: `Ctrl+C` im Terminal.
|
||
|
||
---
|
||
|
||
## Single- vs. Multi-Tenant
|
||
|
||
Unter **Settings → Verbindung → Verbindungsmodus** wählst du zwischen:
|
||
|
||
| Modus | Verhalten |
|
||
|---------------|---------------------------------------------------------------------------|
|
||
| Single-Tenant | Klassisch: ein Mandant (Tenant ID + App-Registrierung). Standard. |
|
||
| Multi-Tenant | Mehrere Mandanten mit **je eigener App-Registrierung**; Umschalten oben rechts |
|
||
|
||
Im Multi-Tenant-Modus legst du pro Mandant ein Profil an (Bezeichnung, Tenant ID,
|
||
Client-ID Read/Write, optional Client-ID Read-Only). Der **aktive** Mandant ist
|
||
markiert. Über das **Dropdown in der Topbar** wechselst du zwischen den Mandanten —
|
||
der Wechsel trennt die aktuelle Verbindung und verbindet **sofort neu** mit dem
|
||
gewählten Tenant (Login-Prompt erscheint). Alle Caches werden beim Wechsel geleert.
|
||
|
||
> Die Scopes (RW/RO) gelten **global** für alle Profile. Jeder Mandant benötigt
|
||
> eine eigene App-Registrierung mit denselben delegierten Berechtigungen und
|
||
> (Admin-)Consent im jeweiligen Tenant. Bestehende Single-Tenant-Konfigurationen
|
||
> werden verlustfrei übernommen (Umschalten auf Multi bietet an, das vorhandene
|
||
> Setup als erstes Profil zu übernehmen).
|
||
|
||
### Pro-Tenant-Vorgaben
|
||
|
||
Jedes Tenant-Profil kann **eigene Vorgaben** hinterlegen (z. B. Kunde A → `abt-hm-*`,
|
||
Kunde B → `dept-*`):
|
||
|
||
- **Abteilungs-Präfixe**
|
||
- **Required-Gruppen-Naming** (Präfix/Suffix)
|
||
- **Available-Gruppen-Naming** (Präfix/Suffix)
|
||
|
||
Bleibt ein Feld **leer, gilt die globale Vorgabe** aus den entsprechenden
|
||
Einstellungs-Abschnitten (bei Naming greift der Fallback pro Feld einzeln). Beim
|
||
Mandantenwechsel werden die Caches geleert, sodass die passenden Vorgaben gegen
|
||
den aktiven Tenant wirken.
|
||
|
||
---
|
||
|
||
## Verbindungsmodus (Read/Write vs. Read-Only)
|
||
|
||
Der Server probiert beim Verbinden **zuerst die Read/Write-App-ID** (`clientId`).
|
||
Schlaegt diese fehl, wird automatisch auf die **Read-Only-App-ID** (`clientIdRo`) zurueckgefallen.
|
||
|
||
| Modus | Anmelde-Ergebnis | Verhalten im Frontend |
|
||
|-------------|------------------------------------------|------------------------------------------------|
|
||
| Read/Write | Normale Verbindung | Alle Funktionen verfuegbar |
|
||
| Read-Only | Fallback auf `clientIdRo` | Schreib-Buttons/Tabs werden ausgeblendet |
|
||
|
||
### Was wird im Read-Only-Modus ausgeblendet?
|
||
|
||
- App-Zuweisung (Required / Available hinzufuegen und entfernen)
|
||
- Gruppen erstellen und loeschen
|
||
- Tab „Benutzer hinzufuegen" im Group Management
|
||
- Tab „CSV-Import" im Group Management
|
||
- Session-Ausfuehren-Button
|
||
- Alle Rename-/Delete-Buttons
|
||
|
||
### Konfiguration in den Einstellungen
|
||
|
||
```
|
||
Settings → Verbindung
|
||
Client-ID (Read/Write): <Azure App-Registration mit Schreibrechten>
|
||
Client-ID (Read-Only): <Azure App-Registration nur mit Leserechten> (optional)
|
||
Scopes RW: DeviceManagementApps.ReadWrite.All ...
|
||
Scopes RO: DeviceManagementApps.Read.All ...
|
||
```
|
||
|
||
### Benoetigt Graph-Berechtigungen
|
||
|
||
| Funktion | Benoetigt |
|
||
|----------------------------------|----------------------------------------------|
|
||
| Apps anzeigen | `DeviceManagementApps.Read.All` |
|
||
| Gruppen anzeigen / Mitglieder | `GroupMember.Read.All` |
|
||
| User suchen | `User.Read.All` |
|
||
| Zuweisungen aendern | `DeviceManagementApps.ReadWrite.All` |
|
||
| Gruppen-Mitglieder aendern | `GroupMember.ReadWrite.All` |
|
||
| App-Installationszaehler (Report)| `DeviceManagementManagedDevices.Read.All` ⚠ |
|
||
| Geraete-Exportliste pro App | `DeviceManagementManagedDevices.Read.All` ⚠ |
|
||
|
||
> ⚠ Ohne `DeviceManagementManagedDevices.Read.All` zeigt der App-Report nur
|
||
> Zaehler = 0 und der Geraete-Export liefert keine Eintraege.
|
||
|
||
---
|
||
|
||
## Navigation (Tabs)
|
||
|
||
| Tab | Beschreibung |
|
||
|--------------------|-------------------------------------------------------------|
|
||
| App Management | Haupt-Ansicht: Apps laden, Gruppen zuweisen / entfernen |
|
||
| Group Management | Mitglieder anzeigen, exportieren, Benutzer hinzufuegen, CSV-Import |
|
||
| App Report | Installationszaehler pro App, Geraete-Export als CSV |
|
||
| Geräte | Geraete suchen, Detail-Panel, Aktionen (Sync, Wipe, …) |
|
||
| Policies | Policies exportieren (JSON-Download) und importieren (Neuanlage) |
|
||
|
||
---
|
||
|
||
## Group Management
|
||
|
||
### Mitglieder & Export
|
||
|
||
- Gruppe links suchen und auswaehlen
|
||
- Mitgliederliste (inkl. verschachtelte Gruppen) wird geladen
|
||
- Filter zum Einschraenken der Anzeige
|
||
- **Als CSV exportieren** laedt alle Mitglieder als `members-<Gruppe>-<Datum>.csv`
|
||
|
||
### Benutzer hinzufuegen *(nur Read/Write)*
|
||
|
||
- Benutzer per Name, UPN oder E-Mail suchen
|
||
- Mehrere Benutzer auswaehlen und gemeinsam hinzufuegen
|
||
|
||
### CSV-Import *(nur Read/Write)*
|
||
|
||
Massenhaftes Hinzufuegen von Benutzern zu einer Gruppe per CSV-Datei.
|
||
|
||
**Ablauf:**
|
||
|
||
1. Gruppe links auswaehlen
|
||
2. CSV hochladen **oder** UPNs/E-Mails direkt einfuegen (eine pro Zeile)
|
||
3. **„Benutzer aufloesen"** – Graph-Lookup via UPN/Mail → Vorschau mit ✓/✗
|
||
4. **„Gefundene importieren"** – fuegt alle gefundenen Benutzer zur Gruppe hinzu
|
||
|
||
**CSV-Format:**
|
||
|
||
Die erste Zeile wird als optionaler Header erkannt.
|
||
Spalte mit dem Namen `UserPrincipalName`, `Mail` oder `Email` wird automatisch
|
||
ausgewaehlt. Ohne Header: erste Spalte mit `@` wird verwendet.
|
||
|
||
```csv
|
||
DisplayName,UserPrincipalName,Mail
|
||
Max Mustermann,max.mustermann@firma.de,max.mustermann@firma.de
|
||
Erika Muster,erika.muster@firma.de,
|
||
```
|
||
|
||
Oder einfach eine UPN pro Zeile:
|
||
```
|
||
max.mustermann@firma.de
|
||
erika.muster@firma.de
|
||
```
|
||
|
||
**Ergebnis-Anzeige nach Import:**
|
||
|
||
| Chip / Status | Bedeutung |
|
||
|--------------------|------------------------------------------------|
|
||
| ✔ Gruen | Erfolgreich hinzugefuegt |
|
||
| ≈ Gelb | Bereits Mitglied (kein Fehler) |
|
||
| ✗ Rot | Fehler (Meldung in der Tabelle) |
|
||
| Grau | UPN nicht in Graph gefunden (nicht importiert) |
|
||
|
||
---
|
||
|
||
## App Report
|
||
|
||
- **Neu laden**: laedt Installationszaehler fuer alle Apps (kann 1–2 Min. dauern)
|
||
- Tabelle sortierbar per Klick auf Spaltenheader
|
||
- Suche filtert nach App-Name, Publisher, Typ
|
||
- **Als CSV exportieren**: exportiert die gefilterte Tabelle
|
||
- **Geraete-Export** (pro Zeile): laedt alle Geraete mit Installationsstatus fuer diese App
|
||
|
||
---
|
||
|
||
## Policies (Export / Import)
|
||
|
||
Sichert Intune-Policies als JSON und legt sie in einem (ggf. anderen) Tenant
|
||
neu an. Unterstuetzte Typen:
|
||
|
||
| Typ | Graph-Collection |
|
||
|-------------------------|-----------------------------|
|
||
| Settings Catalog | `configurationPolicies` |
|
||
| Compliance Policies | `deviceCompliancePolicies` |
|
||
| Konfigurationsprofile | `deviceConfigurations` |
|
||
|
||
### Export
|
||
|
||
- Tabelle laedt alle drei Typen; Suche + Typ-Filter grenzen ein
|
||
- Policies per Checkbox auswaehlen → **Export**
|
||
- **1 Policy** → eine JSON-Datei
|
||
- **mehrere** → eine gebündelte JSON-Datei (`intune-policies_<Datum>.json`)
|
||
- Zusaetzlich wird jeder Export server-seitig unter
|
||
`%APPDATA%\IntuneAppManager-Web\policy-exports` archiviert
|
||
|
||
### Import *(nur Read/Write)*
|
||
|
||
- **Import…** → eine oder mehrere JSON-Dateien waehlen (einzeln oder Bundle)
|
||
- Import legt die Policies **immer als neue Policy** an — bestehende Policies
|
||
werden nie ueberschrieben
|
||
- Ergebnis-Toast meldet Erfolg/Fehler pro Policy
|
||
|
||
> Beim Anlegen entfernt der Import instanzgebundene / schreibgeschuetzte Felder
|
||
> (`id`, Zeitstempel, `version`, Zuweisungen, Status). Compliance-Policies
|
||
> bekommen bei Bedarf einen Default-`scheduledActionsForRule`-Block, den Graph
|
||
> beim Anlegen zwingend verlangt.
|
||
|
||
### Benoetigt Graph-Berechtigung
|
||
|
||
`DeviceManagementConfiguration.ReadWrite.All` (Import) bzw.
|
||
`DeviceManagementConfiguration.Read.All` (nur Export). Der Scope wird beim Start
|
||
automatisch zur Verbindung ergaenzt — in der Azure-App-Registration muss die
|
||
Berechtigung aber vorhanden und (Admin-)zugestimmt sein.
|
||
|
||
### Git-Snapshot (versioniertes Backup)
|
||
|
||
Button **„Git-Snapshot"** im Policies-Tab schreibt **alle** Policies aller Typen
|
||
als JSON in einen konfigurierten **lokalen Git-Ordner** und committet sie
|
||
automatisch — ideal als versioniertes Backup mit nachvollziehbarer History.
|
||
|
||
- Konfiguration: **Settings → Policy-Backup (Git)** — lokaler Repo-Pfad (wird bei
|
||
Bedarf angelegt und `git init`-isiert) + optional **automatischer `git push`**
|
||
(nutzt den vorhandenen Git-Credential-Helper; **kein Token in der App**).
|
||
- **Stabile Dateinamen** (`<tenant>/<typ>/<name>__<id8>.json`) und deterministische,
|
||
zeitstempel-freie JSON-Ausgabe → saubere Git-Diffs zwischen Snapshots. Entfernte
|
||
Policies verschwinden aus dem Snapshot.
|
||
- Multi-Tenant: je Mandant ein eigener Unterordner (nach Profil-Label bzw. Tenant-ID).
|
||
- Gibt es keine Aenderungen seit dem letzten Snapshot, wird nichts committet.
|
||
|
||
---
|
||
|
||
## Architektur
|
||
|
||
```
|
||
Intune Manager/
|
||
├── Start.ps1 Entry Point, Config, State
|
||
├── README.md diese Datei
|
||
├── src/
|
||
│ ├── Models.ps1 Settings-Schema und Defaults
|
||
│ ├── Graph.ps1 Microsoft Graph API Helpers
|
||
│ ├── Server.ps1 HTTP-Server (HttpListener)
|
||
│ ├── Router.ps1 Request-Routing
|
||
│ ├── Api.ps1 REST-API-Endpoints
|
||
│ └── PolicyIO.ps1 Policy Export/Import (Compliance, Config, Settings Catalog)
|
||
└── www/
|
||
├── index.html SPA-Shell
|
||
├── styles.css Dark Theme, Read-Only-Regeln
|
||
└── app.js Frontend-Logik (Vanilla JS)
|
||
```
|
||
|
||
---
|
||
|
||
## REST-API (intern)
|
||
|
||
| Methode | Pfad | Rechte | Zweck |
|
||
|---------|-------------------------------------------|-----------|------------------------------------------|
|
||
| GET | `/api/status` | — | Verbindungsstatus + ReadOnly-Flag |
|
||
| POST | `/api/connect` | — | Microsoft Graph verbinden |
|
||
| POST | `/api/disconnect` | — | Trennen |
|
||
| GET | `/api/groups` | Read | Abteilungs-Gruppen (`abt-hm-*`) |
|
||
| GET | `/api/groups/rpa` | Read | 3 vordefinierte RPA-Gruppen |
|
||
| GET | `/api/groups/search?q=` | Read | Gruppen suchen |
|
||
| POST | `/api/groups` | **Write** | Neue Required-Gruppe erstellen |
|
||
| POST | `/api/groups/check` | Read | Gruppen-Name pruefen |
|
||
| POST | `/api/groups/resolve-upns` | Read | UPN-Liste → UserId + DisplayName |
|
||
| GET | `/api/groups/{id}/members` | Read | Mitglieder einer Gruppe |
|
||
| GET | `/api/groups/{id}/members/export` | Read | Mitglieder als CSV |
|
||
| POST | `/api/groups/{id}/members` | **Write** | Benutzer zur Gruppe hinzufuegen |
|
||
| DELETE | `/api/groups/{id}/members/{uid}` | **Write** | Benutzer aus Gruppe entfernen |
|
||
| GET | `/api/users?q=` | Read | Benutzer suchen |
|
||
| GET | `/api/apps` | Read | Apps laden (cached) |
|
||
| POST | `/api/apps/refresh` | Read | Apps neu laden |
|
||
| GET | `/api/apps/report` | Read+MDM | Installationszaehler alle Apps |
|
||
| GET | `/api/apps/devicestatus?appId=` | Read+MDM | Geraete-Installationsstatus einer App |
|
||
| GET | `/api/membership?targets=&groupId=` | Read | Mitgliedschafts-Status pruefen |
|
||
| POST | `/api/membership/bulk` | Read | Mehrere Mitgliedschaften pruefen |
|
||
| POST | `/api/assignments/apply` | **Write** | Geplante Zuweisungen ausfuehren |
|
||
| GET | `/api/policies/settingscatalog` | Read+Cfg | Settings-Catalog-Policies auflisten |
|
||
| GET | `/api/policies/compliance` | Read+Cfg | Compliance-Policies auflisten |
|
||
| GET | `/api/policies/configuration` | Read+Cfg | Konfigurationsprofile auflisten |
|
||
| POST | `/api/policies/export` | Read+Cfg | Ausgewaehlte Policies als JSON exportieren |
|
||
| GET | `/api/policies/exports` | — | Server-Archiv der Exporte auflisten |
|
||
| POST | `/api/policies/import` | **Write** | Policies als Neuanlage importieren |
|
||
|
||
> **Read+MDM** = benoetigt zusaetzlich `DeviceManagementManagedDevices.Read.All`
|
||
> **Read+Cfg** = benoetigt `DeviceManagementConfiguration.Read.All` (Export) bzw. `.ReadWrite.All` (Import)
|
||
|
||
---
|
||
|
||
## Hinweise
|
||
|
||
- **Single-User-App** – der lokale HTTP-Server ist fuer einen Benutzer ausgelegt.
|
||
- **Auth lebt im PowerShell-Prozess** – Disconnect oder Schliessen des Terminals beendet die Session.
|
||
- **Cache** – Apps werden im Serverprozess gecacht. Refresh-Knopf laedt neu.
|
||
- **Port-Konflikt** – laeuft bereits eine Instanz, beendet `Start.ps1` sie automatisch (per CommandLine-Matching). Bei PID-4-Konflikt (http.sys) anderen Port waehlen: `.\Start.ps1 -Port 8088`.
|