gymedu
Goto Top

Löschen von clientseitig gespeicherten Userprofilen auf Windows10-Clients unter Windows Server 2019

Hallo zusammen,
wir haben die Betreuung eines gut laufenden Windows Server 2019 mit ca. 70 Windows 10 Clients und ca. 800 Usern übernommen (Schule). Die User haben lokal gespeicherte Profile und bei der hohen Fluktuation (jedes Jahr ca. 100 Zu- und Abgänge) sammeln sich auf den Clients erhebliche Mengen an lokalen Profilen an. Ich habe in einigen Fällen mit der Windows-Systemsteuerung händisch nicht mehr benötigte Profile auf den Clients gelöscht, aber es kam schnell der Wunsch ein PowerShell-Script zu schreiben, welches Profile, die z.B. mehrere Monate alt sind bei Bedarf auf den Clients per Batch zu löschen. Leider bin ich PowerShell-Anfänger, möchte mich aber gerne einarbeiten.
Der erste Zugang zum Löschen erschien mir über Remove-LocalUser sinnvoll, aber wenn ich mir die Ausgabe über Get-LocalUser ansehe, habe ich nur die üblichen Windows10 User, wie Administrator, Gast usw. nicht aber die Domänenuser, die sich irgendwann an diesem Client angemeldet haben. Diese kann ich zwar z.B. mittels

Get-ChildItem -path "C:\Users\" *.* | Where-Object -FilterScript {($_.LastWriteTime -lt '2021-10-01')}

ausfiltern, aber ich weiß nicht wie ich den vollständigen Löschvorgang per Script vornehmen soll.
Für eine Hilfe wäre ich dankbar, meine Suche hier im Forum hat mich leider noch nicht weitergebracht.

Gruß

Content-ID: 1728883210

Url: https://administrator.de/contentid/1728883210

Ausgedruckt am: 19.11.2024 um 11:11 Uhr

Meierjo
Meierjo 16.01.2022 um 17:52:56 Uhr
Goto Top
Hallo

Weisst du, dass es eine GPO gibt, um alte Profile zu löschen?
http://woshub.com/delete-old-user-profiles-gpo-powershell/

Gruss
gymedu
gymedu 16.01.2022 um 18:32:44 Uhr
Goto Top
Hallo Meierjo,

vielen Dank für den Tipp, das wäre ja noch einfacher als das kürzeste Script. Und ein paar gute Scripte sind auch noch auf der Seite vorhanden.

Beste Grüße
DerWoWusste
DerWoWusste 16.01.2022 aktualisiert um 21:55:41 Uhr
Goto Top
Ich wäre mit dieser GPO vorsichtig.

Wie dem Link zu entnehmen ist:
this policy may not work if some third-party software (most often it is an antivirus) accesses NTUSER.DAT file in user profiles and updates the date of last use.
Ich habe beobachtet, dass auf vielen Systemen das Letzter-Zugriff-Datum der ntuser.dat stets mit dem letzten Reboot übereinstimmte, egal, ob die Profile in Benutzung waren, oder nicht. Das hieße in Konsequenz: alte Profile würden nicht gelöscht.

Ich suche selbst noch nach einer besseren Alternative. Microsoft muss diesen Umstand fixen.
gymedu
gymedu 16.01.2022 um 23:13:47 Uhr
Goto Top
Danke für die wichtige Ergänzung.

Nach dem Studium des Linkinhalts werde ich zunächst mit den vorgeschlagenen und modifizierten Scripts an den lokalen Clients arbeiten und dann von meinen Erfahrungen berichten, wenn es dann noch jemand interessiert.

Zitat: Ich suche selbst noch nach einer besseren Alternative.

