# 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 | Gruppen 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): Client-ID (Read-Only): (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. **Vollstaendige Berechtigungsliste, Redirect-URIs und ein Skript, das die App-Registrierung automatisch anlegt/konfiguriert:** [docs/App-Registration.md](docs/App-Registration.md) — inkl. [`Setup-AppRegistration.ps1`](docs/Setup-AppRegistration.ps1). --- ## Navigation (Tabs) | Tab | Beschreibung | |--------------------|-------------------------------------------------------------| | App Management | Haupt-Ansicht: Apps laden, Gruppen zuweisen / entfernen | | Group Management | Mitglieder anzeigen/exportieren, Benutzer **und Gruppen** hinzufuegen, CSV-Import | | App Report | Installationszaehler pro App, Geraete-Export als CSV | | Geräte | Geraete suchen, Filter (auch nach **Gruppe**), Spalte **Verwaltung** (Intune / Co-Managed), CSV-Export, **Bulk-Offboarding**, Detail-Panel (inkl. **Autopilot**-Status) und Aktionen (Sync, Wipe, …) | | Policies | Policies exportieren/importieren (Neuanlage), **zusammenfuehren** (Settings Catalog) und **Gruppen zuweisen** (Bulk) | --- ## 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--.csv` ### Benutzer | Gruppen hinzufuegen *(nur Read/Write)* - Umschalter **Benutzer / Gruppen** oben im Tab - Benutzer per Name, UPN oder E-Mail suchen — oder Gruppen per Name (verschachtelte Gruppen) - Mehrere Eintraege auswaehlen und gemeinsam zur gewaehlten Gruppe 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_.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. ### Konsolidieren *(nur Read/Write, nur Settings Catalog)* - Mindestens zwei **Settings-Catalog**-Policies per Checkbox waehlen → **„Konsolidieren"** - Die Einstellungen werden zu **einer neuen** Policy vereint (Neuanlage) — gleiche `settingDefinitionId` mit gleichem Wert wird einmal uebernommen - **Konflikte** (gleiche Einstellung, unterschiedlicher Wert) werden vor dem Anlegen angezeigt; pro Konflikt waehlst du, welche Policy gewinnt - Voraussetzung: gleiche Plattform. Zuweisungen werden nicht uebernommen. ### Gruppen zuweisen (Bulk) *(nur Read/Write)* Weist mehreren ausgewaehlten Policies **in einem Schritt** dieselben Gruppen zu — typuebergreifend (Settings Catalog, Compliance, Konfigurationsprofil, Administrative Vorlage gemischt). - Eine oder mehrere Policies per Checkbox waehlen → Button **„Gruppen zuweisen"** in der Toolbar. - Im Dialog **Include-** und/oder **Exclude-Gruppen** einmal waehlen; zusaetzlich die integrierten Include-Ziele **Alle Geraete** und **Alle Benutzer**. - **Zwei Modi:** - **Hinzufuegen** (Default): bestehende Zuweisungen jeder Policy bleiben erhalten, die gewaehlten Gruppen/Ziele kommen hinzu (dedupliziert, inkl. vorhandener Filter- und Alle-Geraete/Benutzer-Ziele). - **Ersetzen**: alle bisherigen Zuweisungen der Policy werden durch die Auswahl ersetzt. - Hintergrund: Die Graph-`/assign`-Action ersetzt immer die **komplette** Zuweisungsliste — der Hinzufuegen-Modus liest die bestehenden Zuweisungen daher zuerst aus und schickt sie zusammen mit den neuen mit. Ist das Auslesen fuer eine Policy nicht moeglich, wird sie uebersprungen (statt versehentlich bestehende Zuweisungen zu loeschen). - Ergebnis-Toast meldet Erfolg/Fehler pro Policy. ### 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** (`//__.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/Gruppen zur Gruppe hinzufuegen | | DELETE | `/api/groups/{id}/members/{uid}` | **Write** | Benutzer/Gruppe aus Gruppe entfernen | | GET | `/api/groups/{id}/devices/export` | Read+MDM | Geraete der Gruppen-User als CSV | | 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 | | POST | `/api/policies/consolidate` | **Write** | Settings-Catalog-Policies zusammenfuehren | | POST | `/api/policies/assign` | **Write** | Policies Gruppen/Alle-Geraete/-Benutzer zuweisen (Bulk, Add/Replace) | > **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`.