:

    Intune-Frühjahrsputz

    Über die Jahre sammeln sich in Intune viele Objekte an, die nicht mehr benötigt werden und nirgends zugewiesen sind. Um Klarheit zu schaffen und die Umgebung etwas aufzuräumen, haben wir ein Script erstellt. Damit können inaktive Geräte, nicht zugewiesene Apps oder Konfigurationsrichtlinien und so weiter ermittelt und optional gelöscht werden.



    Benötigst du Unterstützung?

    Melde dich bei uns!



    Hier findest du das im Video genutzte Script. Bitte beachte die Nutzungshinweise im Header des Scripts. Es wird ohne Gewähr bereitgestellt und auf eigene Gefahr genutzt.

    Intune-Cleanup.ps1

    <#
    .SYNOPSIS
        Intune-Frühjahrsputz - Interaktives Tool zur Bereinigung ungenutzter Intune-Ressourcen
    
    .DESCRIPTION
        Das Intune-Frühjahrsputz-Tool bietet eine zentrale Oberfläche zum Identifizieren und Löschen 
        ungenutzter oder nicht zugewiesener Ressourcen in Microsoft Intune. Das Tool hilft dabei, die 
        Intune-Umgebung aufgeräumt und übersichtlich zu halten.
    
        Funktionen:
        [1] Device Cleanup - Identifiziert inaktive Geräte (Standard: >90 Tage ohne Sync)
        [2] Configuration Policy Cleanup - Findet nicht zugewiesene Konfigurationsrichtlinien
        [3] App Cleanup - Identifiziert nicht zugewiesene Apps
        [4] Compliance Policy Cleanup - Findet nicht zugewiesene Compliance-Richtlinien
        [5] Windows Update Policy Cleanup - Identifiziert nicht zugewiesene Update-Richtlinien
        [6] Enrollment Profile Cleanup - Findet nicht zugewiesene Enrollment-Profile
        [7] Script Cleanup - Identifiziert nicht zugewiesene Skripte (PowerShell, Remediation, macOS)
    
        Das Tool arbeitet mit dem Microsoft Graph PowerShell SDK und nutzt ausschließlich die 
        Microsoft Graph REST API. Alle Löschvorgänge erfordern explizite Benutzerbestätigung.
    
    .NOTES
        Dateiname:      Intune-Cleanup.ps1
        Autor:          itelio GmbH
        Voraussetzungen:
            - PowerShell 5.1 oder höher
            - Microsoft.Graph PowerShell SDK Module
            - Microsoft Graph API-Berechtigungen (siehe unten)
        
        WICHTIG - Ausführungsumgebung:
            Das Skript MUSS aus einer normalen PowerShell-Konsole ausgeführt werden.
            PowerShell ISE wird NICHT unterstützt, da die Authentifizierung mit Web Account Manager (WAM)
            in der ISE nicht funktioniert. Verwenden Sie Windows Terminal, PowerShell 7, oder die 
            klassische PowerShell-Konsole.
    
        Erforderliche Microsoft Graph API-Berechtigungen (Application Permissions):
            - DeviceManagementManagedDevices.ReadWrite.All
              Benötigt für: Device Cleanup (Funktion 1)
              
            - DeviceManagementConfiguration.ReadWrite.All
              Benötigt für: Configuration Policy Cleanup, Compliance Policy Cleanup, 
                            Windows Update Policy Cleanup (Funktionen 2, 4, 5)
              
            - DeviceManagementApps.ReadWrite.All
              Benötigt für: App Cleanup (Funktion 3)
              
            - DeviceManagementServiceConfig.ReadWrite.All
              Benötigt für: Enrollment Profile Cleanup (Funktion 6)
              
            - DeviceManagementScripts.ReadWrite.All
              Benötigt für: Script Cleanup (Funktion 7)
    
        Erforderliche Admin-Rolle zum Erteilen der Berechtigungen:
            Zum Consent/Granten der oben genannten Graph API-Berechtigungen wird eine der 
            folgenden Azure AD-Rollen benötigt:
            - Global Administrator (empfohlen für initiales Setup)
            - Intune Administrator (ausreichend für alle Device Management Scopes)
            
            Alternativ kann ein Global Administrator im Azure Portal eine App Registration erstellen
            und die erforderlichen API-Berechtigungen dort vorab erteilen.
    
        Beim ersten Aufruf einer Funktion fordert das Skript interaktiv die Anmeldung und den
        Consent für alle benötigten Berechtigungen an. Nachfolgende Funktionsaufrufe in derselben
        Session nutzen die bestehende Authentifizierung.
    
    .EXAMPLE
        .\Intune-Cleanup.ps1
        
        Startet das interaktive Menü. Wählen Sie die gewünschte Funktion durch Eingabe der 
        entsprechenden Nummer.
    
    .LINK
        https://learn.microsoft.com/en-us/graph/api/resources/intune-graph-overview
        https://learn.microsoft.com/en-us/powershell/microsoftgraph/
    
    .DISCLAIMER
        HAFTUNGSAUSSCHLUSS:
        Dieses Skript wird "wie besehen" ohne jegliche Gewährleistung bereitgestellt. Die Nutzung 
        erfolgt auf eigene Gefahr. Der Autor (itelio GmbH) übernimmt keine Haftung für Schäden, 
        Datenverlust oder andere Probleme, die durch die Verwendung dieses Skripts entstehen können.
        
        Es wird dringend empfohlen:
        - Das Skript zunächst in einer Testumgebung zu verwenden
        - Vor dem Löschen von Ressourcen die Auswahl sorgfältig zu prüfen
        - Regelmäßige Backups Ihrer Intune-Konfiguration durchzuführen
        
        Das Löschen von Ressourcen kann nicht rückgängig gemacht werden.
    #>
    
    # Requires -Version 5.1
    
    [CmdletBinding()]
    param()
    
    # Globale Variablen
    $script:isConnected = $false
    
    # Funktion: Graph-Verbindung herstellen
    function Connect-ToGraph {
        if (-not $script:isConnected) {
            try {
                Write-Host "`nVerbindung zu Microsoft Graph wird hergestellt..." -ForegroundColor Cyan
                Connect-MgGraph -Scopes "DeviceManagementManagedDevices.ReadWrite.All", "DeviceManagementConfiguration.ReadWrite.All", "DeviceManagementApps.ReadWrite.All", "DeviceManagementServiceConfig.ReadWrite.All", "DeviceManagementScripts.ReadWrite.All" -NoWelcome -ErrorAction Stop
                
                # Verbindung verifizieren
                $context = Get-MgContext
                if ($null -eq $context -or $null -eq $context.Account) {
                    throw "Graph-Verbindung konnte nicht hergestellt werden"
                }
                
                $script:isConnected = $true
                Write-Host "Verbindung erfolgreich" -ForegroundColor Green
            }
            catch {
                Write-Host "Verbindung fehlgeschlagen!" -ForegroundColor Red
                Write-Host "Fehler: $($_.Exception.Message)" -ForegroundColor Red
                Write-Host "`nDas Programm wird beendet." -ForegroundColor Yellow
                exit 1
            }
        }
    }
    
    # Funktion: Graph-Verbindung trennen
    function Disconnect-FromGraph {
        if ($script:isConnected) {
            Disconnect-MgGraph | Out-Null
            $script:isConnected = $false
            Write-Host "`nGraph-Verbindung getrennt" -ForegroundColor Cyan
        }
    }
    
    # Funktion: Device Cleanup
    function Start-DeviceCleanup {
        Write-Host "`n=== Device Cleanup ===" -ForegroundColor Cyan
        Write-Host "Identifiziert und löscht inaktive Geräte`n" -ForegroundColor Cyan
        
        # Sicherstellen dass Graph-Verbindung besteht
        Connect-ToGraph
        
        # Interaktive Abfrage: Inaktivitätsschwelle
        Write-Host "Konfiguration" -ForegroundColor Yellow
        Write-Host "-------------" -ForegroundColor Yellow
        $inactiveDaysInput = Read-Host "Anzahl Tage ohne Aktivität [Standard: 90]"
        if ([string]::IsNullOrWhiteSpace($inactiveDaysInput)) {
            $inactiveDays = 90
        } else {
            $inactiveDays = [int]$inactiveDaysInput
        }
        
        # Berechnung des Schwellwert-Datums
        $thresholdDate = (Get-Date).AddDays(-$inactiveDays)
        Write-Host "`nSuche nach Geräten ohne Aktivität seit: $($thresholdDate.ToString('dd.MM.yyyy HH:mm'))" -ForegroundColor Cyan
        Write-Host "Inaktivitätsschwelle: $inactiveDays Tage`n" -ForegroundColor Cyan
        
        # Alle Intune Managed Devices abrufen
        Write-Host "Rufe alle verwalteten Geräte ab..." -ForegroundColor Yellow
        $allDevices = Get-MgDeviceManagementManagedDevice -All
        
        # Filtern der inaktiven Geräte
        $inactiveDevices = $allDevices | Where-Object {
            $_.LastSyncDateTime -lt $thresholdDate
        }
        
        # Ergebnisse ausgeben
        Write-Host "`n=== Ergebnis ===" -ForegroundColor Green
        Write-Host "Gesamtanzahl Geräte: $($allDevices.Count)" -ForegroundColor White
        Write-Host "Inaktive Geräte (>$inactiveDays Tage): $($inactiveDevices.Count)" -ForegroundColor Yellow
        
        if ($inactiveDevices.Count -gt 0) {
            Write-Host "`nInaktive Geräte:" -ForegroundColor Yellow
            
            # Nummerierung hinzufügen und Spalten umbenennen
            $counter = 1
            $formattedDevices = $inactiveDevices | ForEach-Object {
                [PSCustomObject]@{
                    'Nummer' = $counter++
                    'Name' = $_.DeviceName
                    'Benutzer' = $_.UserPrincipalName
                    'Betriebssystem' = $_.OperatingSystem
                    'Letzter Sync' = $_.LastSyncDateTime
                    'DeviceId' = $_.Id
                }
            }
            
            $formattedDevices | Format-Table Nummer, Name, Benutzer, Betriebssystem, 'Letzter Sync' -AutoSize
            
            # Abfrage: Geräte löschen?
            Write-Host "`n=== Löschoptionen ===" -ForegroundColor Yellow
            Write-Host "Geben Sie die zu löschenden Geräte an:" -ForegroundColor White
            Write-Host "  - Einzelne Nummer: 1" -ForegroundColor Gray
            Write-Host "  - Mehrere Nummern: 1,3,5" -ForegroundColor Gray
            Write-Host "  - Bereich: 1-5" -ForegroundColor Gray
            Write-Host "  - Alle: A" -ForegroundColor Gray
            Write-Host "  - Abbrechen: Enter (leer lassen)`n" -ForegroundColor Gray
            
            $deleteInput = Read-Host "Auswahl"
            
            if ([string]::IsNullOrWhiteSpace($deleteInput)) {
                Write-Host "`nKeine Geräte ausgewählt. Vorgang abgebrochen." -ForegroundColor Yellow
            }
            else {
                # Eingabe parsen und Geräte-Nummern ermitteln
                $selectedNumbers = @()
                
                if ($deleteInput -eq "A" -or $deleteInput -eq "a") {
                    $selectedNumbers = 1..$formattedDevices.Count
                }
                else {
                    $parts = $deleteInput -split ","
                    
                    foreach ($part in $parts) {
                        $part = $part.Trim()
                        
                        if ($part -match "^(\d+)-(\d+)$") {
                            $start = [int]$matches[1]
                            $end = [int]$matches[2]
                            $selectedNumbers += $start..$end
                        }
                        elseif ($part -match "^\d+$") {
                            $selectedNumbers += [int]$part
                        }
                        else {
                            Write-Warning "Ungültige Eingabe: $part (wird ignoriert)"
                        }
                    }
                }
                
                $selectedNumbers = $selectedNumbers | Select-Object -Unique | Sort-Object
                $validNumbers = $selectedNumbers | Where-Object { $_ -ge 1 -and $_ -le $formattedDevices.Count }
                
                if ($validNumbers.Count -eq 0) {
                    Write-Host "`nKeine gültigen Geräte-Nummern ausgewählt. Vorgang abgebrochen." -ForegroundColor Red
                }
                else {
                    $devicesToDelete = $formattedDevices | Where-Object { $validNumbers -contains $_.Nummer }
                    
                    Write-Host "`n=== Folgende Geräte werden gelöscht ===" -ForegroundColor Red
                    $devicesToDelete | Format-Table Nummer, Name, Benutzer, Betriebssystem, 'Letzter Sync' -AutoSize
                    
                    Write-Host "`nACHTUNG: Diese Aktion kann nicht rückgängig gemacht werden!" -ForegroundColor Red
                    $confirmation = Read-Host "Möchten Sie diese Geräte wirklich löschen? (Y/n)"
                    
                    if ($confirmation -eq "Y" -or $confirmation -eq "y") {
                        Write-Host "`nLösche Geräte..." -ForegroundColor Yellow
                        
                        $successCount = 0
                        $failCount = 0
                        
                        foreach ($device in $devicesToDelete) {
                            try {
                                Remove-MgDeviceManagementManagedDevice -ManagedDeviceId $device.DeviceId -Confirm:$false
                                Write-Host "  [OK] $($device.Name)" -ForegroundColor Green
                                $successCount++
                            }
                            catch {
                                Write-Host "  [FEHLER] $($device.Name): $_" -ForegroundColor Red
                                $failCount++
                            }
                        }
                        
                        Write-Host "`n=== Löschvorgang abgeschlossen ===" -ForegroundColor Green
                        Write-Host "Erfolgreich gelöscht: $successCount" -ForegroundColor Green
                        if ($failCount -gt 0) {
                            Write-Host "Fehler: $failCount" -ForegroundColor Red
                        }
                    }
                    else {
                        Write-Host "`nLöschvorgang abgebrochen." -ForegroundColor Yellow
                    }
                }
            }
        }
        else {
            Write-Host "Keine inaktiven Geräte gefunden." -ForegroundColor Green
        }
        
        # Zurück zum Menü
        Read-Host "`nDrücken Sie Enter, um zum Hauptmenü zurückzukehren"
    }
    
    # Funktion: Unassigned Policy Cleanup
    function Start-UnassignedPolicyCleanup {
        Write-Host "`n=== Unassigned Configuration Policy Cleanup ===" -ForegroundColor Cyan
        Write-Host "Identifiziert und löscht nicht zugewiesene Konfigurationsrichtlinien`n" -ForegroundColor Cyan
        
        # Sicherstellen dass Graph-Verbindung besteht
        Connect-ToGraph
        
        Write-Host "Rufe Konfigurationsrichtlinien ab..." -ForegroundColor Yellow
        
        $unassignedPolicies = @()
        
        # 1. Device Configurations abrufen - Beta API via REST (findet mehr als der v1.0 Cmdlet)
        try {
            $deviceConfigsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?`$top=100"
            $deviceConfigsResponse = Invoke-MgGraphRequest -Method GET -Uri $deviceConfigsUri
            $deviceConfigs = $deviceConfigsResponse.value
            
            # Paging falls mehr als 100 vorhanden
            while ($deviceConfigsResponse.'@odata.nextLink') {
                $deviceConfigsResponse = Invoke-MgGraphRequest -Method GET -Uri $deviceConfigsResponse.'@odata.nextLink'
                $deviceConfigs += $deviceConfigsResponse.value
            }
            
            Write-Host "  Device Configurations: $($deviceConfigs.Count) gefunden" -ForegroundColor Gray
            
            foreach ($config in $deviceConfigs) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations('$($config.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    # Prüfen ob Include-Assignments vorhanden sind (Excludes ignorieren)
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $platformType = $config.'@odata.type' -replace '#microsoft.graph.', ''
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $config.id
                            Name = $config.displayName
                            Platform = $platformType
                            Type = "Device Configuration"
                            LastModified = $config.lastModifiedDateTime
                            PolicyType = "DeviceConfiguration"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Device Configuration '$($config.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Device Configurations: $_"
        }
        
        # 2. Configuration Policies (Settings Catalog + Endpoint Security) abrufen - Beta API via REST
        try {
            $configPoliciesUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies?`$top=100"
            $configPoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $configPoliciesUri
            $configPolicies = $configPoliciesResponse.value
            
            # Paging falls mehr als 100 vorhanden
            while ($configPoliciesResponse.'@odata.nextLink') {
                $configPoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $configPoliciesResponse.'@odata.nextLink'
                $configPolicies += $configPoliciesResponse.value
            }
            
            Write-Host "  Configuration Policies (Settings Catalog + Endpoint Security): $($configPolicies.Count) gefunden" -ForegroundColor Gray
            
            foreach ($policy in $configPolicies) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies('$($policy.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    # Prüfen ob Include-Assignments vorhanden sind (Excludes ignorieren)
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $policy.id
                            Name = $policy.name
                            Platform = $policy.platforms
                            Type = "Settings Catalog"
                            LastModified = $policy.lastModifiedDateTime
                            PolicyType = "ConfigurationPolicy"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Policy '$($policy.name)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Configuration Policies: $($_.Exception.Message)"
        }
        
        # 3. Group Policy Configurations (Administrative Templates) abrufen - Beta API via REST
        try {
            $groupPoliciesUri = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations?`$top=100"
            $groupPoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $groupPoliciesUri
            $groupPolicies = $groupPoliciesResponse.value
            
            # Paging falls mehr als 100 vorhanden
            while ($groupPoliciesResponse.'@odata.nextLink') {
                $groupPoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $groupPoliciesResponse.'@odata.nextLink'
                $groupPolicies += $groupPoliciesResponse.value
            }
            
            Write-Host "  Group Policy Configurations (Administrative Templates): $($groupPolicies.Count) gefunden" -ForegroundColor Gray
            
            foreach ($gp in $groupPolicies) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations('$($gp.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    # Prüfen ob Include-Assignments vorhanden sind (Excludes ignorieren)
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $gp.id
                            Name = $gp.displayName
                            Platform = "Windows"
                            Type = "Administrative Template"
                            LastModified = $gp.lastModifiedDateTime
                            PolicyType = "GroupPolicyConfiguration"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Policy '$($gp.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Group Policy Configurations: $($_.Exception.Message)"
        }
        
        # Ergebnisse ausgeben
        Write-Host "`n=== Ergebnis ===" -ForegroundColor Green
        Write-Host "Nicht zugewiesene Richtlinien: $($unassignedPolicies.Count)" -ForegroundColor Yellow
        
        if ($unassignedPolicies.Count -gt 0) {
            Write-Host "`nNicht zugewiesene Richtlinien:" -ForegroundColor Yellow
            
            # Nummerierung hinzufügen
            $counter = 1
            $formattedPolicies = $unassignedPolicies | ForEach-Object {
                [PSCustomObject]@{
                    'Nummer' = $counter++
                    'Name' = $_.Name
                    'Plattform' = $_.Platform
                    'Typ' = $_.Type
                    'Last Modified' = $_.LastModified
                    'Id' = $_.Id
                    'PolicyType' = $_.PolicyType
                }
            }
            
            $formattedPolicies | Format-Table Nummer, Name, Plattform, Typ, 'Last Modified' -AutoSize
            
            # Abfrage: Richtlinien löschen?
            Write-Host "`n=== Löschoptionen ===" -ForegroundColor Yellow
            Write-Host "Geben Sie die zu löschenden Richtlinien an:" -ForegroundColor White
            Write-Host "  - Einzelne Nummer: 1" -ForegroundColor Gray
            Write-Host "  - Mehrere Nummern: 1,3,5" -ForegroundColor Gray
            Write-Host "  - Bereich: 1-5" -ForegroundColor Gray
            Write-Host "  - Alle: A" -ForegroundColor Gray
            Write-Host "  - Abbrechen: Enter (leer lassen)`n" -ForegroundColor Gray
            
            $deleteInput = Read-Host "Auswahl"
            
            if ([string]::IsNullOrWhiteSpace($deleteInput)) {
                Write-Host "`nKeine Richtlinien ausgewählt. Vorgang abgebrochen." -ForegroundColor Yellow
            }
            else {
                # Eingabe parsen
                $selectedNumbers = @()
                
                if ($deleteInput -eq "A" -or $deleteInput -eq "a") {
                    $selectedNumbers = 1..$formattedPolicies.Count
                }
                else {
                    $parts = $deleteInput -split ","
                    
                    foreach ($part in $parts) {
                        $part = $part.Trim()
                        
                        if ($part -match "^(\d+)-(\d+)$") {
                            $start = [int]$matches[1]
                            $end = [int]$matches[2]
                            $selectedNumbers += $start..$end
                        }
                        elseif ($part -match "^\d+$") {
                            $selectedNumbers += [int]$part
                        }
                        else {
                            Write-Warning "Ungültige Eingabe: $part (wird ignoriert)"
                        }
                    }
                }
                
                $selectedNumbers = $selectedNumbers | Select-Object -Unique | Sort-Object
                $validNumbers = $selectedNumbers | Where-Object { $_ -ge 1 -and $_ -le $formattedPolicies.Count }
                
                if ($validNumbers.Count -eq 0) {
                    Write-Host "`nKeine gültigen Richtlinien-Nummern ausgewählt. Vorgang abgebrochen." -ForegroundColor Red
                }
                else {
                    $policiesToDelete = $formattedPolicies | Where-Object { $validNumbers -contains $_.Nummer }
                    
                    Write-Host "`n=== Folgende Richtlinien werden gelöscht ===" -ForegroundColor Red
                    $policiesToDelete | Format-Table Nummer, Name, Plattform, Typ, 'Last Modified' -AutoSize
                    
                    Write-Host "`nACHTUNG: Diese Aktion kann nicht rückgängig gemacht werden!" -ForegroundColor Red
                    $confirmation = Read-Host "Möchten Sie diese Richtlinien wirklich löschen? (Y/n)"
                    
                    if ($confirmation -eq "Y" -or $confirmation -eq "y") {
                        Write-Host "`nLösche Richtlinien..." -ForegroundColor Yellow
                        
                        $successCount = 0
                        $failCount = 0
                        
                        foreach ($policy in $policiesToDelete) {
                            try {
                                switch ($policy.PolicyType) {
                                    "DeviceConfiguration" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "ConfigurationPolicy" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "GroupPolicyConfiguration" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                }
                                Write-Host "  [OK] $($policy.Name)" -ForegroundColor Green
                                $successCount++
                            }
                            catch {
                                Write-Host "  [FEHLER] $($policy.Name): $_" -ForegroundColor Red
                                $failCount++
                            }
                        }
                        
                        Write-Host "`n=== Löschvorgang abgeschlossen ===" -ForegroundColor Green
                        Write-Host "Erfolgreich gelöscht: $successCount" -ForegroundColor Green
                        if ($failCount -gt 0) {
                            Write-Host "Fehler: $failCount" -ForegroundColor Red
                        }
                    }
                    else {
                        Write-Host "`nLöschvorgang abgebrochen." -ForegroundColor Yellow
                    }
                }
            }
        }
        else {
            Write-Host "Keine nicht zugewiesenen Richtlinien gefunden." -ForegroundColor Green
        }
        
        # Zurück zum Menü
        Read-Host "`nDrücken Sie Enter, um zum Hauptmenü zurückzukehren"
    }
    
    # Funktion: App Cleanup
    function Start-AppCleanup {
        Write-Host "`n=== App Cleanup ===" -ForegroundColor Cyan
        Write-Host "Identifiziert und löscht nicht zugewiesene Apps`n" -ForegroundColor Cyan
        
        # Sicherstellen dass Graph-Verbindung besteht
        Connect-ToGraph
        
        Write-Host "Rufe Apps ab..." -ForegroundColor Yellow
        
        $unassignedApps = @()
        
        try {
            $mobileAppsUri = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$top=100"
            $mobileAppsResponse = Invoke-MgGraphRequest -Method GET -Uri $mobileAppsUri
            $mobileApps = $mobileAppsResponse.value
            
            # Paging falls mehr als 100 vorhanden
            while ($mobileAppsResponse.'@odata.nextLink') {
                $mobileAppsResponse = Invoke-MgGraphRequest -Method GET -Uri $mobileAppsResponse.'@odata.nextLink'
                $mobileApps += $mobileAppsResponse.value
            }
            
            Write-Host "  Mobile Apps: $($mobileApps.Count) gefunden" -ForegroundColor Gray
            
            foreach ($app in $mobileApps) {
                if ($app.isAssigned -eq $false) {
                    # Plattform aus @odata.type ableiten
                    $appType = $app.'@odata.type' -replace '#microsoft.graph.', ''
                    
                    $unassignedApps += [PSCustomObject]@{
                        Id = $app.id
                        Name = $app.displayName
                        Version = $app.displayVersion
                        Publisher = $app.publisher
                        Platform = $appType
                        Created = $app.createdDateTime
                        LastModified = $app.lastModifiedDateTime
                        AppType = $appType
                    }
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Apps: $($_.Exception.Message)"
        }
        
        # Ergebnisse ausgeben
        Write-Host "`n=== Ergebnis ===" -ForegroundColor Green
        Write-Host "Gesamtanzahl Apps: $($mobileApps.Count)" -ForegroundColor White
        Write-Host "Nicht zugewiesene Apps: $($unassignedApps.Count)" -ForegroundColor Yellow
        
        if ($unassignedApps.Count -gt 0) {
            Write-Host "`nNicht zugewiesene Apps:" -ForegroundColor Yellow
            
            # Nummerierung hinzufügen
            $counter = 1
            $formattedApps = $unassignedApps | ForEach-Object {
                [PSCustomObject]@{
                    'Nummer' = $counter++
                    'Name' = $_.Name
                    'Version' = $_.Version
                    'Publisher' = $_.Publisher
                    'Plattform' = $_.Platform
                    'Created' = $_.Created
                    'Last Modified' = $_.LastModified
                    'Id' = $_.Id
                    'AppType' = $_.AppType
                }
            }
            
            $formattedApps | Format-Table Nummer, Name, Version, Publisher, Plattform, Created, 'Last Modified' -AutoSize
            
            # Abfrage: Apps löschen?
            Write-Host "`n=== Löschoptionen ===" -ForegroundColor Yellow
            Write-Host "Geben Sie die zu löschenden Apps an:" -ForegroundColor White
            Write-Host "  - Einzelne Nummer: 1" -ForegroundColor Gray
            Write-Host "  - Mehrere Nummern: 1,3,5" -ForegroundColor Gray
            Write-Host "  - Bereich: 1-5" -ForegroundColor Gray
            Write-Host "  - Alle: A" -ForegroundColor Gray
            Write-Host "  - Abbrechen: Enter (leer lassen)`n" -ForegroundColor Gray
            
            $deleteInput = Read-Host "Auswahl"
            
            if ([string]::IsNullOrWhiteSpace($deleteInput)) {
                Write-Host "`nKeine Apps ausgewählt. Vorgang abgebrochen." -ForegroundColor Yellow
            }
            else {
                # Eingabe parsen
                $selectedNumbers = @()
                
                if ($deleteInput -eq "A" -or $deleteInput -eq "a") {
                    $selectedNumbers = 1..$formattedApps.Count
                }
                else {
                    $parts = $deleteInput -split ","
                    
                    foreach ($part in $parts) {
                        $part = $part.Trim()
                        
                        if ($part -match "^(\d+)-(\d+)$") {
                            $start = [int]$matches[1]
                            $end = [int]$matches[2]
                            $selectedNumbers += $start..$end
                        }
                        elseif ($part -match "^\d+$") {
                            $selectedNumbers += [int]$part
                        }
                        else {
                            Write-Warning "Ungültige Eingabe: $part (wird ignoriert)"
                        }
                    }
                }
                
                $selectedNumbers = $selectedNumbers | Select-Object -Unique | Sort-Object
                $validNumbers = $selectedNumbers | Where-Object { $_ -ge 1 -and $_ -le $formattedApps.Count }
                
                if ($validNumbers.Count -eq 0) {
                    Write-Host "`nKeine gültigen App-Nummern ausgewählt. Vorgang abgebrochen." -ForegroundColor Red
                }
                else {
                    $appsToDelete = $formattedApps | Where-Object { $validNumbers -contains $_.Nummer }
                    
                    Write-Host "`n=== Folgende Apps werden gelöscht ===" -ForegroundColor Red
                    $appsToDelete | Format-Table Nummer, Name, Version, Publisher, Plattform, Created, 'Last Modified' -AutoSize
                    
                    Write-Host "`nACHTUNG: Diese Aktion kann nicht rückgängig gemacht werden!" -ForegroundColor Red
                    $confirmation = Read-Host "Möchten Sie diese Apps wirklich löschen? (Y/n)"
                    
                    if ($confirmation -eq "Y" -or $confirmation -eq "y") {
                        Write-Host "`nLösche Apps..." -ForegroundColor Yellow
                        
                        $successCount = 0
                        $failCount = 0
                        
                        foreach ($app in $appsToDelete) {
                            try {
                                $deleteUri = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps('$($app.Id)')"
                                Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                Write-Host "  [OK] $($app.Name)" -ForegroundColor Green
                                $successCount++
                            }
                            catch {
                                Write-Host "  [FEHLER] $($app.Name): $_" -ForegroundColor Red
                                $failCount++
                            }
                        }
                        
                        Write-Host "`n=== Löschvorgang abgeschlossen ===" -ForegroundColor Green
                        Write-Host "Erfolgreich gelöscht: $successCount" -ForegroundColor Green
                        if ($failCount -gt 0) {
                            Write-Host "Fehler: $failCount" -ForegroundColor Red
                        }
                    }
                    else {
                        Write-Host "`nLöschvorgang abgebrochen." -ForegroundColor Yellow
                    }
                }
            }
        }
        else {
            Write-Host "Keine nicht zugewiesenen Apps gefunden." -ForegroundColor Green
        }
        
        # Zurück zum Menü
        Read-Host "`nDrücken Sie Enter, um zum Hauptmenü zurückzukehren"
    }
    
    # Funktion: Compliance Policy Cleanup
    function Start-CompliancePolicyCleanup {
        Write-Host "`n=== Compliance Policy Cleanup ===" -ForegroundColor Cyan
        Write-Host "Identifiziert und löscht nicht zugewiesene Compliance-Richtlinien`n" -ForegroundColor Cyan
        
        # Sicherstellen dass Graph-Verbindung besteht
        Connect-ToGraph
        
        Write-Host "Rufe Compliance-Richtlinien ab..." -ForegroundColor Yellow
        
        $unassignedPolicies = @()
        
        try {
            $compliancePoliciesUri = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies?`$top=100"
            $compliancePoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $compliancePoliciesUri
            $compliancePolicies = $compliancePoliciesResponse.value
            
            # Paging falls mehr als 100 vorhanden
            while ($compliancePoliciesResponse.'@odata.nextLink') {
                $compliancePoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $compliancePoliciesResponse.'@odata.nextLink'
                $compliancePolicies += $compliancePoliciesResponse.value
            }
            
            Write-Host "  Compliance Policies: $($compliancePolicies.Count) gefunden" -ForegroundColor Gray
            
            foreach ($policy in $compliancePolicies) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies('$($policy.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    # Prüfen ob Include-Assignments vorhanden sind (Excludes ignorieren)
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        # Plattform aus @odata.type ableiten
                        $platformType = $policy.'@odata.type' -replace '#microsoft.graph.', ''
                        
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $policy.id
                            Name = $policy.displayName
                            Platform = $platformType
                            Created = $policy.createdDateTime
                            LastModified = $policy.lastModifiedDateTime
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Compliance Policy '$($policy.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Compliance Policies: $($_.Exception.Message)"
        }
        
        # Ergebnisse ausgeben
        Write-Host "`n=== Ergebnis ===" -ForegroundColor Green
        Write-Host "Gesamtanzahl Compliance Policies: $($compliancePolicies.Count)" -ForegroundColor White
        Write-Host "Nicht zugewiesene Compliance Policies: $($unassignedPolicies.Count)" -ForegroundColor Yellow
        
        if ($unassignedPolicies.Count -gt 0) {
            Write-Host "`nNicht zugewiesene Compliance Policies:" -ForegroundColor Yellow
            
            # Nummerierung hinzufügen
            $counter = 1
            $formattedPolicies = $unassignedPolicies | ForEach-Object {
                [PSCustomObject]@{
                    'Nummer' = $counter++
                    'Name' = $_.Name
                    'Plattform' = $_.Platform
                    'Created' = $_.Created
                    'Last Modified' = $_.LastModified
                    'Id' = $_.Id
                }
            }
            
            $formattedPolicies | Format-Table Nummer, Name, Plattform, Created, 'Last Modified' -AutoSize
            
            # Abfrage: Policies löschen?
            Write-Host "`n=== Löschoptionen ===" -ForegroundColor Yellow
            Write-Host "Geben Sie die zu löschenden Compliance Policies an:" -ForegroundColor White
            Write-Host "  - Einzelne Nummer: 1" -ForegroundColor Gray
            Write-Host "  - Mehrere Nummern: 1,3,5" -ForegroundColor Gray
            Write-Host "  - Bereich: 1-5" -ForegroundColor Gray
            Write-Host "  - Alle: A" -ForegroundColor Gray
            Write-Host "  - Abbrechen: Enter (leer lassen)`n" -ForegroundColor Gray
            
            $deleteInput = Read-Host "Auswahl"
            
            if ([string]::IsNullOrWhiteSpace($deleteInput)) {
                Write-Host "`nKeine Compliance Policies ausgewählt. Vorgang abgebrochen." -ForegroundColor Yellow
            }
            else {
                # Eingabe parsen
                $selectedNumbers = @()
                
                if ($deleteInput -eq "A" -or $deleteInput -eq "a") {
                    $selectedNumbers = 1..$formattedPolicies.Count
                }
                else {
                    $parts = $deleteInput -split ","
                    
                    foreach ($part in $parts) {
                        $part = $part.Trim()
                        
                        if ($part -match "^(\d+)-(\d+)$") {
                            $start = [int]$matches[1]
                            $end = [int]$matches[2]
                            $selectedNumbers += $start..$end
                        }
                        elseif ($part -match "^\d+$") {
                            $selectedNumbers += [int]$part
                        }
                        else {
                            Write-Warning "Ungültige Eingabe: $part (wird ignoriert)"
                        }
                    }
                }
                
                $selectedNumbers = $selectedNumbers | Select-Object -Unique | Sort-Object
                $validNumbers = $selectedNumbers | Where-Object { $_ -ge 1 -and $_ -le $formattedPolicies.Count }
                
                if ($validNumbers.Count -eq 0) {
                    Write-Host "`nKeine gültigen Compliance Policy-Nummern ausgewählt. Vorgang abgebrochen." -ForegroundColor Red
                }
                else {
                    $policiesToDelete = $formattedPolicies | Where-Object { $validNumbers -contains $_.Nummer }
                    
                    Write-Host "`n=== Folgende Compliance Policies werden gelöscht ===" -ForegroundColor Red
                    $policiesToDelete | Format-Table Nummer, Name, Plattform, Created, 'Last Modified' -AutoSize
                    
                    Write-Host "`nACHTUNG: Diese Aktion kann nicht rückgängig gemacht werden!" -ForegroundColor Red
                    $confirmation = Read-Host "Möchten Sie diese Compliance Policies wirklich löschen? (Y/n)"
                    
                    if ($confirmation -eq "Y" -or $confirmation -eq "y") {
                        Write-Host "`nLösche Compliance Policies..." -ForegroundColor Yellow
                        
                        $successCount = 0
                        $failCount = 0
                        
                        foreach ($policy in $policiesToDelete) {
                            try {
                                $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies('$($policy.Id)')"
                                Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                Write-Host "  [OK] $($policy.Name)" -ForegroundColor Green
                                $successCount++
                            }
                            catch {
                                Write-Host "  [FEHLER] $($policy.Name): $_" -ForegroundColor Red
                                $failCount++
                            }
                        }
                        
                        Write-Host "`n=== Löschvorgang abgeschlossen ===" -ForegroundColor Green
                        Write-Host "Erfolgreich gelöscht: $successCount" -ForegroundColor Green
                        if ($failCount -gt 0) {
                            Write-Host "Fehler: $failCount" -ForegroundColor Red
                        }
                    }
                    else {
                        Write-Host "`nLöschvorgang abgebrochen." -ForegroundColor Yellow
                    }
                }
            }
        }
        else {
            Write-Host "Keine nicht zugewiesenen Compliance Policies gefunden." -ForegroundColor Green
        }
        
        # Zurück zum Menü
        Read-Host "`nDrücken Sie Enter, um zum Hauptmenü zurückzukehren"
    }
    
    # Funktion: Windows Update Policy Cleanup
    function Start-WindowsUpdatePolicyCleanup {
        Write-Host "`n=== Windows Update Policy Cleanup ===" -ForegroundColor Cyan
        Write-Host "Identifiziert und löscht nicht zugewiesene Windows Update-Richtlinien`n" -ForegroundColor Cyan
        
        # Sicherstellen dass Graph-Verbindung besteht
        Connect-ToGraph
        
        Write-Host "Rufe Windows Update-Richtlinien ab..." -ForegroundColor Yellow
        
        $unassignedPolicies = @()
        
        # 1. Update-Ringe aus deviceConfigurations filtern
        try {
            $deviceConfigsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?`$top=100&`$filter=isof('microsoft.graph.windowsUpdateForBusinessConfiguration')"
            $deviceConfigsResponse = Invoke-MgGraphRequest -Method GET -Uri $deviceConfigsUri
            $updateRings = $deviceConfigsResponse.value
            
            while ($deviceConfigsResponse.'@odata.nextLink') {
                $deviceConfigsResponse = Invoke-MgGraphRequest -Method GET -Uri $deviceConfigsResponse.'@odata.nextLink'
                $updateRings += $deviceConfigsResponse.value
            }
            
            Write-Host "  Update-Ringe: $($updateRings.Count) gefunden" -ForegroundColor Gray
            
            foreach ($ring in $updateRings) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations('$($ring.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $ring.id
                            Name = $ring.displayName
                            Type = "Update-Ring"
                            Created = $ring.createdDateTime
                            LastModified = $ring.lastModifiedDateTime
                            PolicyType = "UpdateRing"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Update-Ring '$($ring.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Update-Ringen: $($_.Exception.Message)"
        }
        
        # 2. Driver Update Profiles
        try {
            $driverProfilesUri = "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles?`$top=100"
            $driverProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $driverProfilesUri
            $driverProfiles = $driverProfilesResponse.value
            
            while ($driverProfilesResponse.'@odata.nextLink') {
                $driverProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $driverProfilesResponse.'@odata.nextLink'
                $driverProfiles += $driverProfilesResponse.value
            }
            
            Write-Host "  Driver Update Profiles: $($driverProfiles.Count) gefunden" -ForegroundColor Gray
            
            foreach ($profile in $driverProfiles) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles('$($profile.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $profile.id
                            Name = $profile.displayName
                            Type = "Driver Update Profile"
                            Created = $profile.createdDateTime
                            LastModified = $profile.lastModifiedDateTime
                            PolicyType = "DriverUpdateProfile"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Driver Update Profile '$($profile.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Driver Update Profiles: $($_.Exception.Message)"
        }
        
        # 3. Feature Update Profiles
        try {
            $featureProfilesUri = "https://graph.microsoft.com/beta/deviceManagement/windowsFeatureUpdateProfiles?`$top=100"
            $featureProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $featureProfilesUri
            $featureProfiles = $featureProfilesResponse.value
            
            while ($featureProfilesResponse.'@odata.nextLink') {
                $featureProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $featureProfilesResponse.'@odata.nextLink'
                $featureProfiles += $featureProfilesResponse.value
            }
            
            Write-Host "  Feature Update Profiles: $($featureProfiles.Count) gefunden" -ForegroundColor Gray
            
            foreach ($profile in $featureProfiles) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/windowsFeatureUpdateProfiles('$($profile.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $profile.id
                            Name = $profile.displayName
                            Type = "Feature Update Profile"
                            Created = $profile.createdDateTime
                            LastModified = $profile.lastModifiedDateTime
                            PolicyType = "FeatureUpdateProfile"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Feature Update Profile '$($profile.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Feature Update Profiles: $($_.Exception.Message)"
        }
        
        # 4. Quality Update Profiles
        try {
            $qualityProfilesUri = "https://graph.microsoft.com/beta/deviceManagement/windowsQualityUpdateProfiles?`$top=100"
            $qualityProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $qualityProfilesUri
            $qualityProfiles = $qualityProfilesResponse.value
            
            while ($qualityProfilesResponse.'@odata.nextLink') {
                $qualityProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $qualityProfilesResponse.'@odata.nextLink'
                $qualityProfiles += $qualityProfilesResponse.value
            }
            
            Write-Host "  Quality Update Profiles: $($qualityProfiles.Count) gefunden" -ForegroundColor Gray
            
            foreach ($profile in $qualityProfiles) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/windowsQualityUpdateProfiles('$($profile.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $profile.id
                            Name = $profile.displayName
                            Type = "Quality Update Profile"
                            Created = $profile.createdDateTime
                            LastModified = $profile.lastModifiedDateTime
                            PolicyType = "QualityUpdateProfile"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Quality Update Profile '$($profile.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Quality Update Profiles: $($_.Exception.Message)"
        }
        
        # 5. Quality Update Policies
        try {
            $qualityPoliciesUri = "https://graph.microsoft.com/beta/deviceManagement/windowsQualityUpdatePolicies?`$top=100"
            $qualityPoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $qualityPoliciesUri
            $qualityPolicies = $qualityPoliciesResponse.value
            
            while ($qualityPoliciesResponse.'@odata.nextLink') {
                $qualityPoliciesResponse = Invoke-MgGraphRequest -Method GET -Uri $qualityPoliciesResponse.'@odata.nextLink'
                $qualityPolicies += $qualityPoliciesResponse.value
            }
            
            Write-Host "  Expedited Quality Updates: $($qualityPolicies.Count) gefunden" -ForegroundColor Gray
            
            foreach ($policy in $qualityPolicies) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/windowsQualityUpdatePolicies('$($policy.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedPolicies += [PSCustomObject]@{
                            Id = $policy.id
                            Name = $policy.displayName
                            Type = "Expedited Quality Update"
                            Created = $policy.createdDateTime
                            LastModified = $policy.lastModifiedDateTime
                            PolicyType = "QualityUpdatePolicy"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Quality Update Policy '$($policy.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Quality Update Policies: $($_.Exception.Message)"
        }
        
        # Ergebnisse ausgeben
        Write-Host "`n=== Ergebnis ===" -ForegroundColor Green
        Write-Host "Nicht zugewiesene Windows Update-Richtlinien: $($unassignedPolicies.Count)" -ForegroundColor Yellow
        
        if ($unassignedPolicies.Count -gt 0) {
            Write-Host "`nNicht zugewiesene Windows Update-Richtlinien:" -ForegroundColor Yellow
            
            # Nummerierung hinzufügen
            $counter = 1
            $formattedPolicies = $unassignedPolicies | ForEach-Object {
                [PSCustomObject]@{
                    'Nummer' = $counter++
                    'Name' = $_.Name
                    'Typ' = $_.Type
                    'Created' = $_.Created
                    'Last Modified' = $_.LastModified
                    'Id' = $_.Id
                    'PolicyType' = $_.PolicyType
                }
            }
            
            $formattedPolicies | Format-Table Nummer, Name, Typ, Created, 'Last Modified' -AutoSize
            
            # Abfrage: Policies löschen?
            Write-Host "`n=== Löschoptionen ===" -ForegroundColor Yellow
            Write-Host "Geben Sie die zu löschenden Windows Update-Richtlinien an:" -ForegroundColor White
            Write-Host "  - Einzelne Nummer: 1" -ForegroundColor Gray
            Write-Host "  - Mehrere Nummern: 1,3,5" -ForegroundColor Gray
            Write-Host "  - Bereich: 1-5" -ForegroundColor Gray
            Write-Host "  - Alle: A" -ForegroundColor Gray
            Write-Host "  - Abbrechen: Enter (leer lassen)`n" -ForegroundColor Gray
            
            $deleteInput = Read-Host "Auswahl"
            
            if ([string]::IsNullOrWhiteSpace($deleteInput)) {
                Write-Host "`nKeine Windows Update-Richtlinien ausgewählt. Vorgang abgebrochen." -ForegroundColor Yellow
            }
            else {
                # Eingabe parsen
                $selectedNumbers = @()
                
                if ($deleteInput -eq "A" -or $deleteInput -eq "a") {
                    $selectedNumbers = 1..$formattedPolicies.Count
                }
                else {
                    $parts = $deleteInput -split ","
                    
                    foreach ($part in $parts) {
                        $part = $part.Trim()
                        
                        if ($part -match "^(\d+)-(\d+)$") {
                            $start = [int]$matches[1]
                            $end = [int]$matches[2]
                            $selectedNumbers += $start..$end
                        }
                        elseif ($part -match "^\d+$") {
                            $selectedNumbers += [int]$part
                        }
                        else {
                            Write-Warning "Ungültige Eingabe: $part (wird ignoriert)"
                        }
                    }
                }
                
                $selectedNumbers = $selectedNumbers | Select-Object -Unique | Sort-Object
                $validNumbers = $selectedNumbers | Where-Object { $_ -ge 1 -and $_ -le $formattedPolicies.Count }
                
                if ($validNumbers.Count -eq 0) {
                    Write-Host "`nKeine gültigen Windows Update-Richtlinien-Nummern ausgewählt. Vorgang abgebrochen." -ForegroundColor Red
                }
                else {
                    $policiesToDelete = $formattedPolicies | Where-Object { $validNumbers -contains $_.Nummer }
                    
                    Write-Host "`n=== Folgende Windows Update-Richtlinien werden gelöscht ===" -ForegroundColor Red
                    $policiesToDelete | Format-Table Nummer, Name, Typ, Created, 'Last Modified' -AutoSize
                    
                    Write-Host "`nACHTUNG: Diese Aktion kann nicht rückgängig gemacht werden!" -ForegroundColor Red
                    $confirmation = Read-Host "Möchten Sie diese Windows Update-Richtlinien wirklich löschen? (Y/n)"
                    
                    if ($confirmation -eq "Y" -or $confirmation -eq "y") {
                        Write-Host "`nLösche Windows Update-Richtlinien..." -ForegroundColor Yellow
                        
                        $successCount = 0
                        $failCount = 0
                        
                        foreach ($policy in $policiesToDelete) {
                            try {
                                switch ($policy.PolicyType) {
                                    "UpdateRing" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "DriverUpdateProfile" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "FeatureUpdateProfile" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/windowsFeatureUpdateProfiles('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "QualityUpdateProfile" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/windowsQualityUpdateProfiles('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "QualityUpdatePolicy" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/windowsQualityUpdatePolicies('$($policy.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                }
                                Write-Host "  [OK] $($policy.Name)" -ForegroundColor Green
                                $successCount++
                            }
                            catch {
                                Write-Host "  [FEHLER] $($policy.Name): $_" -ForegroundColor Red
                                $failCount++
                            }
                        }
                        
                        Write-Host "`n=== Löschvorgang abgeschlossen ===" -ForegroundColor Green
                        Write-Host "Erfolgreich gelöscht: $successCount" -ForegroundColor Green
                        if ($failCount -gt 0) {
                            Write-Host "Fehler: $failCount" -ForegroundColor Red
                        }
                    }
                    else {
                        Write-Host "`nLöschvorgang abgebrochen." -ForegroundColor Yellow
                    }
                }
            }
        }
        else {
            Write-Host "Keine nicht zugewiesenen Windows Update-Richtlinien gefunden." -ForegroundColor Green
        }
        
        # Zurück zum Menü
        Read-Host "`nDrücken Sie Enter, um zum Hauptmenü zurückzukehren"
    }
    
    # Funktion: Enrollment Profile Cleanup
    function Start-EnrollmentProfileCleanup {
        Write-Host "`n=== Enrollment Profile Cleanup ===" -ForegroundColor Cyan
        Write-Host "Identifiziert und löscht nicht zugewiesene Enrollment-Profile`n" -ForegroundColor Cyan
        
        # Sicherstellen dass Graph-Verbindung besteht
        Connect-ToGraph
        
        Write-Host "Rufe Enrollment-Profile ab..." -ForegroundColor Yellow
        
        $unassignedProfiles = @()
        
        # 1. Windows Autopilot Deployment Profiles
        try {
            $autopilotProfilesUri = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles?`$top=100"
            $autopilotProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $autopilotProfilesUri
            $autopilotProfiles = $autopilotProfilesResponse.value
            
            while ($autopilotProfilesResponse.'@odata.nextLink') {
                $autopilotProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $autopilotProfilesResponse.'@odata.nextLink'
                $autopilotProfiles += $autopilotProfilesResponse.value
            }
            
            Write-Host "  Windows Autopilot Profiles: $($autopilotProfiles.Count) gefunden" -ForegroundColor Gray
            
            foreach ($profile in $autopilotProfiles) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles('$($profile.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedProfiles += [PSCustomObject]@{
                            Id = $profile.id
                            Name = $profile.displayName
                            Platform = "Windows"
                            Type = "Autopilot Profile"
                            Created = $profile.createdDateTime
                            LastModified = $profile.lastModifiedDateTime
                            ProfileType = "AutopilotProfile"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Autopilot Profile '$($profile.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Autopilot Profiles: $($_.Exception.Message)"
        }
        
        # 2. iOS/Apple User Initiated Enrollment Profiles
        try {
            $iosProfilesUri = "https://graph.microsoft.com/beta/deviceManagement/appleUserInitiatedEnrollmentProfiles?`$top=100"
            $iosProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $iosProfilesUri
            $iosProfiles = $iosProfilesResponse.value
            
            while ($iosProfilesResponse.'@odata.nextLink') {
                $iosProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $iosProfilesResponse.'@odata.nextLink'
                $iosProfiles += $iosProfilesResponse.value
            }
            
            Write-Host "  iOS Enrollment Profiles: $($iosProfiles.Count) gefunden" -ForegroundColor Gray
            
            foreach ($profile in $iosProfiles) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/appleUserInitiatedEnrollmentProfiles('$($profile.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedProfiles += [PSCustomObject]@{
                            Id = $profile.id
                            Name = $profile.displayName
                            Platform = "iOS"
                            Type = "iOS Enrollment Profile"
                            Created = $profile.createdDateTime
                            LastModified = $profile.lastModifiedDateTime
                            ProfileType = "iOSEnrollmentProfile"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei iOS Enrollment Profile '$($profile.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von iOS Enrollment Profiles: $($_.Exception.Message)"
        }
        
        # 3. Android Enrollment Profiles (beide Endpunkte abrufen und deduplizieren)
        $androidProfiles = @()
        
        # 3a. Android Device Owner Enrollment Profiles
        try {
            $androidOwnerProfilesUri = "https://graph.microsoft.com/beta/deviceManagement/androidDeviceOwnerEnrollmentProfiles?`$top=100"
            $androidOwnerProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $androidOwnerProfilesUri
            $androidOwnerProfiles = $androidOwnerProfilesResponse.value
            
            while ($androidOwnerProfilesResponse.'@odata.nextLink') {
                $androidOwnerProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $androidOwnerProfilesResponse.'@odata.nextLink'
                $androidOwnerProfiles += $androidOwnerProfilesResponse.value
            }
            
            $androidProfiles += $androidOwnerProfiles
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Android Device Owner Enrollment Profiles: $($_.Exception.Message)"
        }
        
        # 3b. Android For Work Enrollment Profiles
        try {
            $androidForWorkProfilesUri = "https://graph.microsoft.com/beta/deviceManagement/androidForWorkEnrollmentProfiles?`$top=100"
            $androidForWorkProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $androidForWorkProfilesUri
            $androidForWorkProfiles = $androidForWorkProfilesResponse.value
            
            while ($androidForWorkProfilesResponse.'@odata.nextLink') {
                $androidForWorkProfilesResponse = Invoke-MgGraphRequest -Method GET -Uri $androidForWorkProfilesResponse.'@odata.nextLink'
                $androidForWorkProfiles += $androidForWorkProfilesResponse.value
            }
            
            $androidProfiles += $androidForWorkProfiles
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Android For Work Enrollment Profiles: $($_.Exception.Message)"
        }
        
        # Deduplizierung basierend auf ID
        $androidProfiles = $androidProfiles | Sort-Object -Property id -Unique
        
        Write-Host "  Android Enrollment Profiles: $($androidProfiles.Count) gefunden" -ForegroundColor Gray
        
        foreach ($profile in $androidProfiles) {
            try {
                # Assignments-Endpunkt ermitteln (abhängig vom odata.type)
                $assignmentsUri = $null
                if ($profile.'@odata.type' -eq '#microsoft.graph.androidDeviceOwnerEnrollmentProfile') {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/androidDeviceOwnerEnrollmentProfiles('$($profile.id)')/assignments"
                }
                elseif ($profile.'@odata.type' -eq '#microsoft.graph.androidForWorkEnrollmentProfile') {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/androidForWorkEnrollmentProfiles('$($profile.id)')/assignments"
                }
                
                if ($assignmentsUri) {
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedProfiles += [PSCustomObject]@{
                            Id = $profile.id
                            Name = $profile.displayName
                            Platform = "Android"
                            Type = "Android Enrollment Profile"
                            Created = $profile.createdDateTime
                            LastModified = $profile.lastModifiedDateTime
                            ProfileType = $profile.'@odata.type' -replace '#microsoft.graph.', ''
                        }
                    }
                }
            }
            catch {
                Write-Warning "Fehler bei Android Enrollment Profile '$($profile.displayName)': $($_.Exception.Message)"
            }
        }
        
        # Ergebnisse ausgeben
        Write-Host "`n=== Ergebnis ===" -ForegroundColor Green
        Write-Host "Nicht zugewiesene Enrollment-Profile: $($unassignedProfiles.Count)" -ForegroundColor Yellow
        
        if ($unassignedProfiles.Count -gt 0) {
            Write-Host "`nNicht zugewiesene Enrollment-Profile:" -ForegroundColor Yellow
            
            # Nummerierung hinzufügen
            $counter = 1
            $formattedProfiles = $unassignedProfiles | ForEach-Object {
                [PSCustomObject]@{
                    'Nummer' = $counter++
                    'Name' = $_.Name
                    'Plattform' = $_.Platform
                    'Typ' = $_.Type
                    'Created' = $_.Created
                    'Last Modified' = $_.LastModified
                    'Id' = $_.Id
                    'ProfileType' = $_.ProfileType
                }
            }
            
            $formattedProfiles | Format-Table Nummer, Name, Plattform, Typ, Created, 'Last Modified' -AutoSize
            
            # Abfrage: Profile löschen?
            Write-Host "`n=== Löschoptionen ===" -ForegroundColor Yellow
            Write-Host "Geben Sie die zu löschenden Enrollment-Profile an:" -ForegroundColor White
            Write-Host "  - Einzelne Nummer: 1" -ForegroundColor Gray
            Write-Host "  - Mehrere Nummern: 1,3,5" -ForegroundColor Gray
            Write-Host "  - Bereich: 1-5" -ForegroundColor Gray
            Write-Host "  - Alle: A" -ForegroundColor Gray
            Write-Host "  - Abbrechen: Enter (leer lassen)`n" -ForegroundColor Gray
            
            $deleteInput = Read-Host "Auswahl"
            
            if ([string]::IsNullOrWhiteSpace($deleteInput)) {
                Write-Host "`nKeine Enrollment-Profile ausgewählt. Vorgang abgebrochen." -ForegroundColor Yellow
            }
            else {
                # Eingabe parsen
                $selectedNumbers = @()
                
                if ($deleteInput -eq "A" -or $deleteInput -eq "a") {
                    $selectedNumbers = 1..$formattedProfiles.Count
                }
                else {
                    $parts = $deleteInput -split ","
                    
                    foreach ($part in $parts) {
                        $part = $part.Trim()
                        
                        if ($part -match "^(\d+)-(\d+)$") {
                            $start = [int]$matches[1]
                            $end = [int]$matches[2]
                            $selectedNumbers += $start..$end
                        }
                        elseif ($part -match "^\d+$") {
                            $selectedNumbers += [int]$part
                        }
                        else {
                            Write-Warning "Ungültige Eingabe: $part (wird ignoriert)"
                        }
                    }
                }
                
                $selectedNumbers = $selectedNumbers | Select-Object -Unique | Sort-Object
                $validNumbers = $selectedNumbers | Where-Object { $_ -ge 1 -and $_ -le $formattedProfiles.Count }
                
                if ($validNumbers.Count -eq 0) {
                    Write-Host "`nKeine gültigen Enrollment-Profile-Nummern ausgewählt. Vorgang abgebrochen." -ForegroundColor Red
                }
                else {
                    $profilesToDelete = $formattedProfiles | Where-Object { $validNumbers -contains $_.Nummer }
                    
                    Write-Host "`n=== Folgende Enrollment-Profile werden gelöscht ===" -ForegroundColor Red
                    $profilesToDelete | Format-Table Nummer, Name, Plattform, Typ, Created, 'Last Modified' -AutoSize
                    
                    Write-Host "`nACHTUNG: Diese Aktion kann nicht rückgängig gemacht werden!" -ForegroundColor Red
                    $confirmation = Read-Host "Möchten Sie diese Enrollment-Profile wirklich löschen? (Y/n)"
                    
                    if ($confirmation -eq "Y" -or $confirmation -eq "y") {
                        Write-Host "`nLösche Enrollment-Profile..." -ForegroundColor Yellow
                        
                        $successCount = 0
                        $failCount = 0
                        
                        foreach ($profile in $profilesToDelete) {
                            try {
                                switch ($profile.ProfileType) {
                                    "AutopilotProfile" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles('$($profile.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "iOSEnrollmentProfile" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/appleUserInitiatedEnrollmentProfiles('$($profile.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "androidDeviceOwnerEnrollmentProfile" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/androidDeviceOwnerEnrollmentProfiles('$($profile.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "androidForWorkEnrollmentProfile" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/androidForWorkEnrollmentProfiles('$($profile.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                }
                                Write-Host "  [OK] $($profile.Name)" -ForegroundColor Green
                                $successCount++
                            }
                            catch {
                                Write-Host "  [FEHLER] $($profile.Name): $_" -ForegroundColor Red
                                $failCount++
                            }
                        }
                        
                        Write-Host "`n=== Löschvorgang abgeschlossen ===" -ForegroundColor Green
                        Write-Host "Erfolgreich gelöscht: $successCount" -ForegroundColor Green
                        if ($failCount -gt 0) {
                            Write-Host "Fehler: $failCount" -ForegroundColor Red
                        }
                    }
                    else {
                        Write-Host "`nLöschvorgang abgebrochen." -ForegroundColor Yellow
                    }
                }
            }
        }
        else {
            Write-Host "Keine nicht zugewiesenen Enrollment-Profile gefunden." -ForegroundColor Green
        }
        
        # Zurück zum Menü
        Read-Host "`nDrücken Sie Enter, um zum Hauptmenü zurückzukehren"
    }
    
    # Funktion: Script Cleanup
    function Start-ScriptCleanup {
        Write-Host "`n=== Script Cleanup ===" -ForegroundColor Cyan
        Write-Host "Identifiziert und löscht nicht zugewiesene Skripte`n" -ForegroundColor Cyan
        
        # Sicherstellen dass Graph-Verbindung besteht
        Connect-ToGraph
        
        Write-Host "Rufe Skripte ab..." -ForegroundColor Yellow
        
        $unassignedScripts = @()
        
        # 1. PowerShell Scripts (Device Management Scripts)
        try {
            $psScriptsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts?`$top=100"
            $psScriptsResponse = Invoke-MgGraphRequest -Method GET -Uri $psScriptsUri
            $psScripts = $psScriptsResponse.value
            
            while ($psScriptsResponse.'@odata.nextLink') {
                $psScriptsResponse = Invoke-MgGraphRequest -Method GET -Uri $psScriptsResponse.'@odata.nextLink'
                $psScripts += $psScriptsResponse.value
            }
            
            Write-Host "  PowerShell-Skripte: $($psScripts.Count) gefunden" -ForegroundColor Gray
            
            foreach ($script in $psScripts) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts('$($script.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedScripts += [PSCustomObject]@{
                            Id = $script.id
                            Name = $script.displayName
                            Type = "PowerShell-Skript"
                            Created = $script.createdDateTime
                            LastModified = $script.lastModifiedDateTime
                            ScriptType = "PowerShellScript"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei PowerShell-Skript '$($script.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von PowerShell-Skripten: $($_.Exception.Message)"
        }
        
        # 2. Remediation Scripts (Device Health Scripts)
        try {
            $remediationScriptsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts?`$top=100"
            $remediationScriptsResponse = Invoke-MgGraphRequest -Method GET -Uri $remediationScriptsUri
            $remediationScripts = $remediationScriptsResponse.value
            
            while ($remediationScriptsResponse.'@odata.nextLink') {
                $remediationScriptsResponse = Invoke-MgGraphRequest -Method GET -Uri $remediationScriptsResponse.'@odata.nextLink'
                $remediationScripts += $remediationScriptsResponse.value
            }
            
            Write-Host "  Remediation-Skripte: $($remediationScripts.Count) gefunden" -ForegroundColor Gray
            
            foreach ($script in $remediationScripts) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts('$($script.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedScripts += [PSCustomObject]@{
                            Id = $script.id
                            Name = $script.displayName
                            Type = "Remediation-Skript"
                            Created = $script.createdDateTime
                            LastModified = $script.lastModifiedDateTime
                            ScriptType = "RemediationScript"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei Remediation-Skript '$($script.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von Remediation-Skripten: $($_.Exception.Message)"
        }
        
        # 3. macOS Scripts (Device Shell Scripts)
        try {
            $macOSScriptsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts?`$top=100"
            $macOSScriptsResponse = Invoke-MgGraphRequest -Method GET -Uri $macOSScriptsUri
            $macOSScripts = $macOSScriptsResponse.value
            
            while ($macOSScriptsResponse.'@odata.nextLink') {
                $macOSScriptsResponse = Invoke-MgGraphRequest -Method GET -Uri $macOSScriptsResponse.'@odata.nextLink'
                $macOSScripts += $macOSScriptsResponse.value
            }
            
            Write-Host "  macOS-Skripte: $($macOSScripts.Count) gefunden" -ForegroundColor Gray
            
            foreach ($script in $macOSScripts) {
                try {
                    $assignmentsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts('$($script.id)')/assignments"
                    $assignmentsResponse = Invoke-MgGraphRequest -Method GET -Uri $assignmentsUri
                    $assignments = $assignmentsResponse.value
                    
                    $includeAssignments = $assignments | Where-Object {
                        $_.target.'@odata.type' -ne '#microsoft.graph.exclusionGroupAssignmentTarget'
                    }
                    
                    if ($includeAssignments.Count -eq 0) {
                        $unassignedScripts += [PSCustomObject]@{
                            Id = $script.id
                            Name = $script.displayName
                            Type = "macOS-Skript"
                            Created = $script.createdDateTime
                            LastModified = $script.lastModifiedDateTime
                            ScriptType = "macOSScript"
                        }
                    }
                }
                catch {
                    Write-Warning "Fehler bei macOS-Skript '$($script.displayName)': $($_.Exception.Message)"
                }
            }
        }
        catch {
            Write-Warning "Fehler beim Abrufen von macOS-Skripten: $($_.Exception.Message)"
        }
        
        # Ergebnisse ausgeben
        Write-Host "`n=== Ergebnis ===" -ForegroundColor Green
        Write-Host "Nicht zugewiesene Skripte: $($unassignedScripts.Count)" -ForegroundColor Yellow
        
        if ($unassignedScripts.Count -gt 0) {
            Write-Host "`nNicht zugewiesene Skripte:" -ForegroundColor Yellow
            
            # Nummerierung hinzufügen
            $counter = 1
            $formattedScripts = $unassignedScripts | ForEach-Object {
                [PSCustomObject]@{
                    'Nummer' = $counter++
                    'Name' = $_.Name
                    'Typ' = $_.Type
                    'Created' = $_.Created
                    'Last Modified' = $_.LastModified
                    'Id' = $_.Id
                    'ScriptType' = $_.ScriptType
                }
            }
            
            $formattedScripts | Format-Table Nummer, Name, Typ, Created, 'Last Modified' -AutoSize
            
            # Abfrage: Skripte löschen?
            Write-Host "`n=== Löschoptionen ===" -ForegroundColor Yellow
            Write-Host "Geben Sie die zu löschenden Skripte an:" -ForegroundColor White
            Write-Host "  - Einzelne Nummer: 1" -ForegroundColor Gray
            Write-Host "  - Mehrere Nummern: 1,3,5" -ForegroundColor Gray
            Write-Host "  - Bereich: 1-5" -ForegroundColor Gray
            Write-Host "  - Alle: A" -ForegroundColor Gray
            Write-Host "  - Abbrechen: Enter (leer lassen)`n" -ForegroundColor Gray
            
            $deleteInput = Read-Host "Auswahl"
            
            if ([string]::IsNullOrWhiteSpace($deleteInput)) {
                Write-Host "`nKeine Skripte ausgewählt. Vorgang abgebrochen." -ForegroundColor Yellow
            }
            else {
                # Eingabe parsen
                $selectedNumbers = @()
                
                if ($deleteInput -eq "A" -or $deleteInput -eq "a") {
                    $selectedNumbers = 1..$formattedScripts.Count
                }
                else {
                    $parts = $deleteInput -split ","
                    
                    foreach ($part in $parts) {
                        $part = $part.Trim()
                        
                        if ($part -match "^(\d+)-(\d+)$") {
                            $start = [int]$matches[1]
                            $end = [int]$matches[2]
                            $selectedNumbers += $start..$end
                        }
                        elseif ($part -match "^\d+$") {
                            $selectedNumbers += [int]$part
                        }
                        else {
                            Write-Warning "Ungültige Eingabe: $part (wird ignoriert)"
                        }
                    }
                }
                
                $selectedNumbers = $selectedNumbers | Select-Object -Unique | Sort-Object
                $validNumbers = $selectedNumbers | Where-Object { $_ -ge 1 -and $_ -le $formattedScripts.Count }
                
                if ($validNumbers.Count -eq 0) {
                    Write-Host "`nKeine gültigen Skript-Nummern ausgewählt. Vorgang abgebrochen." -ForegroundColor Red
                }
                else {
                    $scriptsToDelete = $formattedScripts | Where-Object { $validNumbers -contains $_.Nummer }
                    
                    Write-Host "`n=== Folgende Skripte werden gelöscht ===" -ForegroundColor Red
                    $scriptsToDelete | Format-Table Nummer, Name, Typ, Created, 'Last Modified' -AutoSize
                    
                    Write-Host "`nACHTUNG: Diese Aktion kann nicht rückgängig gemacht werden!" -ForegroundColor Red
                    $confirmation = Read-Host "Möchten Sie diese Skripte wirklich löschen? (Y/n)"
                    
                    if ($confirmation -eq "Y" -or $confirmation -eq "y") {
                        Write-Host "`nLösche Skripte..." -ForegroundColor Yellow
                        
                        $successCount = 0
                        $failCount = 0
                        
                        foreach ($script in $scriptsToDelete) {
                            try {
                                switch ($script.ScriptType) {
                                    "PowerShellScript" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts('$($script.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "RemediationScript" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts('$($script.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                    "macOSScript" {
                                        $deleteUri = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts('$($script.Id)')"
                                        Invoke-MgGraphRequest -Method DELETE -Uri $deleteUri
                                    }
                                }
                                Write-Host "  [OK] $($script.Name)" -ForegroundColor Green
                                $successCount++
                            }
                            catch {
                                Write-Host "  [FEHLER] $($script.Name): $_" -ForegroundColor Red
                                $failCount++
                            }
                        }
                        
                        Write-Host "`n=== Löschvorgang abgeschlossen ===" -ForegroundColor Green
                        Write-Host "Erfolgreich gelöscht: $successCount" -ForegroundColor Green
                        if ($failCount -gt 0) {
                            Write-Host "Fehler: $failCount" -ForegroundColor Red
                        }
                    }
                    else {
                        Write-Host "`nLöschvorgang abgebrochen." -ForegroundColor Yellow
                    }
                }
            }
        }
        else {
            Write-Host "Keine nicht zugewiesenen Skripte gefunden." -ForegroundColor Green
        }
        
        # Zurück zum Menü
        Read-Host "`nDrücken Sie Enter, um zum Hauptmenü zurückzukehren"
    }
    
    # Funktion: Hauptmenü anzeigen
    function Show-MainMenu {
        Clear-Host
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host "  Intune-Frühjahrsputz" -ForegroundColor Cyan
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host ""
        Write-Host "  [1] Device Cleanup" -ForegroundColor White
        Write-Host "      Inaktive Geräte identifizieren und löschen" -ForegroundColor Gray
        Write-Host ""
        Write-Host "  [2] Unassigned Configuration Policy Cleanup" -ForegroundColor White
        Write-Host "      Nicht zugewiesene Richtlinien identifizieren und löschen" -ForegroundColor Gray
        Write-Host ""
        Write-Host "  [3] App Cleanup" -ForegroundColor White
        Write-Host "      Nicht zugewiesene Apps identifizieren und löschen" -ForegroundColor Gray
        Write-Host ""
        Write-Host "  [4] Compliance Policy Cleanup" -ForegroundColor White
        Write-Host "      Nicht zugewiesene Compliance-Richtlinien identifizieren und löschen" -ForegroundColor Gray
        Write-Host ""
        Write-Host "  [5] Windows Update Policy Cleanup" -ForegroundColor White
        Write-Host "      Nicht zugewiesene Windows Update-Richtlinien identifizieren und löschen" -ForegroundColor Gray
        Write-Host ""
        Write-Host "  [6] Enrollment Profile Cleanup" -ForegroundColor White
        Write-Host "      Nicht zugewiesene Enrollment-Profile identifizieren und löschen" -ForegroundColor Gray
        Write-Host ""
        Write-Host "  [7] Script Cleanup" -ForegroundColor White
        Write-Host "      Nicht zugewiesene Skripte identifizieren und löschen" -ForegroundColor Gray
        Write-Host ""
        Write-Host "  [0] Beenden" -ForegroundColor White
        Write-Host ""
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host ""
    }
    
    # Hauptprogramm
    function Start-MainProgram {
        # Prüfung ob erforderliche Module vorhanden sind
        if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.DeviceManagement)) {
            Write-Error "Microsoft.Graph.DeviceManagement Modul nicht installiert. Installation mit: Install-Module Microsoft.Graph.DeviceManagement"
            exit 1
        }
        
        # Graph Modul importieren
        Import-Module Microsoft.Graph.DeviceManagement
        
        # Hauptschleife
        $running = $true
        while ($running) {
            Show-MainMenu
            $choice = Read-Host "Wählen Sie eine Option"
            
            switch ($choice) {
                "1" {
                    Start-DeviceCleanup
                }
                "2" {
                    Start-UnassignedPolicyCleanup
                }
                "3" {
                    Start-AppCleanup
                }
                "4" {
                    Start-CompliancePolicyCleanup
                }
                "5" {
                    Start-WindowsUpdatePolicyCleanup
                }
                "6" {
                    Start-EnrollmentProfileCleanup
                }
                "7" {
                    Start-ScriptCleanup
                }
                "0" {
                    Write-Host "`nProgramm wird beendet..." -ForegroundColor Yellow
                    Disconnect-FromGraph
                    $running = $false
                }
                default {
                    Write-Host "`nUngültige Auswahl. Bitte wählen Sie eine gültige Option." -ForegroundColor Red
                    Start-Sleep -Seconds 2
                }
            }
        }
    }
    
    # Programm starten
    Start-MainProgram