Skip to the main content.

ScriptRunner Blog

PowerShell 'Auditing' – zwei Szenarien genauer betrachtet

Inhaltsverzeichnis

 

 

Post Featured Image

Auditing ist keine eingebaute Funktion und die Nachverfolgung von Änderungen müssen manuell durchgeführt werden - wer mit PowerShell arbeitet, hat vielleicht schon überlegt, wie dieses Problem zu lösen ist. In diesem Artikel wird es darum gehen, einige Szenarien zu betrachten, in denen ein angemessenes Maß an Auditing mit verschiedenen Techniken durchgeführt wird, um dies zu bewältigen. Die angebotenen Lösungen sind nicht szenario-spezifisch, sondern werden an den jeweiligen Bedarf angepasst. Auditing (Überprüfung/Überwachung/Protokollierung) ist ein breiter Begriff für das, was in diesem Artikel gezeigt wird. Einfach ausgedrückt, exportiert der nachstehende Script-Code wichtige Informationen in Textdateien, also Logs, zur späteren Verwendung.

Bei der Erstellung dieser Textdateien können wir das Cmdlet Out-File verwenden, mit dem Informationen in Dateien abgelegt werden können. Wir können einfache CSV-Dateien erstellen, indem wir eine Kopfzeile und dann eine Zeile pro CSV-Ausgabe mit durch Komma getrennten Werten angeben. Nachstehend finden Sie ein Beispiel für eine CSV-Datei:

data lines explainedErklärung zum Inhalt der Zeilen

 

Erstellung von Log-Dateien 

Bevor wir eines der beiden Szenarien durchgehen, sollten wir grundlegende Log-Dateien für die Nachverfolgung und Prüfung erstellen. Zunächst müssen wir eine Basis für diese Berichtsdateien erstellen. Hierfür verwenden wir eine Variable ($BasePath), um den aktuellen Pfad zu speichern. Für Log-Dateien wird ein Ordner mit dem Namen "Reports" als Ziel für alle Protokolldateien verwendet.