Auf jeden Fall würde mich eine bessere Alternative ebenfalls interessieren.
DerWoWusste
DerWoWusste 17.01.2022 aktualisiert um 07:01:47 Uhr
Goto Top
Die GPO macht das selbe, Skripte werden ebenfalls fehlschlagen, wenn der Zeitstempel nicht die Nutzung widerspiegelt. Auf diversen PCs bei uns tut er das nicht und es ist keine Software ausmachbar, die dafür verantwortlich ist, sprich: das macht Windows selbst.
gymedu
gymedu 17.01.2022 um 22:14:48 Uhr
Goto Top
Die Bedenken von DerWoWusste haben sich bestätigt.
Die o.a. Scripte verwenden für das Objekt Get-WMIObject -class Win32_UserProfile das Attribut LastUseTime und das wird tatsächlich stets auf den letzten Systemstart gesetzt, insofern ist das Attribut für das gestellte Problem unbrauchbar. Vielmehr bräuchte man m.E. das Attribut LastWriteTime, das man sieht wenn man sich z.B. mit Get-ChildItem die Einträge in C:\Users anschaut, was aber das erste Objekt nicht als Attribut hat. Man müsste beide Sichtweisen zusammenbringen. Ist in Arbeit, aber die Programmierung in PowerShell bringt am Anfang viele Überraschungen mit sich.
bild1
bild2
gymedu
gymedu 19.01.2022 um 12:23:23 Uhr
Goto Top
Der Weg über das Attribut LastWriteTime hat sich auch nicht als Lösung erwiesen, da hiermit auch nicht die letzte Anmeldung protokolliert wird. Neuer Versuch mit dem Attribut LastLogonDate ist in Arbeit.
gymedu
Lösung gymedu 20.04.2022 um 11:49:59 Uhr
Goto Top
Nach einigen Versuchen habe ich jetzt ein gut funktionierendes Script im Einsatz, welches das oben angegebene Problem zusammen mit diesem Link recht zuverlässig löst.
<#
.Synopsis
   Erzeugt eine Datei Deleters.csv aller lokal gespeicherten Userprofile mit folgenden Ausnahmen:
   Letztes Anmeldedatum jünger als 60 Tage 
   Lokale Ausnahmen wie Administratoren.
   Für die gespeicherten Daten sollte auf jedem Client ein Ordner 
   C:\Test erzeugt werden.
.Description
   Mit Get_EventLog System werden alle User mit einem Login vor höchstens 60 Tage ausgefiltert
   und im Array logDatenClean gespeichert. Zusätzlich sind noch in $logDatenClean die sinnvollerweise 
   nicht zu löschenden lokalen Profile wie Administratoren enthalten.
   Mit einem Durchlauf über alle gespeicherten Profile werden alle nicht in $logDatenClean
   enthaltenen User/Profile ausgefiltert und in die Datei Deleters.txt geschrieben.
   Späteres Ziel mit dem PS-Script Delete_CIMInstance_Datei:
   Es werden alle auf dem Client lokal gespeicherten Profile durchlaufen und gelöscht, 
   soweit das Profil in Deleters enthalten ist. 
#> 

# 1. Schritt: Alle Login-Ereignisse mit Time, User und Event und 60 Tage oder jünger
# aus $logs in $res sammeln

$logs = get-eventlog system -source Microsoft-Windows-Winlogon -After (Get-Date).AddDays(-60)
$res = @()
ForEach ($log in $logs) {
  if($log.instanceid -eq 7001) {
      $type = "Logon"  
    } 
    # Elseif ($log.instanceid -eq 7002){$type="Logoff"} # Logout wird nicht gesammelt  
  Else {Continue} 
  try {
    $res += New-Object PSObject -Property @{Time = $log.TimeWritten 
    "Event" = $type  
    User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}
  }
  catch {}
}

# 2. Schritt: $res ist immer noch ein Array von Objekten, aus diesem werden die Usernamen isoliert.
# Diese werden mittels "..." in einen String umgewandelt und nur der Teil hinter Domäne\ gespeichert  
# Da in der Regel ein User mehrfach auftritt werden mittels select -Unique alle Dopplungen entfernt

$logDatenClean = @() 

