Dieses Skript dient dazu sämtliche logins aus den Eventlogs aller Domaincontroller zu lesen. Man kann die Domaincontroller, den Benutzer, die Zeit, sowie die Anzahl der Events restriktieren und das Ganze in eine CSV exportieren. Die Ansicht wird per default als Tabelle dargestellt, lässt sich jedoch mit dem Parameter „-ViewType“ leicht auf „Grid-View“ oder „None“ umstellen. „Non“ heißt hier ohne Tabellen Formatierung.
Das Skript zeigt alle Anmeldungen von einem Domaincontroller, also folgende Anmeldeformen:
"Interaktiv"
"Netzwerk"
"Batch"
"Dienst"
"Entsperrt"
"NetworkCleartext"
"NewCredentials"
"RemoteInteraktiv"
"CachedInteraktiv"
PowerShell
# Standardansicht für einen bestimmten BenutzerGet-UserLoginHistory-Username "Samaaccountname"# Interaktive GridView-AnzeigeGet-UserLoginHistory-ViewType Grid# Auf einzelnem DC mit höherem Limit für mehr ErgebnisseGet-UserLoginHistory-DomainControllers "domaincontroller"-MaxEvents 5000-ViewType Full# Schnellerer Export ohne AnzeigeGet-UserLoginHistory-Hours 48-ExportToCsv -ViewType None
Das Skript wird durch „Runspace Pools“ parallel verarbeitet. Zur Vereinfachung könnte man hier auch mit Powershell-Jobs arbeiten, falls es einem lieber ist.
PowerShell
functionGet-UserLoginHistory{<#.SYNOPSIS Ermittelt den Anmeldeverlauf von Benutzern aus den Sicherheitsprotokollen der Domain-Controller..DESCRIPTION Diese Funktion durchsucht die Sicherheitsprotokolle aller oder bestimmter Domain-Controller nach Anmeldeereignissen (4624, 4625) und zeigt Details zu diesen Anmeldungen in einer übersichtlichen Form an. Die Funktion wurde für bessere Performance optimiert..PARAMETERUsername Optional. Der Benutzername, nach dem gesucht werden soll. Kann im Format "Benutzername", "Domain\Benutzername" oder "[email protected]" angegeben werden..PARAMETERDomainControllers Optional. Eine Liste von Domain-Controllern, die durchsucht werden sollen. Wenn nicht angegeben, werden alle Domain-Controller der aktuellen Domäne durchsucht..PARAMETERHours Optional. Die Anzahl der Stunden in der Vergangenheit, die durchsucht werden sollen. Standard: 24 Stunden.PARAMETERMaxEvents Optional. Die maximale Anzahl von Ereignissen, die pro Domain-Controller abgerufen werden sollen. Standard: 1000 Ereignisse.PARAMETERExportToCsv Optional. Exportiert die Ergebnisse in eine CSV-Datei..PARAMETERLogFilePath Optional. Pfad zur CSV-Datei, wenn ExportToCsv aktiviert ist. Standard: "C:\Logs\UserLoginHistory.csv".PARAMETERViewType Optional. Art der Anzeige der Ergebnisse: - Table: Kompakte Tabellendarstellung (Standard) - Full: Ausführliche Tabellendarstellung - Grid: Anzeige im Out-GridView-Dialog (interaktiv) - None: Keine Anzeige, nur Rückgabe der Objekte.EXAMPLE Get-UserLoginHistory -Username "mustermann" Zeigt die Anmeldungen des Benutzers "mustermann" der letzten 24 Stunden an..EXAMPLE Get-UserLoginHistory -Username "mustermann" -Hours 48 -MaxEvents 5000 -ViewType Grid Zeigt die Anmeldungen des Benutzers "mustermann" der letzten 48 Stunden im GridView-Dialog an..EXAMPLE Get-UserLoginHistory -DomainControllers "DC01.domain.com" -ExportToCsv Zeigt Anmeldungen vom angegebenen Domain-Controller und exportiert sie in eine CSV-Datei..NOTES Erfordert entsprechende Berechtigungen zum Lesen der Sicherheitsprotokolle der Domain-Controller. Dateiname: Get-UserLoginHistory.ps1 Autor: Ismahil Ahmed Erstelldatum: 12.02.2025 Version: 1.6 #>[CmdletBinding()]param([Parameter(Mandatory=$false,Position=0)][string]$Username,[Parameter(Mandatory=$false)][string[]]$DomainControllers,[Parameter(Mandatory=$false)][int]$Hours=24,[Parameter(Mandatory=$false)][int]$MaxEvents=1000,[Parameter(Mandatory=$false)][switch]$ExportToCsv,[Parameter(Mandatory=$false)][string]$LogFilePath="C:\Logs\UserLoginHistory.csv",[Parameter(Mandatory=$false)][switch]$IncludeFailedLogins=$true,[Parameter(Mandatory=$false)][ValidateSet("Table","Full","Grid","None")][string]$ViewType="Table")begin{# Optimierung: Verwende RunspacePool für parallele Verarbeitung$runspacePool=[runspacefactory]::CreateRunspacePool(1,[Environment]::ProcessorCount)$runspacePool.Open()$runspaces=@()$scriptBlock={param([string]$DomainController,[datetime]$StartTime,[datetime]$EndTime,[int[]]$EventIDs,[int]$MaxEventsToGet,[string]$UserFilter,[string]$UserDomain,[string]$UserPrincipalName)# Ergebnisstruktur$results=@{DC=$DomainControllerSuccess=$falseEvents=@()Error=$nullEventCount=0ProcessedCount=0MatchedCount=0}try{# Teste Verbindungif(-not(Test-Connection-ComputerName $DomainController-Count 1-Quiet)){$results.Error="Domain-Controller nicht erreichbar"return$results}# Erstelle FilterHashtable$filter=@{LogName='Security'ID=$EventIDsStartTime=$StartTimeEndTime=$EndTime}# Versuche Zugriff zu testentry{$null=Get-WinEvent-ComputerName $DomainController-LogName Security -MaxEvents 1-ErrorAction Stop}catch{$results.Error="Zugriff auf Sicherheitsprotokolle nicht möglich: $($_.Exception.Message)"return$results}# Hauptabfrage - OPTIMIERT durch limitierte Feldabfrage$events=Get-WinEvent-ComputerName $DomainController-FilterHashtable $filter-MaxEvents $MaxEventsToGet-ErrorAction Stop$results.EventCount=$events.Count$results.Success=$true# Verarbeite Ereignisse - OPTIMIERT durch selektive Verarbeitungforeach($event in$events){$results.ProcessedCount++try{$eventXml=[xml]$event.ToXml()$eventData=$eventXml.Event.EventData.Data# Extrahiere Benutzernamen$targetUsername=($eventData|Where-Object{$_.Name-eq'TargetUserName'}).'#text'$targetDomain=($eventData|Where-Object{$_.Name-eq'TargetDomainName'}).'#text'# Benutzernamen vergleichen, wenn ein Benutzerfilter angegeben ist$matchUser=$trueif($UserFilter){$matchUser=$false# Vereinfachte Matching-Strategien für bessere Performanceif($targetUsername-eq$UserFilter){$matchUser=$true}elseif($UserDomain-and($targetDomain-like"*$UserDomain*")-and($targetUsername-eq$UserFilter)){$matchUser=$true}elseif($UserPrincipalName-and("$targetUsername@$targetDomain"-like"*$UserPrincipalName*")){$matchUser=$true}}# Nur Benutzeranmeldungen (keine Systemkonten)if($matchUser-and$targetUsername-notmatch'^\$'-and$targetUsername-ne'ANONYMOUS LOGON'-and$targetUsername-ne'SYSTEM'-and$targetUsername-ne'LOCAL SERVICE'-and$targetUsername-ne'NETWORK SERVICE'){$results.MatchedCount++# Extrahiere nur die minimal notwendigen Informationen$ipAddress=($eventData|Where-Object{$_.Name-eq'IpAddress'}).'#text'if([string]::IsNullOrEmpty($ipAddress)-or$ipAddress-eq'-'){$ipAddress="Lokal"}$workstation=($eventData|Where-Object{$_.Name-eq'WorkstationName'}).'#text'if([string]::IsNullOrEmpty($workstation)){$workstation="Unbekannt"}$logonType=($eventData|Where-Object{$_.Name-eq'LogonType'}).'#text'$logonTypeDesc=switch($logonType){2{"Interaktiv"}3{"Netzwerk"}4{"Batch"}5{"Dienst"}7{"Entsperrt"}8{"NetworkCleartext"}9{"NewCredentials"}10{"RemoteInteraktiv"}11{"CachedInteraktiv"}default{"Typ $logonType"}}$results.Events+=[PSCustomObject]@{Zeitstempel=$event.TimeCreatedDC=$DomainControllerBenutzername="$targetDomain\$targetUsername"IPAdresse=$ipAddressComputername=$workstationEreignisID=$event.IdStatus=if($event.Id-eq4624){"Erfolgreich"}else{"Fehlgeschlagen"}AnmeldungsTyp=$logonTypeDesc}}}catch{# Ignoriere einzelne Fehler bei der Ereignisverarbeitungcontinue}}}catch{if($_.Exception.Message-like"*Es wurden keine Ereignisse gefunden*"){$results.Success=$true$results.EventCount=0}else{$results.Error=$_.Exception.Message}}return$results}# Erstellen des Log-Verzeichnisses, falls es nicht existiert und Export aktiviert istif($ExportToCsv){$logDir=Split-Path$LogFilePath-Parentif(-not(Test-Path$logDir)){New-Item-ItemType Directory -Path $logDir-Force |Out-Null}}# Funktion zum Abrufen von Domain-Controller-NamenfunctionGet-DCList{try{# Verwende Get-ADDomainController falls RSAT-Tools installiert sindif(Get-CommandGet-ADDomainController-ErrorAction SilentlyContinue){return(Get-ADDomainController-Filter *|Select-Object-ExpandProperty HostName)}else{# Fallback-Methode$domain=[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()return$domain.DomainControllers|ForEach-Object{$_.Name}}}catch{Write-Warning"Fehler beim Abrufen der Domain-Controller: $_"return$null}}# Zeitraum berechnen$endTime=Get-Date$startTime=$endTime.AddHours(-$Hours)Write-Host"Suche nach Anmeldeereignissen zwischen $($startTime.ToString('yyyy-MM-dd HH:mm:ss')) und $($endTime.ToString('yyyy-MM-dd HH:mm:ss'))"-ForegroundColor Cyan# Ereignis-IDs festlegenif($IncludeFailedLogins){$eventIDs=@(4624,4625)# Erfolgreiche und fehlgeschlagene Anmeldungen}else{$eventIDs=@(4624)# Nur erfolgreiche Anmeldungen}# Wenn keine Domain-Controller angegeben wurden, versuche, sie abzurufenif(-not$DomainControllers){$DomainControllers=Get-DCListif(-not$DomainControllers){Write-Error"Keine Domain-Controller gefunden. Bitte gib explizit Domain-Controller an."return}}Write-Host"Domain-Controller: $($DomainControllers-join', ')"-ForegroundColor Cyan# Benutzernamenformat verarbeiten$userSamAccountName=$null$userDomain=$null$userPrincipalName=$nullif($Username){Write-Host"Suche nach Benutzername: $Username"-ForegroundColor Cyan# Prüfe verschiedene Formateif($Username-match'^(.+)\\(.+)$'){# Format: DOMAIN\username$userDomain=$Matches[1]$userSamAccountName=$Matches[2]Write-Host" Format erkannt: DOMAIN\username (Domäne: $userDomain, Benutzername: $userSamAccountName)"-ForegroundColor Yellow}elseif($Username-match'^(.+)@(.+)$'){# Format: [email protected]$userSamAccountName=$Matches[1]$userDomain=$Matches[2]$userPrincipalName=$UsernameWrite-Host" Format erkannt: [email protected] (Benutzername: $userSamAccountName, Domäne: $userDomain)"-ForegroundColor Yellow}else{# Format: username$userSamAccountName=$UsernameWrite-Host" Format erkannt: Nur Benutzername (Benutzername: $userSamAccountName)"-ForegroundColor Yellow# Versuche, den vollständigen Benutzernamen aus Active Directory zu bekommenif(Get-CommandGet-ADUser-ErrorAction SilentlyContinue){try{$adUser=Get-ADUser-Identity $userSamAccountName-Properties UserPrincipalName, DistinguishedNameif($adUser){$userPrincipalName=$adUser.UserPrincipalName$userDomain=($adUser.DistinguishedName-split',DC=')[1]Write-Host" AD-Benutzerinformationen gefunden: $($adUser.DistinguishedName)"-ForegroundColor Green}}catch{Write-Warning" Konnte keine AD-Benutzerinformationen für '$userSamAccountName' finden: $_"}}}}}process{# Starte die parallele Verarbeitung der Domain-ControllerWrite-Host"`nStarte parallele Abfrage von $($DomainControllers.Count) Domain-Controllern..."-ForegroundColor Yellowforeach($dcin$DomainControllers){$runspaceParams=@{DomainController=$dcStartTime=$startTimeEndTime=$endTimeEventIDs=$eventIDsMaxEventsToGet=$MaxEventsUserFilter=$userSamAccountNameUserDomain=$userDomainUserPrincipalName=$userPrincipalName}$runspace=[powershell]::Create().AddScript($scriptBlock).AddParameters($runspaceParams)$runspace.RunspacePool=$runspacePool$runspaces+=[PSCustomObject]@{Runspace=$runspaceHandle=$runspace.BeginInvoke()DC=$dcCompleted=$false}}# Erstelle eine ArrayList für die Ergebnisse$allResults=New-Object System.Collections.ArrayList# Überwache den Fortschritt der Runspaces$completed=0$total=$runspaces.Countwhile($runspaces|Where-Object{-not$_.Completed}){foreach($itemin$runspaces|Where-Object{-not$_.Completed}){if($item.Handle.IsCompleted){$results=$item.Runspace.EndInvoke($item.Handle)$item.Completed=$true$completed++# Status anzeigenif($results.Success){Write-Host"Domain-Controller $($item.DC): $($results.MatchedCount) von $($results.EventCount) Ereignissen gefunden"-ForegroundColor Green}else{Write-Host"Domain-Controller $($item.DC): Fehler - $($results.Error)"-ForegroundColor Red}# Ergebnisse zur Gesamtliste hinzufügenif($results.Events.Count-gt0){[void]$allResults.AddRange($results.Events)}# Runspace bereinigen$item.Runspace.Dispose()}}# Fortschrittsanzeige aktualisierenWrite-Progress-Activity "Abfrage von Domain-Controllern"-Status "$completed von $total abgeschlossen"-PercentComplete (($completed/$total)*100)# Kurze Pause für CPU-EntlastungStart-Sleep-Milliseconds 100}Write-Progress-Activity "Abfrage von Domain-Controllern"-Completed# Sortiere die Ergebnisse nach Zeitstempel (neueste zuerst)$sortedResults=$allResults|Sort-Object-Property Zeitstempel -Descending# Gib Statistiken aus$resultCount=$sortedResults.CountWrite-Host"`nGefundene Anmeldungen: $resultCount"-ForegroundColor Cyan# Exportiere zu CSV, falls gewünschtif($ExportToCsv-and$resultCount-gt0){$sortedResults|Export-Csv-Path $LogFilePath-NoTypeInformation -Encoding UTF8Write-Host"Ergebnisse wurden in $LogFilePath gespeichert."-ForegroundColor Green}# Zeige die Ergebnisse je nach ViewType anif($resultCount-gt0){switch($ViewType){"Table"{# Kompakte Tabellendarstellung mit ausgewählten Spalten$displayCount=[Math]::Min(20,$resultCount)Write-Host"Zeige die neuesten $displayCount Anmeldungen in Tabellenform:"-ForegroundColor Cyan# Format-Table mit benutzerdefinierter Formatierung für bessere Lesbarkeit$sortedResults|Select-Object-First $displayCount|Format-Table-Property @{Label="Zeitpunkt";Expression={$_.Zeitstempel.ToString("yyyy-MM-dd HH:mm:ss")};Width=19},@{Label="Benutzer";Expression={($_.Benutzername-split'\\')[-1]};Width=15},@{Label="Status";Expression={if($_.Status-eq"Erfolgreich"){"✓"}else{"✗"}};Width=6},@{Label="Anmeldungstyp";Expression={$_.AnmeldungsTyp};Width=15},@{Label="IP-Adresse";Expression={$_.IPAdresse};Width=15},@{Label="Computer";Expression={$_.Computername};Width=15},@{Label="Domain-Controller";Expression={($_.DC-split'\.')[0]};Width=15}if($resultCount-gt20){Write-Host"... und $($resultCount-20) weitere Anmeldungen."-ForegroundColor YellowWrite-Host"Vollständige Anzeige mit 'Get-UserLoginHistory -ViewType Grid' oder 'Get-UserLoginHistory -ViewType Full'"-ForegroundColor Yellow}# Bei Table-Ansicht keine Objekte zurückgebenreturn}"Full"{# Ausführliche Tabellendarstellung mit allen SpaltenWrite-Host"Zeige die Anmeldungen in ausführlicher Tabellenform:"-ForegroundColor Cyan$sortedResults|Format-Table-AutoSize# Bei Full-Ansicht keine Objekte zurückgebenreturn}"Grid"{# GridView-Darstellung (interaktiv)Write-Host"Öffne interaktives GridView-Fenster mit den Ergebnissen..."-ForegroundColor Cyan$sortedResults|Out-GridView-Title "Benutzeranmeldungen ($resultCount Einträge)"-PassThru# Bei Grid-Ansicht keine Objekte zurückgebenreturn}"None"{# Keine Anzeige, nur Rückgabe der ObjekteWrite-Host"Keine Anzeige ausgewählt. Die Ergebnisse sind als Objekte verfügbar."-ForegroundColor Yellow}}}else{Write-Host"Keine Anmeldungen gefunden, die den Filterkriterien entsprechen."-ForegroundColor Yellow}# Gib die Ergebnisse nur zurück, wenn ViewType "None" ist oder keine Ergebnisse gefunden wurden# Bei anderen ViewTypes wurde die Rückgabe bereits mit "return" beendetreturn$sortedResults}end{# Runspace-Pool schließen$runspacePool.Close()$runspacePool.Dispose()}}
Mit dem Modul ActiveDirectory in PowerShell steht Administratoren ein mächtiges Werkzeug zur Verfügung, um Active-Directory-Objekte wie Benutzer, Gruppen, Computer, Organisationseinheiten (OUs) und Gruppenrichtlinien schnell, skriptgesteuert und zuverlässig zu verwalten.
Dieser Beitrag gibt Ihnen einen erweiterten Überblick über die wichtigsten Cmdlets, den produktiven Einsatz in der Domäne und bewährte Best Practices.
✅ Voraussetzungen
Windows Server mit Active Directory-Rolle oder ein Windows-Client mit den RSAT-Tools.
PowerShell-Version ≥ 5.1 (oder Windows PowerShell 7+ mit Import-Module).
Import des Moduls (meist automatisch verfügbar auf AD-Servern):
PowerShell
Import-Module ActiveDirectory
🧩 Modulüberblick und Hilfe anzeigen
PowerShell
Get-Module-ListAvailable ActiveDirectoryGet-Command-Module ActiveDirectoryGet-HelpGet-ADUser-Detailed # Beispiel um die Funktionsweise vom Command "Get-ADUser" zu verstehen
👨💼 Top 10 Cmdlets für Domänen-Administratoren
1. Benutzer suchen
PowerShell
Get-ADUser-Filter *-SearchBase "OU=Benutzer,DC=testdomain,DC=local"Get-ADUser-Filter *-SearchBase "OU=Benutzer,DC=testdomain,DC=local"| ft
Die Ausgabe der Benutzer sieht wie nachfolgend aus und kann zur Übersicht noch formatiert und angepasst werden.
Alternativen – Active Directory Service Interface oder auch adsi
Zwei Beispiele um einen User mittels adsi zu finden.
PowerShell
$searcher=New-Object DirectoryServices.DirectorySearcher$searcher.SearchRoot=[ADSI]"LDAP://RootDSE"# Die root Domain$searcher.Filter="(&(objectCategory=User)(samAccountName=USERNAME))"# Userobjecte werden gesucht mit dem bestimmten anmeldenamen$result=$searcher.FindOne()# FindAll()
Das Ganze kann man auch als simple Funktion aufbauen und so direkt nach dem User suchen.
Die Filter lassen sich dabei individuell auf das gewünschte ad Objekt anpassen. Bei diesem Cheatsheet könnt ihr euch einen Überblick verschaffen. Oder ihr schaut im AD nach z.B. mit dem ADSI-Editor oder in der Konsole/Snap-In „Active Directory-Benutzer und -Computer“ (dsa.msc). Dazu einfach die erweiterte Ansicht anschalten und in die Eigenschaften der jeweiligen Objekte auf „Attribut-Editor“ gehen.
🧠 Best Practices für den produktiven Einsatz
Um das ActiveDirectory-Modul effizient und sicher zu nutzen, empfehlen sich folgende Vorgehensweisen:
Sichere Passwortverwaltung: Verwende Secure Strings für Passwörter – auch in Skripten.
Effiziente Filterung: Setze den -Filter {} Parameter ein, anstatt Where-Object zu nutzen, um die Performance zu optimieren.
Reporting: Generiere regelmäßige Berichte und exportiere Daten mit Export-Csv:
Testen mit -WhatIf: Führe neue Skripte zunächst mit -WhatIf oder -Confirm:$false aus, um unbeabsichtigte Änderungen zu vermeiden.
Überwachung inaktiver Konten: Analysiere LastLogonDate Werte, um inaktive Benutzerkonten zu identifizieren und zu verwalten.
📚 Weitere Ressourcen
Vertiefende Informationen und aktuelle Entwicklungen findest du in diesen Quellen:
Das ActiveDirectory-Modul in PowerShell ist ein unverzichtbares Tool für die Verwaltung komplexer Domänenumgebungen. Mit den vorgestellten Cmdlets, erweiterten Anwendungsfällen und Best Practices kannst du administrative Aufgaben automatisieren, detaillierte Reports erstellen und schnell auf Veränderungen reagieren. Richtig eingesetzt verbessert es die Effizienz und Sicherheit deiner IT-Infrastruktur erheblich.
Dies ist der Auftakt zu einer fortlaufenden Serie, in der wir noch tiefer in fortgeschrittene Techniken, praxisnahe Skripte und optimierte Abläufe in Windows-Domänenumgebungen eintauchen. Hast du Fragen oder möchtest du deine eigenen Erfahrungen teilen? Dann diskutiere gern in den Kommentaren mit!
Das Grundskript ist recht simple aufgebaut. Es wird Remote auf dem Client unter HKEY_USERS nachgesehen. Diese Hive enthält Benutzerprofile (SIDs) und damit auch deren gemappte Laufwerke.
PowerShell
$reg.GetSubKeyNames()|?{$_ -match'\d{4,5}$'}
Der Bereich filtert nur SIDs (Security Identifier), die auf eine Ziffernfolge enden, z. B. S-1-5-21-...-1001. Das sind in der Regel die aktiven Benutzerprofile (statt z. B. .DEFAULT oder Classes).
-'param ($computer = $env:COMPUTERNAME)'-- Standardmäßig wird der lokale Computer verwendet, kann aber überschrieben werden-'[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('Users', $computer)'-- Öffnet die Registry-Hive HKEY_USERS des angegebenen Computers (lokal oder remote)-'?'-- ist ein Alias für Where-Object. Es filtert also die Liste.-'%'-- ist ein Alias für foreach-'$_'-- ist das aktuelle Element in der Pipeline (also ein Schlüsselname wie S-1-5-21-...-1000).-"-match '\d{4,5}$'"-- prüft per Regulärem Ausdruck (Regex), ob das Ende des Strings aus genau 4 oder 5 Ziffern besteht-'$reg.GetSubKeyNames()'= Listet alle SIDs (Benutzer und Systemkonten), die in der Registry vertreten sind-'$reg.OpenSubKey("$sid\Network")'-- Greift auf Registry-Schlüssel zu,in denen Windows Netzlaufwerk-Zuordnungen speichert-'.GetSubKeyNames()'-- Holt die Laufwerksbuchstaben, z. B. Z, P, etc.-'([SecurityIdentifier]$sid).Translate([NTAccount])'-- Wandelt die SID in einen Benutzernamen um (z. B. DOMAIN\Max.Mustermann)-".GetValue('RemotePath')"-- Liest den Pfad des gemappten Netzlaufwerks aus, z. B. \\server\freigabe-'New-Object psobject -Property @{ ... }'-- Baut ein Objekt mit den Informationen: Computer, Benutzer, Laufwerksbuchstabe, Pfadregex:-'\d'-- eine Ziffer (0–9)-'{4,5}'-- genau 4 oder 5 Ziffern-'$'-- Am ende des Strings
Da das Skript noch sehr rudimentär ist, sollte man dieses noch erweitern und Fehlerhandling, sowie Performance verbessern. Die Perfomance kann man alleine dadurch verbessern, dass wir vorher einen Ping an den Host senden, bevor wir versuchen Remote auf die Registry zuzugreifen.
🔍 Die Komplette Funktion
PowerShell
functionGet-MappedDrives{param([string]$computer=$env:COMPUTERNAME)# Prüfe ob der Host erreichbar ist, bevor weitermachentry{if(-not(Test-Connection-ComputerName $computer-Count 1-Quiet -ErrorAction Stop)){Write-Warning"Computer '$computer' ist nicht erreichbar."return}}catch{Write-Warning"Verbindungsfehler zu '$computer': $_"return}# Sammle zuerst alle Daten in einer Hashtable, gruppiert nach Benutzern$userDrives=@{}try{$reg=[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('Users',$computer)$reg.GetSubKeyNames()|?{$_ -match'\d{4,5}$'}|%{$sid=$_$networkKey=$reg.OpenSubKey("$sid\Network")if($networkKey){try{# Wandle SID in Benutzername um - nur einmal pro Benutzer$userName=([System.Security.Principal.SecurityIdentifier]($sid)).Translate([System.Security.Principal.NTAccount]).Value# Erstelle einen Eintrag für diesen Benutzer, falls noch nicht vorhandenif(-not$userDrives.ContainsKey($userName)){$userDrives[$userName]=@()}$networkKey.GetSubKeyNames()|%{$driveLetter=$_$driveKey=$networkKey.OpenSubKey($driveLetter)if($driveKey){$driveInfo=New-Object psobject -Property @{Computer=$computerUser=$userNameDriveLetter=$driveLetterMap=$driveKey.GetValue('RemotePath')}# Füge die Laufwerksinformation zum Array dieses Benutzers hinzu$userDrives[$userName]+=$driveInfo# Ressourcen freigeben$driveKey.Close()}}}catch{Write-Verbose"Fehler bei Verarbeitung von SID '$sid': $_"}# Ressourcen freigeben$networkKey.Close()}}# Ausgabe der nach Benutzern gruppierten Daten$userDrives.GetEnumerator()|%{$userName=$_.KeyWrite-Output"Netzlaufwerke für Benutzer: $userName"Write-Output"----------------------------------------"$_.Value|Format-Table-AutoSizeWrite-Output""}}catch{Write-Error"Fehler beim Zugriff auf den Computer '$computer': $_"}finally{if($reg){$reg.Close()}}}
Da man im Best Practice die Verwendung von Aliasen vermeidet. Hier noch mal das Skript mit volle Cmdlet-Schreibweise. Und natürlich das Kommentarbasiertes Help-System um die Bedienung für neue Benutzer zu erleichtern.
📃Das Fertige Skript
PowerShell
functionGet-MappedDrives{<#.SYNOPSIS Retrieves mapped network drives for all users on a remote computer..DESCRIPTION Connects to the remote registry of a specified computer to enumerate network drives mapped under all user SIDs found in the HKU hive..PARAMETERComputerName The target computer name. Defaults to the local computer..EXAMPLE Get-MappedDrives -ComputerName SRV-01.NOTES Requires the Remote Registry service to be running on the target machine. Dateiname: Get-MappedDrives.ps1 Autor: Ismahil Ahmed Erstelldatum: 19.02.2022 Version: 1.0#>[CmdletBinding()]param([Parameter(Position=0)][string]$ComputerName=$env:COMPUTERNAME)try{if(-not(Test-Connection-ComputerName $ComputerName-Count 1-Quiet -ErrorAction Stop)){Write-Warning"Computer '$ComputerName' is not reachable."return}}catch{Write-Warning"Connection error to '$ComputerName': $_"return}$userDrives=@{}try{$reg=[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('Users',$ComputerName)$reg.GetSubKeyNames()|Where-Object{$_ -match'\d{4,5}$'}|ForEach-Object{$sid=$_$networkKey=$reg.OpenSubKey("$sid\Network")if($networkKey){try{$userName=([System.Security.Principal.SecurityIdentifier]$sid).Translate([System.Security.Principal.NTAccount]).Valueif(-not$userDrives.ContainsKey($userName)){$userDrives[$userName]=@()}$networkKey.GetSubKeyNames()|ForEach-Object{$driveLetter=$_$driveKey=$networkKey.OpenSubKey($driveLetter)if($driveKey){$driveInfo=[PSCustomObject]@{Computer=$ComputerNameUser=$userNameDriveLetter=$driveLetterMap=$driveKey.GetValue('RemotePath')}$userDrives[$userName]+=$driveInfo$driveKey.Close()}}}catch{Write-Verbose"Error processing SID '$sid': $_"}$networkKey.Close()}}$userDrives.GetEnumerator()|ForEach-Object{$userName=$_.KeyWrite-Output"Mapped drives for user: $userName"Write-Output"----------------------------------------"$_.Value|Format-Table-AutoSizeWrite-Output""}}catch{Write-Error"Error accessing computer '$ComputerName': $_"}finally{if($reg){$reg.Close()}}}
„Install-TrendMicro.ps1“ – Das Skript um den Apex One Security Agent von Trend Micro zu deployen!
In diesem Beitrag stelle ich dir ein Powershell-Skript vor, das für die automatische Installation und Überwachung eines Trend Micro OfficeScan-Updates in einer Windows-Domänenumgebung entwickelt wurde. Das Skript führt verschiedene Aufgaben aus – von der Logrotation über das Auslesen von Versionsinformationen bis hin zur Startsteuerung relevanter Dienste. Es richtet sich an fortgeschrittene Administratoren, die Prozesse automatisieren und gleichzeitig eine lückenlose Dokumentation sicherstellen möchten.
🔍 Überblick über das Skript
Es handelt sich im Grunde nur um zwei Funktionen, eine dient zum loggen und die andere Funktion „Install-TM“ beinhaltet den Kernteil. Im Kernteil werden zusätzlich die Trend Micro Dienste lokal auf dem Client geprüft und ggf. gestartet. Den Teil mit den Diensten könnte man noch in eine eigene Funktion überführen.
✅ Write-Log ✅ Install-TM #TM Dienste werden zusätzlich geprüft
PowerShell
param(# Das Logverzeichnis – standardmäßig C:\Temp\log[string]$LogFilePath="C:\Temp\log",# $Server ist verpflichtend (z.B. "Exampleserver.domain.de")[Parameter(Mandatory=$true)][string]$Server,# Der Share wird aus dem Servernamen gebildet#[string]$ServerShare = '\\$Server\ofcscan',[string]$ServerShare=('\\{0}\ofcscan'-f$Server),[string]$FileName='OFCSCAN.INI')# Erstelle den ServerFilePath$ServerFilePath=Join-Path$ServerShare"ofcscan.ini"# Erstelle das Logverzeichnis, falls es nicht existiertif(-not(Test-Path$LogFilePath)){New-Item-Path $LogFilePath-ItemType Directory -Force |Out-Null}# Definiere den Pfad der Logdatei und das Archiv (als ZIP) im gleichen Verzeichnis$LogFile=Join-Path$LogFilePath"log.txt"
Das Skript beginnt mit der Definition von Parametern, die folgende Informationen beinhalten:
LogFilePath: Das Verzeichnis, in dem Logdateien gespeichert werden (Standard: C:\Temp\log).
Server: Der Name des Servers (z. B. Exampleserver.domain.de), von dem die Konfigurationsdatei geladen wird – dieser Parameter ist verpflichtend.
ServerShare: Der Pfad zum freigegebenen Ordner auf dem Server, in dem sich die zentrale Konfigurationsdatei (OFCSCAN.INI) befindet.
FileName: Der Name der Konfigurationsdatei, standardmäßig OFCSCAN.INI.
Anschließend wird ein Pfad zur Server-Konfigurationsdatei erstellt und das Logverzeichnis erzeugt, falls es noch nicht existiert. Alle wichtigen Aktionen werden in einer Logdatei dokumentiert – inklusive einer automatischen Logrotation, wenn die Logdatei 5 MB überschreitet.
🚀 Teil 1: Logdatei und Logrotation
PowerShell
# Das Archiv erhält einen Zeitstempel im Dateinamen; standardmäßig wird hier eine ZIP erstellt$DefaultArchiveZipFilePattern="log_archive_{0:yyyyMMdd_HHmmss}.zip"$ArchiveZipFile=Join-Path$LogFilePath([string]::Format($DefaultArchiveZipFilePattern,(Get-Date)))functionWrite-Log{param([string]$Message,[string]$LogFile)$timestamp=Get-Date-Format "yyyy-MM-dd HH:mm:ss""$timestamp - $Message"|Out-File-FilePath $LogFile-Append}
Die Funktion Write-Log fügt jedem Logeintrag einen Zeitstempel hinzu. Ist die Logdatei zu groß, wird sie temporär umbenannt, komprimiert und archiviert – so bleibt der Logordner übersichtlich und alte Daten werden gesichert.
🚀 Teil 2: Lokale und Server-Konfiguration auslesen
PowerShell
functionInstall-TM{param([string]$LogFile,[string]$LogFilePath,[string]$ArchiveZipFile,[string]$ServerFilePath,[string]$FileName)# Log-Rotation prüfen und durchführenif(Test-Path$LogFile){$logSize=(Get-Item$LogFile).Lengthif($logSize-gt5MB){$tempArchive="$LogFile.tmp"Rename-Item-Path $LogFile-NewName $tempArchive-Forcetry{Compress-Archive-Path $tempArchive-DestinationPath $ArchiveZipFile-ForceRemove-Item$tempArchive-ForceWrite-Log-Message "Archivierte Logdatei komprimiert zu: $ArchiveZipFile"-LogFile $LogFile}catch{Write-Log-Message "Fehler beim Komprimieren der archivierten Logdatei: $_"-LogFile $LogFile}}}Write-Log-Message "TM Skript läuft"-LogFile $LogFile# Lokale Pfade definieren (z.B. OfficeScan Client und Security Agent)$localPaths=@(Join-Path$env:"ProgramFiles(x86)""Trend Micro\OfficeScan Client",Join-Path$env:"ProgramFiles(x86)""Trend Micro\Security Agent")# Lokale INI-Datei einlesen$lokalFile=$nullforeach($pathin$localPaths){$iniPath=Join-Path-Path $path-ChildPath $FileNameif(Test-Path$iniPath){try{$lokalFile=Get-Content-Path $iniPath-ErrorAction Stopif($lokalFile){break}}catch{Write-Log-Message "Fehler beim Lesen der lokalen INI-Datei in $iniPath: $_"-LogFile $LogFile}}}if(-not$lokalFile){Write-Log-Message "Lokale INI-Datei nicht gefunden."-LogFile $LogFilereturn}# Lokale Version auslesentry{$localVersionString=($lokalFile|Select-String-Pattern "PccNT_Version").Line.Split("=")[1].Trim()$localVersionParts=$localVersionString.Split(".")$localVersion=@{Version=[int]$localVersionParts[0]SubVersion=[int]$localVersionParts[1]BuildNum=[int](($lokalFile|Select-String-Pattern "PccNT_BuildNum").Line.Split("=")[1].Trim())}}catch{Write-Log-Message "Fehler beim Auslesen der lokalen Version: $_"-LogFile $LogFilereturn}# Server INI-Datei einlesenif(-not(Test-Path$ServerFilePath)){Write-Log-Message "Server INI-Datei nicht gefunden: $ServerFilePath"-LogFile $LogFilereturn}try{$serverFile=Get-Content-Path $ServerFilePath-ErrorAction Stop}catch{Write-Log-Message "Fehler beim Lesen der Server INI-Datei: $_"-LogFile $LogFilereturn}# Server Version auslesentry{$serverVersionString=($serverFile|Select-String-Pattern "PccNT_Version").Line.Split("=")[1].Trim()$serverVersionParts=$serverVersionString.Split(".")$serverVersion=@{Version=[int]$serverVersionParts[0]SubVersion=[int]$serverVersionParts[1]BuildNum=[int](($serverFile|Select-String-Pattern "PccNT_BuildNum").Line.Split("=")[1].Trim())}}catch{Write-Log-Message "Fehler beim Auslesen der Server Version: $_"-LogFile $LogFilereturn}# Installationsentscheidung treffen$clientPath=Join-Path$env:"ProgramFiles(x86)""Trend Micro\OfficeScan Client"$startInstall=$falseif(-not(Test-Path$clientPath)){Write-Log-Message "OfficeScan Client nicht gefunden. Installation wird gestartet."-LogFile $LogFile$startInstall=$true}else{if($localVersion.Version-lt$serverVersion.Version){$startInstall=$true}elseif($localVersion.Version-eq$serverVersion.Version-and$localVersion.SubVersion-lt$serverVersion.SubVersion){$startInstall=$true}elseif($localVersion.Version-eq$serverVersion.Version-and$localVersion.SubVersion-eq$serverVersion.SubVersion-and$localVersion.BuildNum-lt$serverVersion.BuildNum){$startInstall=$true}}
Im nächsten Schritt sucht das Skript in zwei vordefinierten Verzeichnissen nach der lokalen OFCSCAN.INI Datei. Wird die Datei gefunden, wird der darin enthaltene Versionsstring (sowohl PccNT_Version als auch PccNT_BuildNum) ausgelesen und in eine Hashtable überführt. Anschließend erfolgt das Auslesen der entsprechenden Werte aus der zentralen Konfigurationsdatei, die über einen UNC-Pfad erreichbar ist.
Durch den Vergleich der Versionsnummern wird ermittelt, ob eine Aktualisierung erforderlich ist:
Ist die lokale Hauptversion kleiner als die Server-Version, wird ein Update initiiert.
Ist die Hauptversion gleich, aber die Nebenversion kleiner, erfolgt ebenfalls ein Update.
Bei gleichen Haupt- und Nebenversionen wird zusätzlich die Buildnummer verglichen.
🚀 Teil 3: Update-Entscheidung und Dienststeuerung
PowerShell
if($startInstall){Write-Log-Message "Starte Installation von Trend Micro. Lokale Version: $($localVersion.Version).$($localVersion.SubVersion).$($localVersion.BuildNum) - Server Version: $($serverVersion.Version).$($serverVersion.SubVersion).$($serverVersion.BuildNum)"-LogFile $LogFiletry{Start-Process-FilePath "\\$Server\ofcscan\Autopcc.exe"}catch{Write-Log-Message "Fehler beim Starten der Installation: $_"-LogFile $LogFile}}else{Write-Log-Message "Keine Installation notwendig. Lokale Version ist aktuell."-LogFile $LogFile}# Dienste überprüfen und starten$services=@('ntrtscan','tmccsf','tmlisten','TmPfw','TmWSCSvc')foreach($servicein$services){try{$svc=Get-Service-Name $service-ErrorAction Stopif($svc.Status-ne'Running'){Write-Log-Message "Starte Dienst: $($svc.DisplayName)"-LogFile $LogFileStart-Service-Name $service}else{Write-Log-Message "Dienst läuft bereits: $($svc.DisplayName)"-LogFile $LogFile}}catch{Write-Log-Message "Fehler beim Verarbeiten des Dienstes $service: $_"-LogFile $LogFile}}}
Wenn festgestellt wird, dass die lokale Version veraltet ist, startet das Skript das Update, indem es die Anwendung Autopcc.exe vom zentralen Server ausführt. Zusätzlich prüft das Skript eine Liste von wichtigen Trend Micro-Diensten (wie ntrtscan, tmccsf und weitere) – sollten diese nicht laufen, werden sie automatisch gestartet.
📌 Das Ergebnis
PowerShell
param(# Das Logverzeichnis – standardmäßig C:\Temp\log[string]$LogFilePath="C:\Temp\log",# $Server ist verpflichtend (z.B. "Exampleserver.domain.de")[Parameter(Mandatory=$true)][string]$Server,# Der Share wird aus dem Servernamen gebildet[string]$ServerShare=('\\{0}\ofcscan'-f$Server),# Name der INI-Datei[string]$FileName='OFCSCAN.INI')# Erstelle den ServerFilePath$ServerFilePath=Join-Path-Path $ServerShare-ChildPath "ofcscan.ini"# Erstelle das Logverzeichnis, falls es nicht existiertif(-not(Test-Path-Path $LogFilePath)){New-Item-Path $LogFilePath-ItemType Directory -Force |Out-Null}# Definiere den Pfad der Logdatei und das Archiv (als ZIP) im gleichen Verzeichnis$LogFile=Join-Path-Path $LogFilePath-ChildPath "log.txt"# Das Archiv erhält einen Zeitstempel im Dateinamen; standardmäßig wird hier eine ZIP erstellt$DefaultArchiveZipFilePattern="log_archive_{0:yyyyMMdd_HHmmss}.zip"$ArchiveZipFile=Join-Path-Path $LogFilePath-ChildPath ([string]::Format($DefaultArchiveZipFilePattern,(Get-Date)))functionWrite-Log{param([string]$Message,[string]$LogFile)$timestamp=Get-Date-Format "yyyy-MM-dd HH:mm:ss""$timestamp - $Message"|Out-File-FilePath $LogFile-Append}functionInstall-TM{param([string]$LogFile,[string]$LogFilePath,[string]$ArchiveZipFile,[string]$ServerFilePath,[string]$FileName)# Log-Rotation prüfen und durchführenif(Test-Path-Path $LogFile){$logSize=(Get-Item-Path $LogFile).Lengthif($logSize-gt5MB){$tempArchive="$LogFile.tmp"Rename-Item-Path $LogFile-NewName $tempArchive-Forcetry{Compress-Archive-Path $tempArchive-DestinationPath $ArchiveZipFile-ForceRemove-Item-Path $tempArchive-ForceWrite-Log-Message "Archivierte Logdatei komprimiert zu: $ArchiveZipFile"-LogFile $LogFile}catch{Write-Log-Message "Fehler beim Komprimieren der archivierten Logdatei: $($_.Exception.Message)"-LogFile $LogFile}}}Write-Log-Message "TM Skript läuft"-LogFile $LogFile# Lokale Pfade definieren (z.B. OfficeScan Client und Security Agent)$localPaths=@((Join-Path-Path ${env:ProgramFiles(x86)}-ChildPath "Trend Micro\OfficeScan Client"),(Join-Path-Path ${env:ProgramFiles(x86)}-ChildPath "Trend Micro\Security Agent"))# Lokale INI-Datei einlesen$lokalFile=$nullforeach($pathin$localPaths){$iniPath=Join-Path-Path $path-ChildPath $FileNameif(Test-Path-Path $iniPath){try{$lokalFile=Get-Content-Path $iniPath-ErrorAction Stopif($lokalFile){break}}catch{Write-Log-Message "Fehler beim Lesen der lokalen INI-Datei in $iniPath : $($_.Exception.Message)"-LogFile $LogFile}}}if(-not$lokalFile){Write-Log-Message "Lokale INI-Datei nicht gefunden."-LogFile $LogFilereturn}# Lokale Version auslesentry{$localVersionString=($lokalFile|Select-String-Pattern "PccNT_Version").Line.Split("=")[1].Trim()$localVersionParts=$localVersionString.Split(".")$localVersion=@{Version=[int]$localVersionParts[0]SubVersion=[int]$localVersionParts[1]BuildNum=[int](($lokalFile|Select-String-Pattern "PccNT_BuildNum").Line.Split("=")[1].Trim())}}catch{Write-Log-Message "Fehler beim Auslesen der lokalen Version: $($_.Exception.Message)"-LogFile $LogFilereturn}# Server INI-Datei einlesenif(-not(Test-Path-Path $ServerFilePath)){Write-Log-Message "Server INI-Datei nicht gefunden: $ServerFilePath"-LogFile $LogFilereturn}try{$serverFile=Get-Content-Path $ServerFilePath-ErrorAction Stop}catch{Write-Log-Message "Fehler beim Lesen der Server INI-Datei: $($_.Exception.Message)"-LogFile $LogFilereturn}# Server Version auslesentry{$serverVersionString=($serverFile|Select-String-Pattern "PccNT_Version").Line.Split("=")[1].Trim()$serverVersionParts=$serverVersionString.Split(".")$serverVersion=@{Version=[int]$serverVersionParts[0]SubVersion=[int]$serverVersionParts[1]BuildNum=[int](($serverFile|Select-String-Pattern "PccNT_BuildNum").Line.Split("=")[1].Trim())}}catch{Write-Log-Message "Fehler beim Auslesen der Server Version: $($_.Exception.Message)"-LogFile $LogFilereturn}# Installationsentscheidung treffen$clientPath=Join-Path-Path ${env:ProgramFiles(x86)}-ChildPath "Trend Micro\OfficeScan Client"$startInstall=$falseif(-not(Test-Path-Path $clientPath)){Write-Log-Message "OfficeScan Client nicht gefunden. Installation wird gestartet."-LogFile $LogFile$startInstall=$true}else{if($localVersion.Version-lt$serverVersion.Version){$startInstall=$true}elseif($localVersion.Version-eq$serverVersion.Version-and$localVersion.SubVersion-lt$serverVersion.SubVersion){$startInstall=$true}elseif($localVersion.Version-eq$serverVersion.Version-and$localVersion.SubVersion-eq$serverVersion.SubVersion-and$localVersion.BuildNum-lt$serverVersion.BuildNum){$startInstall=$true}}if($startInstall){Write-Log-Message "Starte Installation von Trend Micro. Lokale Version: $($localVersion.Version).$($localVersion.SubVersion).$($localVersion.BuildNum) - Server Version: $($serverVersion.Version).$($serverVersion.SubVersion).$($serverVersion.BuildNum)"-LogFile $LogFiletry{Start-Process-FilePath "\\$Server\ofcscan\Autopcc.exe"}catch{Write-Log-Message "Fehler beim Starten der Installation: $($_.Exception.Message)"-LogFile $LogFile}}else{Write-Log-Message "Keine Installation notwendig. Lokale Version ist aktuell."-LogFile $LogFile}# Dienste überprüfen und starten$services=@('ntrtscan','tmccsf','tmlisten','TmPfw','TmWSCSvc')foreach($servicein$services){try{$svc=Get-Service-Name $service-ErrorAction Stopif($svc.Status-ne'Running'){Write-Log-Message "Starte Dienst: $($svc.DisplayName)"-LogFile $LogFileStart-Service-Name $service}else{Write-Log-Message "Dienst läuft bereits: $($svc.DisplayName)"-LogFile $LogFile}}catch{Write-Log-Message "Fehler beim Verarbeiten des Dienstes $service : $($_.Exception.Message)"-LogFile $LogFile}}}Install-TM-LogFile $LogFile-LogFilePath $LogFilePath-ArchiveZipFile $ArchiveZipFile-ServerFilePath $ServerFilePath-FileName $FileName
Dieses Skript bietet eine umfassende Lösung zur Automatisierung von OfficeScanAgent-Updates in einer Windows-Domänenumgebung. Durch die Kombination von Logrotation, Versionsvergleich und automatisierter Dienststeuerung können Administratoren sicherstellen, dass ihre Systeme stets auf dem neuesten Stand und optimal geschützt sind.
Wenn man das Ganze noch etwas aufhübscht, die comment-based Hilfe hinzufügt und das logging erweitert, erhält man ein gutes Skript und leicht verständliches Skript.
PowerShell
<#.SYNOPSIS Installiert oder aktualisiert den Trend Micro OfficeScan Client..DESCRIPTION Dieses Skript vergleicht die lokal installierte Version des Trend Micro OfficeScan Clients mit der Version auf dem angegebenen Server. Wenn die lokale Version älter ist oder der Client nicht installiert ist, wird automatisch eine Installation oder ein Update durchgeführt. Zusätzlich werden relevante Trend Micro Dienste überprüft und bei Bedarf gestartet..PARAMETERLogFilePath Pfad zum Logverzeichnis. Wenn nicht angegeben, wird standardmäßig "C:\Temp\log" verwendet..PARAMETERServer Name des Servers, auf dem die Trend Micro OfficeScan-Installationsdateien liegen. Dieser Parameter ist obligatorisch..PARAMETERServerShare Netzwerkpfad zum OfficeScan-Share auf dem Server. Wird automatisch aus dem Servernamen generiert. Standardwert: \\[Server]\ofcscan.PARAMETERFileName Name der INI-Datei, die für den Versionsvergleich verwendet wird. Standardwert: OFCSCAN.INI.EXAMPLE .\Install-TrendMicro.ps1 -Server "Exampleserver.domain.de" Führt das Skript mit dem angegebenen Server und Standardwerten für die anderen Parameter aus..EXAMPLE .\Install-TrendMicro.ps1 -Server "Exampleserver.domain.de" -LogFilePath "D:\Logs\TrendMicro" Führt das Skript mit angepasstem Logverzeichnis aus..NOTES Dateiname: Install-TrendMicro.ps1 Autor: Ismahil Ahmed Erstelldatum: 04.04.2023 Version: 1.1 Änderungsprotokoll: 1.0 - Ursprüngliche Version 1.1 - Skriptverbesserungen und Comment-Help hinzugefügt#>[CmdletBinding()]param(# Das Logverzeichnis – standardmäßig C:\Temp\log[string]$LogFilePath="C:\Temp\log",# $Server ist verpflichtend (z.B. "Exampleserver.domain.de")[Parameter(Mandatory=$true)][string]$Server,# Der Share wird aus dem Servernamen gebildet[string]$ServerShare=('\\{0}\ofcscan'-f$Server),# Name der INI-Datei[string]$FileName='OFCSCAN.INI')# Erstelle den ServerFilePath$ServerFilePath=Join-Path-Path $ServerShare-ChildPath $FileName# Erstelle das Logverzeichnis, falls es nicht existiertif(-not(Test-Path-Path $LogFilePath)){New-Item-Path $LogFilePath-ItemType Directory -Force |Out-Null}# Definiere den Pfad der Logdatei und das Archiv (als ZIP) im gleichen Verzeichnis$LogFile=Join-Path-Path $LogFilePath-ChildPath "log.txt"# Das Archiv erhält einen Zeitstempel im Dateinamen; standardmäßig wird hier eine ZIP erstellt$DefaultArchiveZipFilePattern="log_archive_{0:yyyyMMdd_HHmmss}.zip"$ArchiveZipFile=Join-Path-Path $LogFilePath-ChildPath ([string]::Format($DefaultArchiveZipFilePattern,(Get-Date)))functionWrite-Log{<#.SYNOPSIS Schreibt Nachrichten mit Zeitstempel in eine Logdatei. .PARAMETERMessage Die zu protokollierende Nachricht. .PARAMETERLogFile Pfad zur Logdatei. #>[CmdletBinding()]param([Parameter(Mandatory=$true)][string]$Message,[Parameter(Mandatory=$true)][string]$LogFile)$timestamp=Get-Date-Format "yyyy-MM-dd HH:mm:ss""$timestamp - $Message"|Out-File-FilePath $LogFile-Append}functionInstall-TM{<#.SYNOPSIS Hauptfunktion zur Installation und Aktualisierung von Trend Micro OfficeScan. .DESCRIPTION Prüft die lokale Version gegen die Server-Version und installiert oder aktualisiert den Client bei Bedarf. Startet außerdem relevante Dienste. .PARAMETERLogFile Pfad zur Logdatei. .PARAMETERLogFilePath Pfad zum Logverzeichnis. .PARAMETERArchiveZipFile Pfad zur archivierten Logdatei (ZIP). .PARAMETERServerFilePath Vollständiger Pfad zur INI-Datei auf dem Server. .PARAMETERFileName Name der INI-Datei. #>[CmdletBinding()]param([Parameter(Mandatory=$true)][string]$LogFile,[Parameter(Mandatory=$true)][string]$LogFilePath,[Parameter(Mandatory=$true)][string]$ArchiveZipFile,[Parameter(Mandatory=$true)][string]$ServerFilePath,[Parameter(Mandatory=$true)][string]$FileName)# Log-Rotation prüfen und durchführenif(Test-Path-Path $LogFile){$logSize=(Get-Item-Path $LogFile).Lengthif($logSize-gt5MB){$tempArchive="$LogFile.tmp"Rename-Item-Path $LogFile-NewName $tempArchive-Forcetry{Compress-Archive-Path $tempArchive-DestinationPath $ArchiveZipFile-ForceRemove-Item-Path $tempArchive-ForceWrite-Log-Message "Archivierte Logdatei komprimiert zu: $ArchiveZipFile"-LogFile $LogFile}catch{Write-Log-Message "Fehler beim Komprimieren der archivierten Logdatei: $($_.Exception.Message)"-LogFile $LogFile}}}Write-Log-Message "TM Skript läuft"-LogFile $LogFile# Hilfsfunktion zur InstallationfunctionStart-Installation{Write-Log-Message "Starte Installation/Update von Trend Micro."-LogFile $LogFiletry{$installPath="\\$Server\ofcscan\Autopcc.exe"Write-Log-Message "Starte Installer von: $installPath"-LogFile $LogFile$process=Start-Process-FilePath $installPath-PassThru -Wait -ErrorAction Stop if($process.ExitCode-eq0){Write-Log-Message "Installation/Update erfolgreich abgeschlossen."-LogFile $LogFile}else{Write-Log-Message "Installation/Update mit Exit-Code $($process.ExitCode) beendet."-LogFile $LogFile}}catch{Write-Log-Message "Fehler beim Starten der Installation: $($_.Exception.Message)"-LogFile $LogFile}}# Hilfsfunktion zum Starten der DienstefunctionStart-TrendMicroServices{$services=@('ntrtscan','tmccsf','tmlisten','TmPfw','TmWSCSvc')Write-Log-Message "Überprüfe Trend Micro Dienste..."-LogFile $LogFileforeach($servicein$services){try{$svc=Get-Service-Name $service-ErrorAction Stopif($svc.Status-ne'Running'){Write-Log-Message "Starte Dienst: $($svc.DisplayName)"-LogFile $LogFileStart-Service-Name $service-ErrorAction Stop # Warte kurz und prüfe, ob der Dienst gestartet wurdeStart-Sleep-Seconds 2$svc=Get-Service-Name $serviceif($svc.Status-eq'Running'){Write-Log-Message "Dienst $($svc.DisplayName) erfolgreich gestartet."-LogFile $LogFile}else{Write-Log-Message "Dienst $($svc.DisplayName) konnte nicht gestartet werden."-LogFile $LogFile}}else{Write-Log-Message "Dienst läuft bereits: $($svc.DisplayName)"-LogFile $LogFile}}catch{Write-Log-Message "Fehler beim Verarbeiten des Dienstes $service : $($_.Exception.Message)"-LogFile $LogFile}}Write-Log-Message "Dienste-Überprüfung abgeschlossen."-LogFile $LogFile}# Lokale Pfade definieren (z.B. OfficeScan Client und Security Agent)$path1=Join-Path-Path ${env:ProgramFiles(x86)}-ChildPath "Trend Micro\OfficeScan Client"$path2=Join-Path-Path ${env:ProgramFiles(x86)}-ChildPath "Trend Micro\Security Agent"$localPaths=@($path1,$path2)# Lokale INI-Datei einlesen$lokalFile=$nullforeach($pathin$localPaths){$iniPath=Join-Path-Path $path-ChildPath $FileNameif(Test-Path-Path $iniPath){try{$lokalFile=Get-Content-Path $iniPath-ErrorAction Stopif($lokalFile){Write-Log-Message "Lokale INI-Datei gefunden in: $iniPath"-LogFile $LogFilebreak}}catch{Write-Log-Message "Fehler beim Lesen der lokalen INI-Datei in $iniPath : $($_.Exception.Message)"-LogFile $LogFile}}}if(-not$lokalFile){Write-Log-Message "Lokale INI-Datei nicht gefunden. Installation wird gestartet."-LogFile $LogFileStart-Installationreturn}# Lokale Version auslesentry{$localVersionString=($lokalFile|Select-String-Pattern "PccNT_Version").Line.Split("=")[1].Trim()$localVersionParts=$localVersionString.Split(".")$localVersion=@{Version=[int]$localVersionParts[0]SubVersion=[int]$localVersionParts[1]BuildNum=[int](($lokalFile|Select-String-Pattern "PccNT_BuildNum").Line.Split("=")[1].Trim())}Write-Log-Message "Lokale Version: $($localVersion.Version).$($localVersion.SubVersion) Build $($localVersion.BuildNum)"-LogFile $LogFile}catch{Write-Log-Message "Fehler beim Auslesen der lokalen Version: $($_.Exception.Message)"-LogFile $LogFilereturn}# Server INI-Datei einlesenif(-not(Test-Path-Path $ServerFilePath)){Write-Log-Message "Server INI-Datei nicht gefunden: $ServerFilePath"-LogFile $LogFilereturn}try{$serverFile=Get-Content-Path $ServerFilePath-ErrorAction Stop}catch{Write-Log-Message "Fehler beim Lesen der Server INI-Datei: $($_.Exception.Message)"-LogFile $LogFilereturn}# Server Version auslesentry{$serverVersionString=($serverFile|Select-String-Pattern "PccNT_Version").Line.Split("=")[1].Trim()$serverVersionParts=$serverVersionString.Split(".")$serverVersion=@{Version=[int]$serverVersionParts[0]SubVersion=[int]$serverVersionParts[1]BuildNum=[int](($serverFile|Select-String-Pattern "PccNT_BuildNum").Line.Split("=")[1].Trim())}Write-Log-Message "Server Version: $($serverVersion.Version).$($serverVersion.SubVersion) Build $($serverVersion.BuildNum)"-LogFile $LogFile}catch{Write-Log-Message "Fehler beim Auslesen der Server Version: $($_.Exception.Message)"-LogFile $LogFilereturn}# Installationsentscheidung treffen$clientPath=Join-Path-Path ${env:ProgramFiles(x86)}-ChildPath "Trend Micro\OfficeScan Client"$startInstall=$falseif(-not(Test-Path-Path $clientPath)){Write-Log-Message "OfficeScan Client nicht gefunden. Installation wird gestartet."-LogFile $LogFile$startInstall=$true}else{if($localVersion.Version-lt$serverVersion.Version){$startInstall=$trueWrite-Log-Message "Hauptversion ist veraltet. Update erforderlich."-LogFile $LogFile}elseif($localVersion.Version-eq$serverVersion.Version-and$localVersion.SubVersion-lt$serverVersion.SubVersion){$startInstall=$trueWrite-Log-Message "Unterversion ist veraltet. Update erforderlich."-LogFile $LogFile}elseif($localVersion.Version-eq$serverVersion.Version-and$localVersion.SubVersion-eq$serverVersion.SubVersion-and$localVersion.BuildNum-lt$serverVersion.BuildNum){$startInstall=$trueWrite-Log-Message "Build-Nummer ist veraltet. Update erforderlich."-LogFile $LogFile}}# Installation oder Update startenif($startInstall){Start-Installation}else{Write-Log-Message "Keine Installation notwendig. Lokale Version ist aktuell."-LogFile $LogFile}# Dienste überprüfen und startenStart-TrendMicroServices}# Hauptskript ausführentry{Write-Log-Message "=== Skript gestartet ==="-LogFile $LogFileInstall-TM-LogFile $LogFile-LogFilePath $LogFilePath-ArchiveZipFile $ArchiveZipFile-ServerFilePath $ServerFilePath-FileName $FileNameWrite-Log-Message "=== Skript erfolgreich beendet ==="-LogFile $LogFile}catch{Write-Log-Message "=== Skript mit Fehler beendet: $($_.Exception.Message) ==="-LogFile $LogFile}
Hast du Fragen oder Anregungen zu diesem Ansatz? Teile deine Erfahrungen und Tipps gern in den Kommentaren mit!
In diesem Beitrag möchte ich euch zeigen, wie man mit einem simplen Skript, ein wenig Ordnung in die Domäne bringt. Das Skript beinhaltet mehrere Funktionen zum löschen und bereinigen von Verzeichnissen, Logs, Updates etc.
Zweck: Mit dieser Funktion können installierte Softwarepakete abgefragt werden. Das ist hilfreich, wenn man z. B. den Status oder die Version einer bestimmten Software prüfen möchte.
Funktionsweise:
Parameter:
$Name: Der Name der gesuchten Software.
$Version: Optional, um gezielt nach einer bestimmten Version zu suchen.
Ablauf:
Vor der Suche wird eine Log-Meldung ausgegeben.
Mittels Get-Package werden Pakete abgefragt, und – falls ein Versionsfilter gesetzt wurde – zusätzlich gefiltert.
Abschließend wird die Anzahl gefundener Pakete protokolliert und das Ergebnis zurückgegeben.
Nutzen:
Ermöglicht ein schnelles und automatisiertes Inventar der installierten Software.
u3. Funktion zum Deinstallieren eines Softwarepakets
PowerShell
functionuninstall-softwarepackage{param([Microsoft.PackageManagement.Packaging.SoftwareIdentity]$Package)try{ Log-Message "Versuche Deinstallation des Pakets: $($Package.Name)"if($Package.ProviderName-eq"Programs"){$xml=[xml]$Package.SwidTagText$uninstallString=$xml.SoftwareIdentity.Meta.UninstallStringif($uninstallString-match'msiexec.* /i.*'){$uninst=(($uninstallString-split'')[1]-replace'/I','/X')+' /qb'Start-Processmsiexec.exe-ArgumentList $uninst-Wait -NoNewWindow -ErrorAction Stop}elseif(Test-Path-LiteralPath $uninstallString){Start-Process-FilePath $uninstallString-Wait -NoNewWindow -ErrorAction Stop}elseif($uninstallString-match'msiexec.* /x.*'){$uninst=($uninstallString-split'')[1]+' /qb'Start-Processmsiexec.exe-ArgumentList $uninst-Wait -NoNewWindow -ErrorAction Stop} Log-Message "Deinstallation erfolgreich: $($Package.Name)"}else{$Package|Uninstall-Package-ErrorAction Stop Log-Message "Deinstallation erfolgreich über Provider: $($Package.Name)"}}catch{ Log-Message "Fehler bei der Deinstallation des Pakets: $_""ERROR"}}
Zweck: Diese Funktion dient der Deinstallation eines Softwarepakets. Sie ist so aufgebaut, dass sie unterschiedliche Deinstallationsszenarien abdeckt.
Funktionsweise:
Parameter:
$Package: Ein Objekt, das Informationen über das Softwarepaket enthält.
Ablauf:
Zunächst wird über die Log-Funktion vermerkt, welches Paket deinstalliert werden soll.
Wird festgestellt, dass der Provider „Programs“ ist, wird der XML-Inhalt des Pakets ausgelesen, um den richtigen Deinstallationsbefehl zu ermitteln.
Unterschiedliche Ansätze (z. B. Ersetzen von /I zu /X bei MSI-Installationen oder direkter Aufruf eines externen Deinstallationsprogramms) werden durchlaufen.
Ist der Provider ein anderer, wird der generische Befehl Uninstall-Package genutzt.
Bei Erfolg oder im Fehlerfall wird eine entsprechende Log-Meldung ausgegeben.
Nutzen:
Sorgt für eine saubere Entfernung von Software und differenziert zwischen verschiedenen Deinstallationsmethoden.
u4. Funktion zur Ausführung von Cleanmgr mit Voreinstellungen
Es wird ein Array mit Registry-Pfaden definiert, die auf unterschiedliche Cache-Bereiche des Systems verweisen (z. B. Internet Cache, temporäre Dateien usw.).
Für jeden Eintrag wird ein spezieller Registry-Wert (StateFlags1337) mit einem definierten Wert (0, 2 etc.) gesetzt – dies signalisiert Cleanmgr, welche Bereiche bereinigt werden sollen.
Anschließend wird Cleanmgr mit Parametern gestartet, die den Bereinigungsprozess automatisieren (z. B. "/sagerun:1337" für die zuvor definierten Einstellungen).
Alle Schritte werden geloggt.
Nutzen:
Ermöglicht eine automatisierte und konsistente Bereinigung des Systems, um Speicherplatz freizugeben und die Leistung zu optimieren.
Zweck: Mit dieser Funktion werden gezielt bestimmte Ordner gelöscht – hier etwa der Chocolatey-Cache und Adobe ARM-Daten.
Funktionsweise:
Ablauf:
Zunächst wird der Pfad des Chocolatey-Caches überprüft. Liegt dessen Größe über einem definierten Schwellenwert (hier 100 MB), wird der gesamte Inhalt gelöscht.
Ebenso wird der Adobe ARM-Pfad überprüft und – falls vorhanden – geleert.
Beide Aktionen werden protokolliert.
Nutzen:
Reduziert den unnötig belegten Speicherplatz durch veraltete oder temporäre Cache-Daten.
u6. Funktion zur Bereinigung von Event-Logs
PowerShell
functionclear-event-logs{try{ Log-Message "Starte clear-event-logs"$threeMonthsAgo=(Get-Date).AddMonths(-4)# Liste aller Event-Logs$eventLogs=Get-WinEvent-ListLog *|Where{$_.RecordCount-gt0}foreach($login$eventLogs){$logName=$log.LogNametry{# Prüfen, wann das letzte Ereignis erstellt wurde$latestEvent=Get-WinEvent-LogName $logName-MaxEvents 1-ErrorAction Stopif($latestEvent){$latestEventTime=$latestEvent.TimeCreatedif($latestEventTime-lt$threeMonthsAgo){# Log löschen, wenn das letzte Ereignis älter als 3 Monate istClear-EventLog-LogName $logName Log-Message "Event-Log $logName erfolgreich bereinigt"}}}catch{# Wenn keine Ereignisse gefunden werden, überspringenif($_.Exception-is[System.Management.Automation.RuntimeException]){ Log-Message "Keine Ereignisse im Log $logName gefunden oder Fehler beim Abrufen der Ereignisse: $_""WARNING"}else{ Log-Message "Fehler beim Abrufen von Ereignissen im Log $logName : $_""ERROR"}}}}catch{ Log-Message "Fehler in clear-event-logs: $_""ERROR"}}
Zweck: Event-Logs können über längere Zeiträume wachsen und unnötig Speicher belegen. Diese Funktion löscht jene Logs, die über einen definierten Zeitraum (hier: älter als ca. 4 Monate) nicht aktualisiert wurden.
Funktionsweise:
Ablauf:
Es wird ein Schwellenwert-Datum berechnet, das angibt, ab wann Logs als „alt“ gelten.
Mit Get-WinEvent werden alle Event-Logs abgefragt, die Einträge enthalten.
Für jedes Log wird das Datum des letzten Eintrags überprüft.
Sind die Logs „veraltet“, wird Clear-EventLog aufgerufen, um den Inhalt zu löschen.
Besondere Fehlerfälle (z. B. wenn keine Ereignisse vorhanden sind) werden separat behandelt und geloggt.
Nutzen:
Sorgt dafür, dass alte und nicht mehr relevante Log-Daten entfernt werden – ein wichtiger Schritt zur Systemwartung und zur Einsparung von Festplattenspeicher.
u7. Funktion zur Bereinigung des Windows Update Caches
PowerShell
functioncleanup-windows-update{try{ Log-Message "Starte cleanup-windows-update" net stop wuauservRemove-Item-LiteralPath "C:\Windows\SoftwareDistribution\*"-Recurse -Force net start wuauserv Log-Message "Windows Update Cache erfolgreich bereinigt"}catch{ Log-Message "Fehler in cleanup-windows-update: $_""ERROR"}}
Zweck: Diese Funktion räumt den Windows Update-Cache auf, der sich mit der Zeit ansammeln und potenziell zu fehlerhaften Update-Vorgängen führen kann.
Funktionsweise:
Ablauf:
Der Windows Update Service (wuauserv) wird gestoppt, um sicherzustellen, dass keine Dateien in Benutzung sind.
Der Inhalt des Ordners C:\Windows\SoftwareDistribution\* wird gelöscht.
Anschließend wird der Dienst wieder gestartet.
Alle Schritte werden im Log dokumentiert.
Nutzen:
Hilft bei der Behebung von Update-Problemen und stellt sicher, dass der Update-Cache stets auf dem neuesten Stand ist.
u8. Funktion zur Aktualisierung von Gruppenrichtlinien
Zweck: Diese Funktion entfernt alte Benutzerprofile, die seit langer Zeit nicht mehr verwendet wurden – etwa von ehemaligen Mitarbeitern oder inaktiven Nutzern.
Funktionsweise:
Ablauf:
Mithilfe von Get-WmiObject und der Klasse Win32_UserProfile werden alle Benutzerprofile abgefragt.
Es werden nur Profile berücksichtigt, die nicht als „Special“ markiert sind und deren letzter Nutzungszeitpunkt (LastUseTime) vor einem bestimmten Datum liegt.
Jedes gefundene Profil wird mittels Remove-WmiObject gelöscht.
Jeder Löschvorgang wird protokolliert.
Nutzen:
Bereinigt das System von überflüssigen Benutzerprofilen, was zu einer verbesserten Systemleistung und einer geringeren Fragmentierung der Benutzerverzeichnisse führt.
Zweck: Diese Funktion löscht temporäre Dateien, die sich sowohl im Windows-Temp-Ordner als auch im benutzerspezifischen Temp-Verzeichnis angesammelt haben.
Funktionsweise:
Ablauf:
Es werden die Inhalte des Systemordners C:\Windows\Temp\* sowie des benutzerspezifischen Temp-Ordners ($env:TEMP\*) rekursiv gelöscht.
Auch hier sorgt die Fehlerbehandlung dafür, dass eventuelle Probleme im Löschvorgang geloggt werden.
Nutzen:
Durch das Entfernen von temporären Dateien wird unnötig belegter Speicher freigegeben und das System bleibt „sauber“, was wiederum die Performance verbessert.
Zweck: Mit dieser Funktion wird der DNS-Cache geleert, um veraltete DNS-Einträge zu entfernen und so mögliche Netzwerkprobleme (z. B. falsche Namensauflösung) zu vermeiden.
Funktionsweise:
Ablauf:
Der Befehl Clear-DnsClientCache wird ausgeführt, um alle zwischengespeicherten DNS-Einträge zu löschen.
Erfolg oder Fehler werden ebenfalls protokolliert.
Nutzen:
Hilft bei der Behebung von Netzwerkproblemen, wenn DNS-Einträge aktualisiert wurden, aber der alte Cache weiterhin verwendet wird.
Zweck: Diese zentrale Funktion orchestriert die Ausführung aller einzelnen Bereinigungsroutinen, um eine umfassende Systemwartung in einem Durchgang zu ermöglichen.
Funktionsweise:
Ablauf:
Zu Beginn wird eine Log-Meldung ausgegeben, die den Start der Systembereinigung signalisiert.
Nacheinander werden alle vorher definierten Funktionen (wie cleanup-mgr, delete-folders, clear-event-logs, cleanup-windows-update, update-group-policy, cleanup-temp-files und cleanup-dns-cache) aufgerufen.
(Hinweis: Die Funktion cleanup-user-profiles ist auskommentiert – je nach Bedarf kann sie aktiviert werden.)
Abschließend wird ein Log-Eintrag erstellt, der den erfolgreichen Abschluss der Bereinigung bestätigt.
Nutzen:
Sorgt für einen strukturierten und automatisierten Ablauf, der alle wichtigen Wartungsaufgaben zusammenfasst.
Das Komplette Skript sieht dann wie folgt aus:
PowerShell
# Funktion zum Loggen von Nachrichten in eine DateifunctionLog-Message{param([string]$Message,[string]$Type="INFO")$timestamp=Get-Date-Format "yyyy-MM-dd HH:mm:ss"$formattedMessage="$timestamp [$Type] $Message"Write-Output$formattedMessage|Out-File-FilePath "C:\temp\log\system_cleanup.log"-Append# Optional: Protokoll auch zur Konsole ausgebenWrite-Output$formattedMessage}# Funktion zum Abrufen installierter Softwarefunctionget-installedsoftware{param([string]$Name,[string]$Version=$null)try{ Log-Message "Suche nach installierter Software: Name=$Name, Version=$Version"$package=Get-Package-Provider Programs -IncludeWindowsInstaller -Name $Nameif($Version){$package=$package|Where-Object{$_.Version-eq$Version}} Log-Message "Gefundene Pakete: $($package.Count)"return$package}catch{ Log-Message "Fehler beim Abrufen installierter Software: $_""ERROR"}}# Funktion zum Deinstallieren eines Softwarepaketsfunctionuninstall-softwarepackage{param([Microsoft.PackageManagement.Packaging.SoftwareIdentity]$Package)try{ Log-Message "Versuche Deinstallation des Pakets: $($Package.Name)"if($Package.ProviderName-eq"Programs"){$xml=[xml]$Package.SwidTagText$uninstallString=$xml.SoftwareIdentity.Meta.UninstallStringif($uninstallString-match'msiexec.* /i.*'){$uninst=(($uninstallString-split'')[1]-replace'/I','/X')+' /qb'Start-Processmsiexec.exe-ArgumentList $uninst-Wait -NoNewWindow -ErrorAction Stop}elseif(Test-Path-LiteralPath $uninstallString){Start-Process-FilePath $uninstallString-Wait -NoNewWindow -ErrorAction Stop}elseif($uninstallString-match'msiexec.* /x.*'){$uninst=($uninstallString-split'')[1]+' /qb'Start-Processmsiexec.exe-ArgumentList $uninst-Wait -NoNewWindow -ErrorAction Stop} Log-Message "Deinstallation erfolgreich: $($Package.Name)"}else{$Package|Uninstall-Package-ErrorAction Stop Log-Message "Deinstallation erfolgreich über Provider: $($Package.Name)"}}catch{ Log-Message "Fehler bei der Deinstallation des Pakets: $_""ERROR"}}# Funktion zur Ausführung von Cleanmgr mit Voreinstellungenfunctioncleanup-mgr{try{ Log-Message "Starte cleanup-mgr"$regList=@(@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Active Setup Temp Folders";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\BranchCache";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\D3D Shader Cache";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Delivery Optimization Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Diagnostic Data Viewer database files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Downloaded Program Files";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Internet Cache Files";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Language Pack";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Old ChkDsk Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Recycle Bin";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\RetailDemo Offline Content";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Setup Log Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\System error memory dump files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\System error minidump files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Files";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Thumbnail Cache";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Update Cleanup";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\User file versions";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Defender";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Error Reporting Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Upgrade Log Files";Key=2})$keyname='StateFlags1337'foreach($keyin$regList){New-ItemProperty-Path $key.Pfad-Name $keyname-Value $key.Key-Type DWORD -Force |Out-Null}Start-Process"$env:SystemRoot\System32\cleanmgr.exe"-ArgumentList "/D C /verylowdisk /sagerun:1337"-WindowStyle Hidden Log-Message "cleanup-mgr erfolgreich abgeschlossen"}catch{ Log-Message "Fehler in cleanup-mgr: $_""ERROR"}}# Funktion zum Löschen bestimmter Ordnerfunctiondelete-folders{try{ Log-Message "Starte delete-folders"$chocoPath='C:\ProgramData\choco-cache\'$adobeARM='C:\ProgramData\Adobe\ARM\'if((Get-ChildItem-Path $chocoPath-ErrorAction SilentlyContinue |Measure-Object-ErrorAction SilentlyContinue -Property Length -Sum).Sum /1MB-ge100){Remove-Item-Path "$chocoPath*"-Recurse -Force Log-Message "Chocolatey Cache gelöscht"}if(Test-Path-Path $adobeARM){Remove-Item-Path "$adobeARM*"-Recurse -Force Log-Message "Adobe ARM-Daten gelöscht"}}catch{ Log-Message "Fehler in delete-folders: $_""ERROR"}}# Funktion zur Bereinigung von Event-Logsfunctionclear-event-logs{try{ Log-Message "Starte clear-event-logs"$threeMonthsAgo=(Get-Date).AddMonths(-4)# Liste aller Event-Logs$eventLogs=Get-WinEvent-ListLog *|Where{$_.RecordCount-gt0}foreach($login$eventLogs){$logName=$log.LogNametry{# Prüfen, wann das letzte Ereignis erstellt wurde$latestEvent=Get-WinEvent-LogName $logName-MaxEvents 1-ErrorAction Stopif($latestEvent){$latestEventTime=$latestEvent.TimeCreatedif($latestEventTime-lt$threeMonthsAgo){# Log löschen, wenn das letzte Ereignis älter als 3 Monate istClear-EventLog-LogName $logName Log-Message "Event-Log $logName erfolgreich bereinigt"}}}catch{# Wenn keine Ereignisse gefunden werden, überspringenif($_.Exception-is[System.Management.Automation.RuntimeException]){ Log-Message "Keine Ereignisse im Log $logName gefunden oder Fehler beim Abrufen der Ereignisse: $_""WARNING"}else{ Log-Message "Fehler beim Abrufen von Ereignissen im Log $logName : $_""ERROR"}}}}catch{ Log-Message "Fehler in clear-event-logs: $_""ERROR"}}# Funktion zur Bereinigung des Windows Update Cachesfunctioncleanup-windows-update{try{ Log-Message "Starte cleanup-windows-update" net stop wuauservRemove-Item-LiteralPath "C:\Windows\SoftwareDistribution\*"-Recurse -Force net start wuauserv Log-Message "Windows Update Cache erfolgreich bereinigt"}catch{ Log-Message "Fehler in cleanup-windows-update: $_""ERROR"}}# Funktion zur Aktualisierung von Gruppenrichtlinienfunctionupdate-group-policy{try{ Log-Message "Starte update-group-policy" gpupdate /force Log-Message "Gruppenrichtlinien erfolgreich aktualisiert"}catch{ Log-Message "Fehler in update-group-policy: $_""ERROR"}}# Funktion zur Bereinigung alter Benutzerprofilefunctioncleanup-user-profiles{try{ Log-Message "Starte cleanup-user-profiles"$profilesToDelete=Get-WmiObject-Class Win32_UserProfile |Where-Object{$_.Special-eq$false-and$_.LastUseTime-lt((Get-Date).AddDays(-6000)).ToFileTime()}foreach($profile in$profilesToDelete){Remove-WmiObject-InputObject $profile Log-Message "Benutzerprofil gelöscht: $($profile.LocalPath)"} Log-Message "Alte Benutzerprofile erfolgreich bereinigt"}catch{ Log-Message "Fehler in cleanup-user-profiles: $_""ERROR"}}# Funktion zur Bereinigung temporärer Dateienfunctioncleanup-temp-files{try{ Log-Message "Starte cleanup-temp-files"Remove-Item-LiteralPath "C:\Windows\Temp\*"-Recurse -ForceRemove-Item-LiteralPath "$env:TEMP\*"-Recurse -Force Log-Message "Temporäre Dateien erfolgreich bereinigt"}catch{ Log-Message "Fehler in cleanup-temp-files: $_""ERROR"}}# Funktion zur Bereinigung des DNS-Cachesfunctioncleanup-dns-cache{try{ Log-Message "Starte cleanup-dns-cache"Clear-DnsClientCache Log-Message "DNS-Cache erfolgreich bereinigt"}catch{ Log-Message "Fehler in cleanup-dns-cache: $_""ERROR"}}# Hauptfunktion zur Reinigung des Systemsfunctioncleanup{try{ Log-Message "Starte Systembereinigung" cleanup-mgr delete-foldersclear-event-logs cleanup-windows-updateupdate-group-policy#cleanup-user-profiles cleanup-temp-files cleanup-dns-cache Log-Message "Systembereinigung erfolgreich abgeschlossen"}catch{ Log-Message "Fehler in cleanup: $_""ERROR"}}# Ausführung der Systembereinigungcleanup
Wenn man das Ganze noch etwas aufhübscht, die comment-based Hilfe hinzufügt und das logging erweitert, erhält man ein gutes und leicht verständliches Skript.
PowerShell
<#.SYNOPSIS System-Cleanup-Skript für Windows-Systeme..DESCRIPTION Dieses Skript führt verschiedene Reinigungsoperationen auf einem Windows-System durch, einschließlich Entfernung temporärer Dateien, Bereinigung des Disk Space Manager, Löschen des Windows Update-Caches, Bereinigung von Event-Logs und mehr..NOTES Dateiname: system_cleanup.ps1 Autor: Ismahiil Ahmed Version: 1.1 Datum: 2025-03-11 Voraussetzungen: PowerShell 5.1 oder höher, Administratorrechte.EXAMPLE .\system_cleanup.ps1 Führt alle Bereinigungsoperationen durch.#># Stellen Sie sicher, dass das Protokollverzeichnis existiert$logPath="C:\temp\log"if(-not(Test-Path-Path $logPath)){New-Item-Path $logPath-ItemType Directory -Force |Out-Null}<#.SYNOPSIS Protokolliert Nachrichten in eine Datei..DESCRIPTION Protokolliert Nachrichten mit Zeitstempel und Typ in eine Protokolldatei und gibt sie optional auch in der Konsole aus..PARAMETERMessage Die zu protokollierende Nachricht..PARAMETERType Der Nachrichtentyp (z.B. INFO, WARNING, ERROR)..EXAMPLE Log-Message "Operation abgeschlossen" "INFO" Protokolliert "Operation abgeschlossen" als INFO-Nachricht.#>functionLog-Message{[CmdletBinding()]param([Parameter(Mandatory=$true,Position=0)][string]$Message,[Parameter(Mandatory=$false,Position=1)][ValidateSet("INFO","WARNING","ERROR","SUCCESS")][string]$Type="INFO")$timestamp=Get-Date-Format "yyyy-MM-dd HH:mm:ss"$formattedMessage="$timestamp [$Type] $Message"# Stellen Sie sicher, dass die Protokolldatei existiert$logFile="C:\temp\log\system_cleanup.log"if(-not(Test-Path-Path $logFile)){New-Item-Path $logFile-ItemType File -Force |Out-Null}Add-Content-Path $logFile-Value $formattedMessage# Farbcodierung für Konsolenausgabeswitch($Type){"INFO"{Write-Host$formattedMessage-ForegroundColor Cyan }"WARNING"{Write-Host$formattedMessage-ForegroundColor Yellow }"ERROR"{Write-Host$formattedMessage-ForegroundColor Red }"SUCCESS"{Write-Host$formattedMessage-ForegroundColor Green }default{Write-Host$formattedMessage}}}<#.SYNOPSIS Ruft installierte Software ab..DESCRIPTION Sucht nach installierter Software anhand des Namens und optional der Version..PARAMETERName Der Name der zu suchenden Software. Unterstützt Platzhalter (*)..PARAMETERVersion Optional: Die spezifische Version der zu suchenden Software..EXAMPLE get-installedsoftware "Adobe*" Gibt alle installierten Adobe-Softwarepakete zurück..EXAMPLE get-installedsoftware "Microsoft Office" "16.0.12345.67890" Gibt Microsoft Office mit der angegebenen Version zurück, falls installiert..OUTPUTS Microsoft.PackageManagement.Packaging.SoftwareIdentity Ein oder mehrere Softwareidentitätsobjekte, die der Suche entsprechen.#>functionget-installedsoftware{[CmdletBinding()]param([Parameter(Mandatory=$true,Position=0)][string]$Name,[Parameter(Mandatory=$false,Position=1)][string]$Version=$null)try{ Log-Message "Suche nach installierter Software: Name=$Name, Version=$Version"# Hole alle Pakete, die dem Namen entsprechen$packages=Get-Package-Provider Programs -IncludeWindowsInstaller -Name $Name-ErrorAction Stop# Filtere nach Version, falls angegebenif($Version){$packages=$packages|Where-Object{$_.Version-eq$Version}}$count=if($packages){$packages.Count}else{0} Log-Message "Gefundene Pakete: $count"return$packages}catch{ Log-Message "Fehler beim Abrufen installierter Software: $_""ERROR"return$null}}<#.SYNOPSIS Deinstalliert ein Softwarepaket..DESCRIPTION Deinstalliert ein Softwarepaket basierend auf dem übergebenen Paketobjekt. Unterstützt verschiedene Deinstallationsmethoden je nach Pakettyp..PARAMETERPackage Das zu deinstallierende Softwarepaket (SoftwareIdentity-Objekt)..EXAMPLE $office = get-installedsoftware "Microsoft Office" uninstall-softwarepackage $office Deinstalliert Microsoft Office..NOTES Diese Funktion erfordert Administratorrechte.#>functionuninstall-softwarepackage{[CmdletBinding()]param([Parameter(Mandatory=$true,Position=0)][Microsoft.PackageManagement.Packaging.SoftwareIdentity]$Package)try{ Log-Message "Versuche Deinstallation des Pakets: $($Package.Name) (Version: $($Package.Version))"# Prüfe, ob es sich um ein Programm handeltif($Package.ProviderName-eq"Programs"){# Extrahiere Deinstallationsinformationen aus SWID-Tag$xml=[xml]$Package.SwidTagText$uninstallString=$xml.SoftwareIdentity.Meta.UninstallString Log-Message "Deinstallationsstring: $uninstallString""INFO"# Verarbeite je nach Art des Deinstallationsstringsif($uninstallString-match'msiexec.* /[iI].*'){# MSI-Deinstallation (ersetze /I durch /X für Deinstallation)$uninst=(($uninstallString-split'')[1]-replace'/[iI]','/X')+' /qb' Log-Message "Führe MSI-Deinstallation aus: msiexec.exe $uninst"Start-Processmsiexec.exe-ArgumentList $uninst-Wait -NoNewWindow -ErrorAction Stop}elseif($uninstallString-match'msiexec.* /[xX].*'){# MSI-Deinstallation (bereits mit /X)$uninst=($uninstallString-split'')[1]+' /qb' Log-Message "Führe MSI-Deinstallation aus: msiexec.exe $uninst"Start-Processmsiexec.exe-ArgumentList $uninst-Wait -NoNewWindow -ErrorAction Stop}elseif(Test-Path-LiteralPath $uninstallString-ErrorAction SilentlyContinue){# Ausführbare Deinstallationsdatei Log-Message "Führe Deinstallationsprogramm aus: $uninstallString"Start-Process-FilePath $uninstallString-Wait -NoNewWindow -ErrorAction Stop}else{# Versuche, den Deinstallationsstring direkt auszuführen Log-Message "Versuche direkten Aufruf des Deinstallationsstrings"Invoke-Expression$uninstallString} Log-Message "Deinstallation erfolgreich: $($Package.Name)""SUCCESS"}else{# Für andere Provider (z.B. PowerShellGet, Chocolatey) Log-Message "Deinstalliere über Paketprovider: $($Package.ProviderName)"$Package|Uninstall-Package-Force -ErrorAction Stop Log-Message "Deinstallation erfolgreich über Provider: $($Package.Name)""SUCCESS"}}catch{ Log-Message "Fehler bei der Deinstallation des Pakets: $_""ERROR"}}<#.SYNOPSIS Führt den Windows Disk Cleanup Manager mit vordefinierten Einstellungen aus..DESCRIPTION Konfiguriert und startet den Windows Disk Cleanup Manager (cleanmgr.exe) mit vordefinierten Einstellungen zur Bereinigung verschiedener Systemdateien..EXAMPLE cleanup-mgr Führt den Disk Cleanup Manager mit den vordefinierten Einstellungen aus..NOTES Diese Funktion erfordert Administratorrechte. Ein Wert von 2 bedeutet, dass diese Kategorie zur Bereinigung ausgewählt wird.#>functioncleanup-mgr{[CmdletBinding()]param()try{ Log-Message "Starte cleanup-mgr"# Definiere die zu bereinigenden Kategorien$regList=@(@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Active Setup Temp Folders";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\BranchCache";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\D3D Shader Cache";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Delivery Optimization Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Diagnostic Data Viewer database files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Downloaded Program Files";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Internet Cache Files";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Language Pack";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Old ChkDsk Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Recycle Bin";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\RetailDemo Offline Content";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Setup Log Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\System error memory dump files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\System error minidump files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Files";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Thumbnail Cache";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Update Cleanup";Key=2},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\User file versions";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Defender";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Error Reporting Files";Key=0},@{Pfad="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Upgrade Log Files";Key=2})# Verwende einen eindeutigen Namen für den Cleanup-Lauf$keyname='StateFlags1337'# Setze die Registry-Einträge für jede Kategorieforeach($keyin$regList){# Prüfe, ob Registry-Pfad existiert, und erstelle ihn ggf.if(-not(Test-Path-Path $key.Pfad)){New-Item-Path $key.Pfad-Force |Out-Null}# Setze oder aktualisiere den Registry-WertNew-ItemProperty-Path $key.Pfad-Name $keyname-Value $key.Key-Type DWORD -Force |Out-Null}# Starte den Disk Cleanup Manager Log-Message "Starte cleanmgr.exe mit vordefinierten Einstellungen"Start-Process-FilePath "$env:SystemRoot\System32\cleanmgr.exe"-ArgumentList "/D C /verylowdisk /sagerun:1337"-WindowStyle Hidden-Wait Log-Message "cleanup-mgr erfolgreich abgeschlossen""SUCCESS"}catch{ Log-Message "Fehler in cleanup-mgr: $_""ERROR"}}<#.SYNOPSIS Löscht spezifische Cache-Ordner und temporäre Dateien..DESCRIPTION Löscht den Chocolatey-Cache und Adobe ARM-Daten, wenn sie vorhanden sind und bestimmte Größenbedingungen erfüllen..EXAMPLE delete-folders Löscht Chocolatey-Cache und Adobe ARM-Daten, falls sie existieren.#>functiondelete-folders{[CmdletBinding()]param()try{ Log-Message "Starte delete-folders"# Definiere zu prüfende Pfade$foldersToCheck=@(@{Path='C:\ProgramData\choco-cache\';Name="Chocolatey Cache";SizeThreshold=100;# in MBCheckSize=$true;},@{Path='C:\ProgramData\Adobe\ARM\';Name="Adobe ARM-Daten";CheckSize=$false;})# Prüfe und lösche jeden Ordner gemäß den Bedingungenforeach($folderin$foldersToCheck){if(Test-Path-Path $folder.Path){$shouldDelete=$true# Prüfe Größe, falls erforderlichif($folder.CheckSize){$size=(Get-ChildItem-Path $folder.Path-Recurse -ErrorAction SilentlyContinue |Measure-Object-Property Length -Sum -ErrorAction SilentlyContinue).Sum /1MBif($size-lt$folder.SizeThreshold){ Log-Message "$($folder.Name) ist kleiner als $($folder.SizeThreshold) MB (aktuell: $([math]::Round($size,2)) MB), wird übersprungen""INFO"$shouldDelete=$false}else{ Log-Message "$($folder.Name) hat die Größe von $([math]::Round($size,2)) MB""INFO"}}# Lösche den Ordner, wenn die Bedingungen erfüllt sindif($shouldDelete){Remove-Item-Path "$($folder.Path)*"-Recurse -Force -ErrorAction Stop Log-Message "$($folder.Name) erfolgreich gelöscht""SUCCESS"}}else{ Log-Message "$($folder.Name) nicht gefunden, Überspringen""INFO"}} Log-Message "delete-folders abgeschlossen""SUCCESS"}catch{ Log-Message "Fehler in delete-folders: $_""ERROR"}}<#.SYNOPSIS Bereinigt Windows-Event-Logs, die älter als ein bestimmter Zeitraum sind..DESCRIPTION Identifiziert Event-Logs, deren letzter Eintrag älter als vier Monate ist, und löscht deren Inhalt..EXAMPLE clear-event-logs Bereinigt alle Event-Logs, deren letzte Einträge älter als vier Monate sind..NOTES Diese Funktion erfordert Administratorrechte.#>functionclear-event-logs{[CmdletBinding()]param()try{ Log-Message "Starte clear-event-logs"# Definiere den Grenzwert (vier Monate zurück)$threeMonthsAgo=(Get-Date).AddMonths(-4) Log-Message "Lösche Event-Logs mit Einträgen älter als $($threeMonthsAgo.ToString('yyyy-MM-dd'))""INFO"# Hole alle Event-Logs mit Einträgen$eventLogs=Get-WinEvent-ListLog *-ErrorAction SilentlyContinue |Where-Object{$_.RecordCount-gt0-and$_.IsEnabled-and$_.IsLogFull-eq$false}$clearedCount=0$skippedCount=0$errorCount=0foreach($login$eventLogs){$logName=$log.LogNametry{# Prüfe, wann das letzte Ereignis erstellt wurde$latestEvent=Get-WinEvent-LogName $logName-MaxEvents 1-ErrorAction Stopif($latestEvent){$latestEventTime=$latestEvent.TimeCreatedif($latestEventTime-lt$threeMonthsAgo){# Log löschen, wenn das letzte Ereignis älter als die Grenze ist[System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog($logName)$clearedCount++ Log-Message "Event-Log '$logName' erfolgreich bereinigt (letzter Eintrag: $($latestEventTime.ToString('yyyy-MM-dd')))""SUCCESS"}else{$skippedCount++ Log-Message "Event-Log '$logName' übersprungen (letzter Eintrag: $($latestEventTime.ToString('yyyy-MM-dd')))""INFO"}}}catch{$errorCount++# Wenn keine Ereignisse gefunden werden oder andere Fehler auftretenif($_.Exception-is[System.Management.Automation.RuntimeException]-or$_.Exception.Message-match"No events"){ Log-Message "Keine Ereignisse im Log '$logName' gefunden oder unzureichende Berechtigungen""WARNING"}else{ Log-Message "Fehler beim Bearbeiten des Logs '$logName': $_""ERROR"}}} Log-Message "Event-Log-Bereinigung abgeschlossen: $clearedCount gelöscht, $skippedCount übersprungen, $errorCount Fehler""SUCCESS"}catch{ Log-Message "Fehler in clear-event-logs: $_""ERROR"}}<#.SYNOPSIS Bereinigt den Windows Update Cache..DESCRIPTION Stoppt den Windows Update-Dienst, löscht den Inhalt des SoftwareDistribution-Ordners und startet den Dienst wieder..EXAMPLE cleanup-windows-update Bereinigt den Windows Update Cache..NOTES Diese Funktion erfordert Administratorrechte.#>functioncleanup-windows-update{[CmdletBinding()]param()try{ Log-Message "Starte cleanup-windows-update"# Prüfe Größe des Update-Caches vor der Bereinigung$updateCachePath="C:\Windows\SoftwareDistribution"$sizeBefore=0if(Test-Path-Path $updateCachePath){$sizeBefore=(Get-ChildItem-Path $updateCachePath-Recurse -ErrorAction SilentlyContinue |Measure-Object-Property Length -Sum -ErrorAction SilentlyContinue).Sum /1MB Log-Message "Windows Update Cache-Größe vor Bereinigung: $([math]::Round($sizeBefore,2)) MB""INFO"}# Stoppe den Windows Update-Dienst Log-Message "Stoppe Windows Update-Dienst""INFO"Stop-Service-Name wuauserv -Force -ErrorAction Stop# Lösche den Inhalt des SoftwareDistribution-Ordnersif(Test-Path-Path $updateCachePath){Remove-Item-LiteralPath "$updateCachePath\*"-Recurse -Force -ErrorAction SilentlyContinue Log-Message "Windows Update Cache-Dateien gelöscht""SUCCESS"}# Starte den Windows Update-Dienst wieder Log-Message "Starte Windows Update-Dienst""INFO"Start-Service-Name wuauserv -ErrorAction Stop# Prüfe Größe nach der Bereinigung$sizeAfter=0if(Test-Path-Path $updateCachePath){$sizeAfter=(Get-ChildItem-Path $updateCachePath-Recurse -ErrorAction SilentlyContinue |Measure-Object-Property Length -Sum -ErrorAction SilentlyContinue).Sum /1MB$savedSpace=$sizeBefore-$sizeAfter Log-Message "Windows Update Cache-Größe nach Bereinigung: $([math]::Round($sizeAfter,2)) MB (gespart: $([math]::Round($savedSpace,2)) MB)""SUCCESS"} Log-Message "Windows Update Cache erfolgreich bereinigt""SUCCESS"}catch{ Log-Message "Fehler in cleanup-windows-update: $_""ERROR"# Versuche, den Windows Update-Dienst zu starten, falls er gestoppt wurdetry{Start-Service-Name wuauserv -ErrorAction SilentlyContinue}catch{ Log-Message "Konnte Windows Update-Dienst nicht neu starten: $_""ERROR"}}}<#.SYNOPSIS Aktualisiert Gruppenrichtlinien..DESCRIPTION Führt eine Aktualisierung der Gruppenrichtlinien auf dem Computer durch..EXAMPLE update-group-policy Aktualisiert Gruppenrichtlinien mit Force-Option..NOTES Diese Funktion erfordert Administratorrechte.#>functionupdate-group-policy{[CmdletBinding()]param()try{ Log-Message "Starte update-group-policy"# Führe gpupdate mit Force-Option aus$process=Start-Process-FilePath "gpupdate.exe"-ArgumentList "/force"-Wait -PassThru -NoNewWindow# Prüfe Exit-Codeif($process.ExitCode-eq0){ Log-Message "Gruppenrichtlinien erfolgreich aktualisiert""SUCCESS"}else{ Log-Message "Gruppenrichtlinien-Update abgeschlossen mit Exit-Code $($process.ExitCode)""WARNING"}}catch{ Log-Message "Fehler in update-group-policy: $_""ERROR"}}<#.SYNOPSIS Bereinigt alte Benutzerprofile..DESCRIPTION Identifiziert und löscht Benutzerprofile, die seit einer bestimmten Zeit nicht mehr verwendet wurden..EXAMPLE cleanup-user-profiles Bereinigt Benutzerprofile, die seit mehr als 6000 Tagen nicht verwendet wurden..NOTES Diese Funktion erfordert Administratorrechte. Systemprofile und aktive Profile werden nicht gelöscht.#>functioncleanup-user-profiles{[CmdletBinding()]param()try{ Log-Message "Starte cleanup-user-profiles"# Berechne das Datum für die Filterung (6000 Tage zurück)$cutoffDate=(Get-Date).AddDays(-6000)$cutoffFileTime=$cutoffDate.ToFileTime() Log-Message "Suche nach Benutzerprofilen, die seit $($cutoffDate.ToString('yyyy-MM-dd')) nicht verwendet wurden""INFO"# Hole alle Benutzerprofile$allProfiles=Get-WmiObject-Class Win32_UserProfile -ErrorAction Stop# Filtere die zu löschenden Profile$profilesToDelete=$allProfiles|Where-Object{# Überspringe Systemprofile, spezielle Profile und das aktuell angemeldete Profil$_.Special-eq$false-and$_.Loaded-eq$false-and$_.LastUseTime-ne$null-and$_.LastUseTime-lt$cutoffFileTime}if($profilesToDelete.Count-eq0){ Log-Message "Keine alten Benutzerprofile gefunden, die gelöscht werden können""INFO"return}# Zeige die zu löschenden Profile anforeach($profile in$profilesToDelete){$lastUseDate=[DateTime]::FromFileTime($profile.LastUseTime) Log-Message "Wird gelöscht: $($profile.LocalPath) (Zuletzt verwendet: $($lastUseDate.ToString('yyyy-MM-dd')))""INFO"# Lösche das Profiltry{Remove-WmiObject-InputObject $profile -ErrorAction Stop Log-Message "Benutzerprofil gelöscht: $($profile.LocalPath)""SUCCESS"}catch{ Log-Message "Fehler beim Löschen des Profils $($profile.LocalPath): $_""ERROR"}} Log-Message "Alte Benutzerprofile erfolgreich bereinigt""SUCCESS"}catch{ Log-Message "Fehler in cleanup-user-profiles: $_""ERROR"}}<#.SYNOPSIS Bereinigt temporäre Dateien..DESCRIPTION Löscht temporäre Dateien aus dem Windows-Temp-Verzeichnis und dem Temp-Verzeichnis des aktuellen Benutzers..EXAMPLE cleanup-temp-files Bereinigt alle temporären Dateien..NOTES Einige Dateien können möglicherweise nicht gelöscht werden, wenn sie von Prozessen verwendet werden.#>functioncleanup-temp-files{[CmdletBinding()]param()try{ Log-Message "Starte cleanup-temp-files"# Definiere zu bereinigende Verzeichnisse$tempFolders=@(@{Path="C:\Windows\Temp";Name="Windows-Temp";},@{Path=$env:TEMP;Name="Benutzer-Temp";})foreach($folderin$tempFolders){if(Test-Path-Path $folder.Path){# Ermittle Größe vor der Bereinigung$sizeBefore=(Get-ChildItem-Path $folder.Path-Recurse -Force -ErrorAction SilentlyContinue |Measure-Object-Property Length -Sum -ErrorAction SilentlyContinue).Sum /1MB Log-Message "$($folder.Name)-Verzeichnis Größe vor Bereinigung: $([math]::Round($sizeBefore,2)) MB""INFO"# Versuche, alle Dateien zu löschen$errorCount=0Get-ChildItem-Path "$($folder.Path)\*"-Recurse -Force -ErrorAction SilentlyContinue |ForEach-Object{try{Remove-Item-LiteralPath $_.FullName-Recurse -Force -ErrorAction SilentlyContinue}catch{$errorCount++}}# Ermittle Größe nach der Bereinigung$sizeAfter=(Get-ChildItem-Path $folder.Path-Recurse -Force -ErrorAction SilentlyContinue |Measure-Object-Property Length -Sum -ErrorAction SilentlyContinue).Sum /1MB$savedSpace=$sizeBefore-$sizeAfter Log-Message "$($folder.Name)-Verzeichnis Größe nach Bereinigung: $([math]::Round($sizeAfter,2)) MB (gespart: $([math]::Round($savedSpace,2)) MB)""SUCCESS"if($errorCount-gt0){ Log-Message "Konnte $errorCount Dateien in $($folder.Name) nicht löschen (möglicherweise in Verwendung)""WARNING"}}else{ Log-Message "$($folder.Name)-Verzeichnis nicht gefunden""WARNING"}} Log-Message "Temporäre Dateien erfolgreich bereinigt""SUCCESS"}catch{ Log-Message "Fehler in cleanup-temp-files: $_""ERROR"}}<#.SYNOPSIS Bereinigt den DNS-Client-Cache..DESCRIPTION Löscht alle DNS-Einträge aus dem lokalen DNS-Client-Cache..EXAMPLE cleanup-dns-cache Bereinigt den DNS-Client-Cache.#>functioncleanup-dns-cache{[CmdletBinding()]param()try{ Log-Message "Starte cleanup-dns-cache"# Hole DNS-Cache-Einträge vor der Bereinigung$entriesBefore=(Get-DnsClientCache-ErrorAction SilentlyContinue).Count Log-Message "DNS-Cache enthält $entriesBefore Einträge vor der Bereinigung""INFO"# Bereinige den DNS-CacheClear-DnsClientCache-ErrorAction Stop# Bestätige die Bereinigung$entriesAfter=(Get-DnsClientCache-ErrorAction SilentlyContinue).Count Log-Message "DNS-Cache erfolgreich bereinigt ($entriesBefore Einträge entfernt)""SUCCESS"}catch{ Log-Message "Fehler in cleanup-dns-cache: $_""ERROR"}}<#.SYNOPSIS Hauptfunktion zur Systembereinigung..DESCRIPTION Führt alle definierten Bereinigungsfunktionen nacheinander aus. Protokolliert den Fortschritt und die Ergebnisse..EXAMPLE cleanup Führt eine vollständige Systembereinigung durch..NOTES Diese Funktion erfordert Administratorrechte.#>functioncleanup{[CmdletBinding()]param()try{$startTime=Get-Date Log-Message "===== STARTE SYSTEMBEREINIGUNG =====""INFO" Log-Message "Startzeit: $($startTime.ToString('yyyy-MM-dd HH:mm:ss'))""INFO"# Führe alle Bereinigungsfunktionen nacheinander aus Log-Message "Führe cleanup-mgr aus...""INFO" cleanup-mgr Log-Message "Führe delete-folders aus...""INFO" delete-folders Log-Message "Führe clear-event-logs aus...""INFO"clear-event-logs Log-Message "Führe cleanup-windows-update aus...""INFO" cleanup-windows-update Log-Message "Führe update-group-policy aus...""INFO"update-group-policy# Benutzerprofile-Bereinigung ist standardmäßig deaktiviert (potenziell gefährlich)# Entfernen Sie den Kommentar, um diese Funktion zu aktivieren# Log-Message "Führe cleanup-user-profiles aus..." "INFO"# cleanup-user-profiles Log-Message "Führe cleanup-temp-files aus...""INFO" cleanup-temp-files Log-Message "Führe cleanup-dns-cache aus...""INFO" cleanup-dns-cache# Berechne Gesamtdauer$endTime=Get-Date$duration=$endTime-$startTime Log-Message "===== SYSTEMBEREINIGUNG ABGESCHLOSSEN =====""SUCCESS" Log-Message "Endzeit: $($endTime.ToString('yyyy-MM-dd HH:mm:ss'))""INFO" Log-Message "Gesamtdauer: $($duration.Hours) Stunden, $($duration.Minutes) Minuten, $($duration.Seconds) Sekunden""INFO"}catch{ Log-Message "Kritischer Fehler in cleanup: $_""ERROR"}}# Prüfe, ob das Skript als Administrator ausgeführt wird$isAdmin=([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)if(-not$isAdmin){Write-Host"Dieses Skript erfordert Administratorrechte. Bitte starten Sie PowerShell als Administrator."-ForegroundColor Redexit}# Ausführung der Systembereinigungcleanup
Zustimmung verwalten
Um dir ein optimales Erlebnis zu bieten, verwenden wir Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wenn du diesen Technologien zustimmst, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn du deine Zustimmung nicht erteilst oder zurückziehst, können bestimmte Merkmale und Funktionen beeinträchtigt werden.
Funktional
Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt.Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.