$BasePath = (Get-Item -Path ".\" -Verbose).FullName
$Path = $BasePath+"\"+"Reports"

Bevor wir Dateien in diesem Ordner speichern, prüfen wir, ob der Ordner existiert. Falls er nicht existiert, erstellen wir ihn:


if(Test-Path $Path -ErrorAction SilentlyContinue){
Write-Host "'nLogging directory exists - $($Path)'n"
}
else{
New-Item -ItemType Directory -Path $Path -Force
}

Außerdem legen wir die Breite des Fensters der PowerShell-Sitzung fest, da dies die Protokollierung langer Zeilen erleichtert.


$Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size (500, 9999)

Um den Überblick zu behalten, wann ein Ereignis eingetreten ist, verwenden wir eine Variable zum Speichern eines Einzeilers, der bei Bedarf aufgerufen werden kann:


$Date = { Get-Date -Format "MM.dd.yyyy-hh.mm" }

Zum Schluss legen wir die Namen unserer Log-Dateien fest:


$LogFileName = "ScriptExecutionLog.txt"
$LogDestination = $Path+"\"+$LogFileName

Jetzt kann es mit den Szenarien losgehen!

 

Szenario Eins

In unserem ersten Szenario handelt es sich um ein großes Unternehmen, das von einer lokalen Lösung zu Microsoft 365 migriert. Innerhalb der Umgebung gibt es eine grundlegende Active Directory-Konfiguration in Bezug auf Domänen und Forests - eins und eins - Forest- und Domänenebene sind beide auf Windows 2012 R2 eingestellt. Die Umgebung umfasst über 40.000 Benutzer und Tausende von Gruppen.

Vor der Migration wurde beschlossen, die primäre SMTP-Adresse mit dem User Principal Name (UPN) abzugleichen, um die Benutzerfreundlichkeit zu erhöhen. Bei einer Überprüfung stellten wir fest, dass über 14.000 Konten nicht übereinstimmten und wir Änderungen vornehmen mussten. Wir hatten auch gehört, dass es Anwendungen gibt, die Konten mit den falschen Informationen neu einrichten (siehe UPN-Änderungen). Es wurde keine Anwendung identifiziert, die sich auf die UPN-Anmeldung stützt, aber wir wollten alle Änderungen verfolgen, für den Fall, dass eine Rückgängigmachung erforderlich ist.

Zunächst müssen wir einen Zeitstempel für den Start des Skripts eingeben (beachten Sie, dass das Datumsformat für die USA üblich ist und internationale Leser möglicherweise etwas anderes benötigen):


$Line = ' ' | Out-File $Destination -Append
$Line = "### START @ $($Date.Invoke()) ###" | Out-File $LogDestination -Append
$Line = ' ' | Out-File $LogDestination -Append

Out-File ist das Cmdlet, das Werte in die zuvor definierte Protokolldatei schreibt.

Log der Ausgangswerte

Für den ersten Teil des Logs definieren wir unsere neue Protokolldatei und die Kopfzeile der Datei (denken Sie an Spaltenüberschriften in einer CSV-Datei):


$BeforeFileName = "BeforeChanges.txt"
$BeforeDestination = $Path+"\"+$BeforeFileName
$FileHeader = 'DisplayName,Alias,PrimarySMTPAddress,UPN' | Out-File $BeforeDestination

 

Dann nehmen wir für jeden Benutzer die Eigenschaftswerte (properties values) des Anzeigenamens (Display Name), des Benutzer-Alias, der primären SMTP-Adresse und des Hauptbenutzernamens (UPN, User Principle Name):

 
$Line = "$DisplayName,$Alias,$PrimarySMTPAddress,$UPN" | Out-File $BeforeDestination -Append

Beachten Sie, dass die Zeile an die Datei angehängt wird, da wir jede Zeile zur Datei hinzufügen und die Datei nicht überschreiben wollen. Wir können auch ausführlicher sein und jeden verarbeiteten Benutzer loggen:


$Output = "The mailbox $DisplayName was processed." | Out-File $LogDestination -Append

** Beachten Sie, dass die verwendete Variable ($Output oder $Line) unerheblich ist, da wir jeden akzeptablen PowerShell-Variablennamen wählen können.

 

Log der Änderungen und Fehler

In diesem Code gibt es einen Codeabschnitt, der sich mit der Änderung des UPN (User Principle Name) des Benutzers befasst. Der Code ist als Try {} Catch {}-Block aufgebaut, so dass wir gute Änderungen (im Try {}-Block) oder Fehlschläge (Catch {}-Block) protokollieren können.


If ($UPN -ne $PrimarySMTP) {
Try {
Set-Mailbox $User -UserPrincipalName $NewUPN -ErrorAction STOP
$Line = "$($Date.Invoke()),Successfully changed the UPN to $PrimarySMTPto the correct matching value for the $User." | Out-File $LogDestination -Append
} Catch {
$Line = "$($Date.Invoke()) , Failed to set the UPN to $PrimarySMTP to the correct matching value for the $User." | Out-File $LogDestination -Append
$Line = "$($Date.Invoke()) , Error message - $_.Exception.Message" | Out-File $LogDestination -Append
}
} Else {
$Line = "$($Date.Invoke()) , UPN for $User is correct, no change needed." | Out-File $LogDestination -Append
}

Beachten Sie die Variable $_.Exception.Message in der Mitte. Damit können wir alle PowerShell-Fehler erfassen, die normalerweise angezeigt worden wären - diese werden nun zur späteren Überprüfung in eine Protokolldatei kopiert.

 

Log geänderter Werte 

Dieser Code sieht genauso aus wie der Abschnitt, den wir für die Protokollierung der Anfangseinstellungen verwendet haben, nur dass unsere Zieldatei eine andere ist, da wir eine Vorher- und eine Nachher-Datei benötigen:


$AfterFileName = "AfterChanges.txt"
$AfterDestination = $Path+"\"+$AfterFileName
$FileHeader = 'DisplayName,Alias,PrimarySMTPAddress,UPN' | Out-File $AfterDestination
$Line = "$DisplayName,$Alias,$PrimarySMTPAddress,$UPN" | Out-File $AfterDestination -Append

 

Ende des Durchlaufs

Zum Schluss fügen wir wieder einen Zeitstempel für das Ende des Skripts hinzu.


$Line = ' ' | Out-File $LogDestination -Append
$Line = "### END @ $($Date.Invoke()) ###" | Out-File $LogDestination -Append
$Line = ' ' | Out-File $LogDestination -Append

Nachfolgend finden Sie ein Beispiel für eine Log-Datei mit einem Start-/Endzeitstempel sowie einem Log der Ereignisse in der Mitte:

START and END timestamps

START- und END-Zeitstempel

Weiter zum nächsten Szenario! 

 

Szenario Zwei

Bei diesem Szenario handelt es sich um eine Exchange-to-Exchange Online-Migration, bei der über 10.000 Postfächer aus mehreren geografischen Regionen in einen einzigen Exchange Online-Tenant verschoben werden. Beim Verschieben dieser Benutzer nutzen wir ein Skript wie das hier beschriebene, das Administratoren für Migrationen verwenden. Um dieses Skript weiter zu verbessern, können wir PowerShell verwenden, um Logs für kritische Elemente im Skript zu erstellen.

Dies ist die Zusammenfassung der Logs, die wir durchführen können:

  • Jede Aktion sowie ein Start- und Stoppdatum für die Skriptausführung im Log festhalten.

  • Fehler protokollieren - den Fehler, den PowerShell generiert hätte, auf dem Bildschirm darstellen.

Start des Skripts - entsprechender Header für die Datei zum Starten des Skripts:


$FileName = "MailboxMoveAdministrationLogging.txt"
$Destination = $Path+"\"+$FileName
$FileCheck = Test-Path $Destination
If (-not($FileCheck)) {
$Line = "This file logs all changes made to move requests in this script" | Out-File $Destination
$Line = "---------------------------------------------------------------" | Out-File $Destination -Append
$Line = " " | Out-File $Destination -Append
}
$Line = ' ' | Out-File $Destination -Append
$Line = "### START @ $($Date.Invoke()) ###" | Out-File $Destination -Append
$Line = ' ' | Out-File $Destination -Append

 

Detaillierte Move Reports 

Mit dieser Funktion wird lediglich der gesamte Report-Wert exportiert, der eine detaillierte Analyse einer Postfachverschiebung in die Cloud liefert und dazu verwendet wird, festzustellen, warum die Verschiebung fehlgeschlagen ist oder pausiert wurde. Im Folgenden wird die primäre SMTP-Adresse des Postfachs abgerufen, eine Ausgabedatei angegeben (spezifisch für die Postfachverschiebung, die wir abfragen), der Bericht des Postfachs abgerufen und dann der gesamte Bericht in die Datei exportiert, die wir in Zeile 2 angegeben haben:


$Mailbox = $MoveRequest.PrimarySMTPAddress
$OutputFile ="$Path"+"\"+"$Mailbox"+"-Report.txt"
$Report = (Get-MoveRequestStatistics -Identity $Mailbox -IncludeReport).Report
$Report.Entries | Ft | Out-File -FilePath $OutputFile

 

Log der Postfachmigration

In diesem Teil des Skripts erstellen wir eine neue Anforderung für eine Postfachmigration, wiederum unter Verwendung eines Try {} Catch {}-Blocks. Wenn die Erstellung erfolgreich war, wird dies protokolliert, und wenn ein Fehler auftritt, wird die Fehlermeldung ebenfalls protokolliert:


$Mailbox = $User.PrimarySMTPAddress
If ($Null -eq (Get-MoveRequest $Mailbox -ErrorAction Silentlycontinue)){
Try {
$Creation = New-MoveRequest -Identity $Mailbox -Remote -RemoteHostName $Endpoint -TargetDeliveryDomain $TargetDomain -RemoteCredential $OPCred -SuspendWhenReadyToComplete -ErrorAction STOP
$Line = "The mailbox move for $Mailbox was successfully created." | Out-File $Destination -Append
} Catch {
$Line = "The mailbox move for $Mailbox was NOT successfully created." | Out-File $Destination -Append
$Line = "$($Date.Invoke()) , Error message - $_.Exception.Message" | Out-File $Destination -Append
}
}

 

Move Request entfernen, letztes Beispiel

In diesem Codeabschnitt haben wir einen Codeblock, der Move Requests entfernt, z. B. weil der Benutzer warten muss oder aus der aktuellen Umzugsliste entfernt wird, weil er das Unternehmen verlässt. Dieser Vorgang ähnelt dem zuvor gezeigten neuen Move Request, und wir haben diesen Codeabschnitt:


Write-host "Removing move request for " -ForegroundColor White -NoNewline
Write-host "$SMTPAddress....." -ForegroundColor Yellow
Try {
Remove-MoveRequest $SMTPAddress -Confirm:$False -ErrorAction STOP
$Line = "$($Date.Invoke()) , Remove move request for $SMTPAddress succeeded." | Out-File $Destination -Append
} Catch {
$Line = "$($Date.Invoke()) , Remove request for $SMTPAddress failed." | Out-File $Destination -Append
$Line = "$($Date.Invoke()) , Error message - $_.Exception.Message" | Out-File $Destination -Append
}

 

Beispiele für Log-Datei Einträge

log file with START END actions logged

Eine Log-Datei mit START, ENDE und protokollierten Aktionen

log file with START END errors logged

Eine Log-Datei mit START, ENDE und in diesem Fall auch mit protokollierten Fehlern

 

Weitere Szenarien?

Welche Szenarien können Sie sich noch vorstellen? Würden Sie separate Protokolle für gute und schlechte Ergebnisse erstellen? Eine weitere mögliche Verwendung von Out-File ist die Dokumentation, z. B. tägliche Postfachstatistiken für alle Benutzer in Exchange Online. Mit anderen Worten, es gibt viele Möglichkeiten, also machen Sie weiter und protokollieren Sie, oder auditieren Sie, oder setzen andere Ideen um mit PowerShell.

 

Wichtiger Hinweis

Die Zahl 10.000 ist in beiden Szenarien wichtig. PowerShell verwendet ein Limit von 1.000 Ergebnissen. Da beide Umgebungen groß sind, ist die Verwendung von -ResultSize Unlimited für einige Cmdlets die einzige Möglichkeit, um Ergebnisse zu erhalten. Ein weiterer Leistungstipp für große Umgebungen: Wenn ein Cmdlet über 'Filter'-Parameter verfügt, verwenden Sie diese, bevor Sie 'Where' zum Filtern der Ergebnisse verwenden. Die Verwendung von 'Where' in einer großen Umgebung kann die Verarbeitungszeit für ein Skript erheblich verlängern.

 

 

Good2know

Report/Audit DB connector

Mit ScriptRunner jederzeit compliant und auditbereit!

Governance und Compliance Readiness verlangen immer öfter die komplette Nachvollziehbarkeit aller Vorgänge. Auch die Anwendung von PowerShell-Scripten ist davon nicht ausgenommen. Mit der externen Datenbank können Sie über die geforderten Zeiträume hinweg jederzeit nachweisen, welche Skripte auf welchem System mit welchen Parametern ausgeführt worden sind, was die Ergebnisse waren usw. Egal, ob Sie einen oder mehrere ScriptRunner Hosts einsetzen.

Verbindung zum SQL-Server mit Fehlertoleranz

Der ScriptRunner Report/Audit DB Connector verbindet den ScriptRunner Host mit der Datenbank auf einem Microsoft SQL-Server. Wenn Sie mehrere ScriptRunner Hosts betreiben, können Sie alle in die gleiche Datenbank schreiben. Somit haben Sie alle Reporting-Informationen über alle Systeme hinweg in einer einzigen Datenbank.

Wurde durch eine Aktion ein Report erzeugt, so wird dieser zuerst in die Umlaufdatenbank auf dem ScriptRunner Host geschrieben. Der Connector erzeugt zusätzlich eine XML-Datei, deren Inhalte automatisch an die Datenbank auf den SQL-Server übertragen werden. Ein Wiederaufsetzen nach Fehlern sorgt dafür, dass kein Report verloren geht und alle zwischenzeitlich angefallenen Daten in der Datenbank gespeichert werden.

 Audit-Connector-Ablauf_720x442-t

 

 Mehr zum technischen Hintergrund hier

 

 

Weiterführende Links

 

 

Zusammenhängende Posts

5 min read

Microsoft Exchange mit PowerShell managen

2 min read

VMUG Webcast: VMware Management meistern mit PowerCLI

Über den Autor: