Alte Profile auf einem Gerät können den lokalen Speicher unnötig belasten. Dafür gibt es die "Shared Configuration" in Intune, die alten Profile automatisch bereinigt, basierend auf dem letzten Login-Datum oder dem freien Speicher. Leider funktioniert das nicht immer zuverlässig. Ich hatte dadurch schon Situationen in Umgebungen, in denen der Speicher komplett voll war und ein Login danach nicht mehr möglich war.
Dann hilft nur noch ein Neuaufsetzen des Gerätes.
Um diesem Problem vorzubeugen, habe ich für Intune ein Proactive Remediations Package erstellt, welches alte Profile überwacht und nach Bedarf entfernen kann.

Table of Contents

Paket nutzen und anpassen

Du kannst das Paket auf zwei verschiedene Weise einsetzen. Entweder nur um einen Überblick über die alten Profile zu bekommen oder auch um diese zu bereinigen.
Für nur den Überblick reicht das Detection Skript, für die Profil Bereinigung via Intune verteilst du zusätzlich auch das Remediation Skript.

In der ersten Zeile von Detection sowie Remediation wird definiert, welche Profile als "zu alt" gelten. Der Wert ist in Tagen anzugeben:

maximales Profil alter

Intune Paket erstellen (Monitoring und/oder Cleanup)

Um das Paket in Intune zu nutzen, erstelle ein neues unter:
Intune > Reports > Endpoint analytics > Proactive remediations > +Create script package

Im ersten Schritt vergibts du einen aussagekräftigen Namen wie beispielsweise "WIN-oldProfile-Monitor" oder "WIN-oldProfile-Cleanup".

Anschliessend lädst du für ein reines Monitoring nur das Detection-Skript hoch. Wenn du auch die Bereinigung durchführen möchtest, lade zusätzlich das Remediation-Skript hoch.
(Ich empfehle dir, zunächst nur das Monitoring zu aktivieren und erst wenn das Ergebnis wie gewünscht ist, die Bereinigung zu aktivieren.)

Einen Scope-Tag muss dur nur setzten, wenn das deine Umgebung vorsieht.

Beim Assigment weisst du die Konfiguration einer gewünschten Zielgruppe mit einem sinnvollen Intervall zu.

Monitoring mit und ohne Löschung

Um bei der Auswertung alles angezeigt zu bekommen, musst du als erstes alle Spalten einblenden:

Proactive Remediation Spalten

Es ist wichtig zu wissen, dass nur die letzte Ausführung gespeichert wird. Wenn du das Paket z.B. stündlich ausführst, hast du nur eine Stunde Zeit, die Aktion zu verfolgen.

Hast du nur die Detection ausgerollt, wirst du nur Resultate in der Spalte "Pre-remediation detection outputs" oder allenfalls "Pre-remediation detection error" finden. Eine Remediation wird und kann nicht angestossen werden.
Du siehst aber sehr schön, auf welchem Gerät zu alte Profile vorhanden sind:

Post-remediation detection output

Sind Detection und Remediation im Einsatz, wird dir im Falle einer Detection auch der Output nach der Anwendung der Remediation angezeigt:

Übersicht, Remediation
Geräte Übersicht
Pre-remediation detection output
Pre-remediation detection output
Post-remediation detection output
Post-remediation detection output

Erklärung der PowerShell Skripte

Innerhalb des Detection- und Remediation-Script nutze ich nicht die "LastUseTime" aus der Profilabfrage. Dies aus dem Grund, dass diese durch Antivirusprogramme oft verfälscht wird und das datum des letzten Scans für alle Benutzer anzeigt. Darum lese ich das Änderungsdatum der einzelne Profil-Ordnern aus.

Die Basis beider Skripte ist identisch und liest anhand der Variable "Profile_age" die Profilordner aus, die älter als x Tage sind, vergleicht diese mit den aktuellen Profilen und speichert sie in der Variable "Profiles_2remove".
Dies geschieht mit folgendem Befehl, wobei die Ordner "Default", "Windows", "Public" und "Admin" ausgeschlossen werden:

    # Get all User profile folders older than X days
    $LastAccessedFolder = Get-ChildItem "C:\Users" |  Where-Object {$_ -notlike "*Windows*" -and $_ -notlike "*default*" -and $_ -notlike "*Public*" -and $_ -notlike "*Admin*"} | Where LastWriteTime -lt (Get-Date).AddDays(-$Profile_age)

    # Filter the list of folders to only include those that are not associated with local user accounts
    $Profiles_notLocal = $LastAccessedFolder | Where-Object { $_.Name -notin $(Get-LocalUser).Name }

    # Retrieve a list of user profiles and filter to only include the old ones
    $Profiles_2remove = Get-CimInstance -Class Win32_UserProfile | Where-Object { $_.LocalPath -in $($Profiles_notLocal.FullName) }Code language: PowerShell (powershell)

Detection

Die Detection verarbeitet dann nur noch die Variabel "Profiles_2remove" und stösst falls löst die Remediation mit dem Exitcode "1" aus:

if($Profiles_2remove){
    Write-Warning "Old profiles ($Profile_age days+): $($Profiles_2remove.LocalPath)"
    Exit 1
}else{
    Write-Output -NoEnumerate $(Get-CimInstance -Class Win32_UserProfile | Select-Object LocalPath, LastUseTime)
    Exit 0
}Code language: PowerShell (powershell)

Remediation

Die Remediation prüft dasselbe wie die Detection und führ falls nötig mit dem Kommando "Remove-CimInstance" die Bereinigung der alten Profile aus.

if($Profiles_2remove){
    # Removing all old profiles
    $Profiles_2remove | Remove-CimInstance
}else{
    Write-Output "No old profiles found."
}Code language: PowerShell (powershell)

Weitere Hinweise und Beispiele für Proactive Remediations Pakete findest du hier:
Endpoint Analysis Proactive Remediation Community Repository