ForEach ($c in $res) {
   $aus = $c | Select-Object -Property User             # Attribut User isolieren
   $aus = ("$aus").Split("\")                 # z.B. $aus = {User = DESKTOP-NVAPPMG\asus_zen_book}  
   $userName = $aus[1].Substring(0, ($aus[1].Length)-1) # 2. Teil ohne } ist username    
   $logDatenClean += $userName
} 
$logDatenClean = $logDatenClean | select -Unique

# 3. Schritt: Alle User die im Verzeichnis C:\Users gefunden werden und die nicht in 
# $logDatenClean enthalten sind, in eine CSV-Datei schreiben. Ausnahme Administratoren.

Get-ChildItem -Path "C:\Users\" |    
  Where-Object {(!($_.Name -in $logDatenClean) -and !($_.Name -like "*Administrator*"))} |  
   Select-Object -Property Name | 
    Export-Csv -Path C:\Test\Deleters.csv -Delimiter ';' -NoTypeInformation  

Damit ist aus meiner Sicht eine mögliche Lösung gefunden, die auch in der Praxis gut funktioniert. Je nach Anzahl der zu löschenden Profile, kann das Löschprogramm mehrere Stunden benötigen. Der freigegebene Speicherplatz ist aber teilweise beträchtlich.
DerWoWusste
DerWoWusste 20.04.2022 um 11:53:41 Uhr
Goto Top
Was passiert denn, wenn das Eventlog voll ist und der letzte Logon nicht drin ist, aber jünger als 60 Tage?
gymedu
gymedu 20.04.2022 um 15:35:14 Uhr
Goto Top
Ich wusste bis eben gar nicht, dass das eintreten kann. Aber hier wird doch klar, dass vorher ein Hinweis kommt. Das Script macht natürlich momentan noch keinen Unterschied, d. h. bei den zu löschenden Profilen würden auch jüngere Profile auftauchen, die sozusagen nicht mehr in den EventLog passen. Aber selbst wenn diese gelöscht würden, würden die betroffenen User allenfalls beim nächsten Login an diesem Client etwas länger warten müssen, da ja der überwiegende Teil des Profils auf dem Server gespeichert ist und dann eben wieder neu auf den Client transportiert wird - wenigstens mit unserem System.
Ich habe ich bisher noch keine Probleme gehabt (bis jetzt ca. 50 Clients "gereinigt", die größte Anzahl zu löschender Profile war etwas mehr als 200, Laufzeit des Löschvorgangs 7 Stunden, Speicherplatzgewinn fast 130 Gb)
DerWoWusste
DerWoWusste 20.04.2022 um 15:58:17 Uhr
Goto Top
Aber hier wird doch klar, dass vorher ein Hinweis kommt.
Ja, bloß kommt der nicht sonderlich prominent, sondern nur im Log. Dein Skript ist gefährlich, sobald im profil Dinge drin sind, die nicht wiederherstellbar sind, würde ich das nicht zur Nutzung empfehlen.
gymedu
gymedu 20.04.2022 um 17:32:31 Uhr
Goto Top
1) Wenn ich das richtig verstanden habe, läuft das EventLog nur voll, wenn die Aufbewahrungsmethode für Ereignisprotokolle lautet: "Ereignisse nicht überschreiben (Protokolle manuell löschen)". Diese Einstellung liegt bei uns nicht vor, wohl deshalb ist das Ereignis für mich neu.
2) Man könnte sicher mit einem entsprechenden Powershell-Befehl abfragen, ob das EventLog voll ist und dann das Script abbrechen. Aber was machst du dann? Händisch die Profile abklappern, die man löschen könnte, oder erst dafür sorgen, das das EventLog ordnungsgemäß läuft? Wenn im laufenden Betrieb (Schule!) Clients nicht mehr richtig einsatzfähig sind und ich erkenne, dass das Problem der vollgelaufene Arbeitsplatz ist, dann habe ich mit meiner Methode mit wenigen Minuten Arbeitsaufwand am nächsten Morgen wieder voll funktionierende Clients.
3) Die beiden von mir eingestellten Scripte habe ich als Anregung und Diskussionsgrundlage verstanden, da ich selbst nichts Entsprechendes für das Löschen clientseitiger Profile gefunden habe und selbst noch ganz am Anfang meiner PS-Programmierung stehe. Du hast dankenswerterweise einen Punkt gefunden, an dem evtl. für andere noch Verbesserungsmöglichkeiten bestehen. Super wäre gewesen, gleichzeitig an einer möglichen Lösung mitzuarbeiten.
DerWoWusste
DerWoWusste 21.04.2022 um 09:29:11 Uhr
Goto Top
Wenn das Log voll ist, wird das Älteste gelöscht, das ist Standard, ja. Somit verlierst Du, wenn das Log nicht ausreichend groß genug eingestellt ist, ggf. Anmeldelogeinträge und Profile werden unbeabsichtigt gelöscht. Da man das Log recht groß setzen kann, wird das nur selten passieren, wenn überhaupt, da gebe ich Dir Recht.

