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