Aber Achtung: dein Skript funktioniert nur bei lokalen Nutzern. Domänennutzer werden nicht unter 7001 lokal aufgezeichnet, sondern da solltest Du eher 811/812 nehmen.
Dein jetziges Skript löscht somit derzeit die Profile aller Domänenkonten.
gymedu
gymedu 21.04.2022 um 11:57:52 Uhr
Goto Top
@DerWoWusste
Das bringt die Sache schon eher voran.

Dein jetziges Skript löscht somit derzeit die Profile aller Domänenkonten.

Das kann ich nicht bestätigen, da ich das o. a. Script immer mit Kontrollausgaben laufen lasse, in denen wird ausgegeben welche Profile gelöscht werden und welche bestehen bleiben. Man müsste nur in Zeile 51 einfügen:
"Aktuelle User, nicht löschen!!!"  
$logDatenClean
"-------------------------------"  

und am Ende
# Kontrollausgabe
"Zu löschende Profile"  
Get-Content -Path C:\Test\Deleters.csv # Ergebnis darstellen

Diese Kontrollausgaben zeigen, welche Domänenuser gelöscht werden können und welche nicht. Die Ergebnisse entsprachen immer den Anforderungen.
Ich werde das aber mit 811/812 kontrollieren.
DerWoWusste
DerWoWusste 21.04.2022 um 12:20:34 Uhr
Goto Top
Verzeih bitte, ich habe das Log, welches Du nutzt, zunächst falsch abgelesen und mich an
Applications and Services Logs - Microsoft-Windows-Winlogon/Operational
orientiert und nicht an System.

Dann passt Dein Skript, so lange wie gegeben ist, dass es den vollen Zeitraum (60 Tage) aufzeichnet.
Pack also noch eine Zeile rein, die zunächst einmal feststellt, ob das älteste Ereignis des Systemlogs mindestens 60 Tage alt ist und sonst abbricht.
DerWoWusste
DerWoWusste 21.04.2022 aktualisiert um 12:39:50 Uhr
Goto Top
Bau also noch sowas ein:

if ((get-eventlog system -source Microsoft-Windows-Winlogon -Before (Get-Date).AddDays(-60)) -eq $null) {exit}
Exit wäre jetzt natürlich nur die Notbremse, besser wäre zusätzlich eine Benachrichtigungsmail, dass auf diesem System die Loggröße wohl erhöht werden muss. Oder Du automatisierst auch das Erhöhen gleich im Skript.
gymedu
gymedu 21.04.2022 um 16:31:29 Uhr
Goto Top
Danke für den konstruktiven Hinweis , werde ich berücksichtigen. Vlt. können ja noch andere profitieren.
gymedu
gymedu 21.05.2022 um 21:28:58 Uhr
Goto Top
Das Script zum Löschen der ermittelten Userprofile findet man übrigens hier