Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Vile-Gangster/04fae3449a3881d882871d72321a209d to your computer and use it in GitHub Desktop.
Save Vile-Gangster/04fae3449a3881d882871d72321a209d to your computer and use it in GitHub Desktop.
Template_loc_group_manage_by_AD
<#
.SYNOPSIS
Verwaltet lokale Gruppenmitgliedschaften auf Servern nach dem AGDLP-Modell.
.DESCRIPTION
Dieses Skript automatisiert die Verwaltung von serverlokalen Gruppen durch:
- Erstellung von AD-Gruppen pro Server (Globale Gruppe + Domänenlokale Gruppe) gemäß AGDLP
- Migration direkt zugewiesener Domänenbenutzer in die entsprechende Globale Gruppe (GG)
- Verschachtelung der Domänenlokalen Gruppe (DLG) in die lokale Zielgruppe auf jedem Server
- Entfernung von Benutzern aus lokalen Gruppen, sobald sie über GG/DLG verwaltet werden
- Erstellung eines detaillierten Berichts über alle Änderungen (hinzugefügt, entfernt, erstellt)
- Optionaler Versand eines HTML-Berichts per E-Mail mit Fehlerhervorhebung und CSV-Anhang
- Unterstützung für Tiered-Admin-Umgebungen (Tier-0/Tier-1)
Hinweis: Für Tier-1-Systeme muss das Skript mit Tier-1-Berechtigungen oder als NT AUTHORITY\SYSTEM ausgeführt werden.
.PARAMETER GroupSuffix
Logischer Bezeichner für Gruppennamen (z. B. "DCOM" wird zu G_<Server>_DCOM und L_<Server>_DCOM).
.PARAMETER LocalGroupSIDEnd
SID-Suffix zur Identifizierung der lokalen Zielgruppe (562 = Distributed COM-Benutzer).
.PARAMETER EnableMailNotification
Aktiviert ($true) oder deaktiviert ($false) den Versand eines HTML-Zusammenfassungs-E-Mails.
.PARAMETER WhatIf
Simuliert Änderungen ohne sie anzuwenden (Trockenlauf-Modus).
.NOTES
Author : IT-Administration
Version : 2.2
Date : 2025-05-09
Changes : E-Mail-Versand aktiviert; Remoting für Tier-1-Systeme entfernt; ADSI für lokale Operationen verwendet; explizite Lokale Gruppe "Distributed COM-Benutzer"; verbesserte Fehlerbehandlung für SMTP; Tiering-Hinweise hinzugefügt; konsistente ${}-Verwendung; Benutzeranzeige im Format "Nachname, Vorname" für alle Aktionen; Fehler bei Benutzerentfernung aus lokaler Gruppe behoben; Syntaxfehler in Userlist-Log-Ausgabe korrigiert; Join-String durch -join ersetzt für PowerShell 5.1-Kompatibilität; E-Mail-Versand bei keine Änderungen deaktiviert; E-Mail-Optik angepasst: "hinzugefügt" grün, "entfernt" rot, "verschachtelt" orange; E-Mail-Farben korrigiert: angepasste -like-Bedingungen für korrekte Klassen-Zuweisung; "DLG lokal hinzugefügt" nun orange statt grün.
.EXAMPLE
.\Template_loc_group_manage_by_AD.ps1
Führt das Skript mit Standardeinstellungen aus (E-Mail aktiviert).
.EXAMPLE
.\Template_loc_group_manage_by_AD.ps1 -WhatIf
Simuliert die Ausführung ohne Änderungen vorzunehmen.
#>
# ================================================================
# ============================ CONFIG ============================
[CmdletBinding()]
param (
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$GroupSuffix = "DCOM",
[Parameter()]
[ValidatePattern("^\d+$")]
[string]$LocalGroupSIDEnd = "562",
[Parameter()]
[bool]$EnableMailNotification = $true, # Für Tests aktiviert
[Parameter()]
[switch]$WhatIf
)
# Ausschluss-Präfixe für AD-Konten
$ExcludedPrefixes = @('svc_', 'gMSA_')
# AD-Gruppen Präfixe
$GGPräfix = "G_"
$DLGPräfix = "L_"
# AD-Gruppen Beschreibung
$GGDescription = "Verwaltung der Server lokalen ${GroupSuffix} Gruppe"
$DLGDescription = "Verwaltung der Server lokalen ${GroupSuffix} Gruppe"
# OU mit den zu überprüfenden Servern
$ServerOU = "OU=Test,OU=T0,OU=Server,OU=_Administration,DC=TestLab,DC=internal"
# OUs für Gruppen
$DLGroupOU = "OU=Monitoring,OU=Lokale Gruppen,OU=Gruppen,OU=_Administration,DC=TestLab,DC=internal"
$GGGroupOU = "OU=Monitoring,OU=Globale Gruppen,OU=Gruppen,OU=_Administration,DC=TestLab,DC=internal"
# Gruppenkonfiguration
$GCategory = "Security"
$GGScope = "Global"
$DLGScope = "DomainLocal"
# NetBIOS-Name der Domäne
$SMBDomainName = "TestLab"
# Name der lokalen Gruppe
$LocalGroupNameOverride = "Distributed COM-Benutzer"
# Mail-Konfiguration
$MailSender = "ADScripts"
$MailRecipient = "IT-Administration"
$SMTPSrvName = "mail"
$SMTPDomain = "TestLab.de"
$SMTPServer = "${SMTPSrvName}.${SMTPDomain}"
$SMTPPort = 25
$SMTPFrom = "${MailSender}-${GroupSuffix}@${SMTPDomain}"
$SMTPTo = "${MailRecipient}@${SMTPDomain}"
# SMTP-Authentifizierung für Port 587
if ($SMTPPort -eq 587) {
$SMTPUser = "${SMBDomainName}\gMSA_SRV-DC-01$"
$SMTPPassword = ConvertTo-SecureString "" -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($SMTPUser, $SMTPPassword)
}
# ============================ MELDUNGEN & TAGS ============================
$TAG_New = 'Neue'
$TAG_Added = 'hinzugefügt'
$TAG_Removed = 'entfernt'
$TAG_Error = 'FEHLER'
$MSG_Create_GG = "${TAG_New} GG erstellt"
$MSG_Create_GG_Fail = "${TAG_Error} beim Erstellen GG"
$MSG_Create_DLG = "${TAG_New} DLG erstellt"
$MSG_Create_DLG_Fail = "${TAG_Error} beim Erstellen DLG"
$MSG_Nest_GG_Into_DLG = "GG in DLG verschachtelt"
$MSG_Nest_GG_Into_DLG_Fail = "${TAG_Error} beim Verschachteln GG in DLG"
$MSG_LocalGroup_NotFound = "${TAG_Error} Lokale Gruppe nicht gefunden"
$MSG_Read_LocalGroup_Fail = "${TAG_Error} bei Auslesen lokaler Gruppe"
$MSG_AddUser_To_GG = "Benutzer zur GG ${TAG_Added}"
$MSG_AddUser_Fail = "${TAG_Error} bei Benutzerübernahme"
$MSG_Add_DLG_Local = "DLG lokal ${TAG_Added}"
$MSG_Add_DLG_Fail = "${TAG_Error} beim lokalen Hinzufügen DLG"
$MSG_Remove_LocalUser = "Benutzer lokal ${TAG_Removed}"
$MSG_Remove_User_Fail = "${TAG_Error} beim Entfernen aus lokaler Gruppe"
$SUBJECT_PREFIX = "Gruppe"
$SUBJECT_TAG_New = "[Neue Gruppen]"
$SUBJECT_TAG_Changes = "[Mitgliederänderung]"
$SUBJECT_TAG_Errors = "[Fehler]"
# === Protokollierungsfunktion ===
function Write-Log {
param (
[string]$Message,
[string]$Level = "Info"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$color = switch ($Level) {
"Error" { "Red" }
"Warning" { "Yellow" }
"Success" { "Green" }
default { "Cyan" }
}
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
Add-Content -Path ".\GroupManagement.log" -Value "[$timestamp] [$Level] $Message"
}
# Container für Berichtszeilen
$ReportData = @()
$ChangesDetected = $false
# ================================================================
# ============================ START =============================
try {
$ServerList = Get-ADComputer -Filter * -SearchBase $ServerOU -ErrorAction Stop
Write-Log -Message "Serverliste aus OU ${ServerOU} erfolgreich abgerufen." -Level Success
} catch {
Write-Log -Message "Fehler beim Abrufen der Serverliste: $($_.Exception.Message)" -Level Error
exit 1
}
foreach ($Server in $ServerList) {
$ComputerName = $Server.Name
$DLGroupName = "${DLGPräfix}${ComputerName}_${GroupSuffix}"
$GGGroupName = "${GGPräfix}${ComputerName}_${GroupSuffix}"
$Userlist = @()
Write-Log -Message "Verarbeite Server: ${ComputerName}" -Level Info
# === Globale Gruppe prüfen/erstellen ===
try {
$group = Get-ADGroup -Identity ${GGGroupName} -ErrorAction Stop
Write-Log -Message "Globale Gruppe ${GGGroupName} existiert bereits." -Level Info
} catch {
if (-not $WhatIf) {
New-ADGroup `
-Name ${GGGroupName} `
-SamAccountName ${GGGroupName} `
-GroupCategory $GCategory `
-GroupScope $GGScope `
-DisplayName ${GGGroupName} `
-Path $GGGroupOU `
-Description $GGDescription `
-ErrorAction Stop
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = $MSG_Create_GG
Objekt = ${GGGroupName}
}
$ChangesDetected = $true
Write-Log -Message "Globale Gruppe ${GGGroupName} erstellt." -Level Success
} else {
Write-Log -Message "Würde globale Gruppe ${GGGroupName} erstellen." -Level Info
}
}
# === Domänenlokale Gruppe prüfen/erstellen ===
try {
$group = Get-ADGroup -Identity ${DLGroupName} -ErrorAction Stop
Write-Log -Message "Domänenlokale Gruppe ${DLGroupName} existiert bereits." -Level Info
} catch {
if (-not $WhatIf) {
New-ADGroup `
-Name ${DLGroupName} `
-SamAccountName ${DLGroupName} `
-GroupCategory $GCategory `
-GroupScope $DLGScope `
-DisplayName ${DLGroupName} `
-Path $DLGroupOU `
-Description $DLGDescription `
-ErrorAction Stop
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = $MSG_Create_DLG
Objekt = ${DLGroupName}
}
$ChangesDetected = $true
Write-Log -Message "Domänenlokale Gruppe ${DLGroupName} erstellt." -Level Success
} else {
Write-Log -Message "Würde domänenlokale Gruppe ${DLGroupName} erstellen." -Level Info
}
}
# === GG in DLG verschachteln ===
try {
$isMember = Get-ADGroupMember -Identity ${DLGroupName} -ErrorAction Stop |
Where-Object { $_.SamAccountName -eq ${GGGroupName} }
if (-not $isMember) {
if (-not $WhatIf) {
Add-ADGroupMember `
-Identity ${DLGroupName} `
-Members ${GGGroupName} `
-ErrorAction Stop
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = $MSG_Nest_GG_Into_DLG
Objekt = ${GGGroupName}
}
$ChangesDetected = $true
Write-Log -Message "Globale Gruppe ${GGGroupName} in ${DLGroupName} verschachtelt." -Level Success
} else {
Write-Log -Message "Würde ${GGGroupName} in ${DLGroupName} verschachteln." -Level Info
}
}
} catch {
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = "${MSG_Nest_GG_Into_DLG_Fail}: $($_.Exception.Message)"
Objekt = ${GGGroupName}
}
$ChangesDetected = $true
Write-Log -Message "Fehler beim Verschachteln von ${GGGroupName} in ${DLGroupName}: $($_.Exception.Message)" -Level Error
}
# === Lokale Gruppe festlegen ===
try {
$LocalGroupName = $LocalGroupNameOverride
Write-Log -Message "Lokale Gruppe festgelegt: ${LocalGroupName} (SID-Ende: ${LocalGroupSIDEnd})" -Level Info
} catch {
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = "${MSG_LocalGroup_NotFound}: $($_.Exception.Message)"
Objekt = "SID-Ende -${LocalGroupSIDEnd}"
}
$ChangesDetected = $true
Write-Log -Message "Fehler beim Festlegen der lokalen Gruppe: $($_.Exception.Message)" -Level Error
continue
}
# === Mitglieder der lokalen Gruppe auslesen (ADSI) ===
try {
$LocalGroupObj = [ADSI]"WinNT://${ComputerName}/${LocalGroupName},group"
$rawMembers = $LocalGroupObj.psbase.Invoke("Members") | ForEach-Object {
$name = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
$adsPath = $_.GetType().InvokeMember("AdsPath", 'GetProperty', $null, $_, $null)
$class = $_.GetType().InvokeMember("Class", 'GetProperty', $null, $_, $null)
[PSCustomObject]@{
Name = $name
AdsPath = $adsPath
Class = $class
}
}
$memberList = $rawMembers | ForEach-Object { "$($_.Name) ($($_.AdsPath), Class: $($_.Class))" }
$memberString = $memberList -join ', '
Write-Log -Message "Rohe Mitglieder für ${LocalGroupName}: $memberString" -Level Info
$LocalAdmins = $rawMembers | Where-Object {
# Nur Benutzer (Class = User) und passender Domänenname
$_.Class -eq "User" -and $_.Name -match "^(${SMBDomainName}\\)?(.+)$"
} | ForEach-Object {
$samAccountName = if ($_.Name -match "^${SMBDomainName}\\(.+)$") {
$Matches[1].Trim()
} else {
$_.Name.Trim()
}
try {
# Hole Nachname und Vorname aus AD
$user = Get-ADUser -Identity $samAccountName -Properties sn, givenName -ErrorAction Stop
$displayName = if ($user.sn -and $user.givenName) {
"$($user.sn), $($user.givenName)"
} else {
$samAccountName # Fallback auf SamAccountName
}
[PSCustomObject]@{
SamAccountName = $samAccountName
DisplayName = $displayName
}
} catch {
Write-Log -Message "Fehler beim Abrufen von AD-Daten für ${samAccountName}: $($_.Exception.Message). Verwende SamAccountName." -Level Warning
[PSCustomObject]@{
SamAccountName = $samAccountName
DisplayName = $samAccountName
}
}
} | Where-Object { $_.SamAccountName }
$displayNames = $LocalAdmins | ForEach-Object { $_.DisplayName }
Write-Log -Message "Gefilterte Domänenbenutzer für ${LocalGroupName}: $($displayNames -join ', ')" -Level Info
} catch {
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = "${MSG_Read_LocalGroup_Fail}: $($_.Exception.Message). Möglicherweise Netzwerk- oder Berechtigungsproblem. Führe das Skript lokal auf ${ComputerName} als NT AUTHORITY\SYSTEM aus."
Objekt = ${LocalGroupName}
}
$ChangesDetected = $true
Write-Log -Message "Fehler beim Auslesen der lokalen Gruppe ${LocalGroupName}: $($_.Exception.Message)." -Level Error
continue
}
# === Domänenbenutzer aus lokaler Gruppe → GG verschieben ===
$Userlist = @() # Initialisiere Userlist als leeres Array
foreach ($user in $LocalAdmins) {
$samAccountName = $user.SamAccountName
$displayName = $user.DisplayName
$exclude = $false
foreach ($prefix in $ExcludedPrefixes) {
if ($samAccountName -like "${prefix}*") {
Write-Log -Message "Benutzer ${displayName} wird übersprungen (Ausschlusspräfix)." -Level Warning
$exclude = $true
break
}
}
if ($exclude) { continue }
try {
$ADUserObject = Get-ADUser -Identity ${samAccountName} -ErrorAction Stop
$alreadyMember = Get-ADGroupMember -Identity ${GGGroupName} -ErrorAction Stop | Where-Object {
$_.SamAccountName -eq ${samAccountName}
}
if (-not $alreadyMember) {
if (-not $WhatIf) {
Add-ADGroupMember `
-Identity ${GGGroupName} `
-Members $ADUserObject.DistinguishedName `
-ErrorAction Stop
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = $MSG_AddUser_To_GG
Objekt = ${displayName} # Verwende Nachname, Vorname
}
$ChangesDetected = $true
Write-Log -Message "Benutzer ${displayName} zu ${GGGroupName} hinzugefügt." -Level Success
} else {
Write-Log -Message "Würde Benutzer ${displayName} zu ${GGGroupName} hinzufügen." -Level Info
}
# Füge Benutzer mit SamAccountName und DisplayName zu Userlist hinzu
$Userlist += [PSCustomObject]@{
SamAccountName = $samAccountName
DisplayName = $displayName
}
}
} catch {
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = "${MSG_AddUser_Fail}: $($_.Exception.Message)"
Objekt = ${displayName}
}
$ChangesDetected = $true
Write-Log -Message "Fehler beim Hinzufügen von ${displayName} zu ${GGGroupName}: $($_.Exception.Message)" -Level Error
continue
}
}
# === DLG in lokale Gruppe eintragen ===
try {
$GroupObj = [ADSI]"WinNT://${ComputerName}/${LocalGroupName},group"
# SID-Pfad (einzige Methode)
$ADGroup = Get-ADGroup -Identity ${DLGroupName} -ErrorAction Stop
$ADGroupSID = $ADGroup.SID.Value
$ADGroupWinNT_SID = "WinNT://${ADGroupSID}"
# Prüfe, ob die Gruppe bereits Mitglied ist
$alreadyMember = $GroupObj.psbase.Invoke("Members") | ForEach-Object {
$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
} | Where-Object { $_ -ieq ${DLGroupName} }
if (-not $alreadyMember) {
if (-not $WhatIf) {
# Warte, um AD-Replikation zu gewährleisten
Start-Sleep -Seconds 10
# Prüfe Replikation auf dem Ziel-DC
try {
# Wähle den ersten HostName als String
$dc = (Get-ADDomainController -Discover -DomainName $SMBDomainName).HostName | Select-Object -First 1
Get-ADGroup -Identity ${DLGroupName} -Server $dc -ErrorAction Stop
Write-Log -Message "Gruppe ${DLGroupName} auf DC ${dc} repliziert." -Level Info
} catch {
Write-Log -Message "Warnung: Gruppe ${DLGroupName} möglicherweise nicht repliziert: $($_.Exception.Message). Fahre fort, da SID-Pfad verwendet wird." -Level Warning
}
# Füge Gruppe hinzu
$GroupObj.Add($ADGroupWinNT_SID)
Write-Log -Message "DLG ${DLGroupName} lokal zu ${LocalGroupName} hinzugefügt (SID-Pfad, SID: ${ADGroupSID})." -Level Success
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = $MSG_Add_DLG_Local
Objekt = ${DLGroupName}
}
$ChangesDetected = $true
} else {
Write-Log -Message "Würde DLG ${DLGroupName} lokal zu ${LocalGroupName} hinzufügen." -Level Info
}
} else {
Write-Log -Message "DLG ${DLGroupName} ist bereits Mitglied von ${LocalGroupName}." -Level Info
}
} catch {
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = "${MSG_Add_DLG_Fail}: $($_.Exception.Message). Möglicherweise AD-Replikationsproblem oder ADSI-Beschränkung. Führe das Skript lokal auf ${ComputerName} als NT AUTHORITY\SYSTEM aus oder überprüfe die AD-Replikation."
Objekt = ${DLGroupName}
}
$ChangesDetected = $true
Write-Log -Message "Fehler beim lokalen Hinzufügen von ${DLGroupName}: $($_.Exception.Message)." -Level Error
}
# === Lokale Benutzer entfernen ===
$userListString = ($Userlist | ForEach-Object { "$($_.DisplayName) ($($_.SamAccountName))" }) -join ', '
Write-Log -Message "Userlist für Entfernung: $userListString" -Level Info
foreach ($user in $Userlist) {
$samAccountName = $user.SamAccountName
$displayName = $user.DisplayName
try {
$LocalGroupObj = [ADSI]"WinNT://${ComputerName}/${LocalGroupName},group"
# Normalisiere Mitgliedernamen (entferne Domänenpräfix)
$members = $LocalGroupObj.psbase.Invoke("Members") | ForEach-Object {
$name = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
if ($name -match "^${SMBDomainName}\\(.+)$") {
$Matches[1].Trim()
} else {
$name.Trim()
}
}
Write-Log -Message "Mitglieder der lokalen Gruppe ${LocalGroupName}: $($members -join ', ')" -Level Info
# Versuche direkt zu entfernen
if (-not $WhatIf) {
$LocalGroupObj.Remove("WinNT://${SMBDomainName}/${samAccountName}")
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = $MSG_Remove_LocalUser
Objekt = $displayName # Verwende Nachname, Vorname
}
$ChangesDetected = $true
Write-Log -Message "Benutzer ${displayName} lokal aus ${LocalGroupName} entfernt." -Level Success
} else {
Write-Log -Message "Würde Benutzer ${displayName} lokal aus ${LocalGroupName} entfernen." -Level Info
}
} catch {
# Ignoriere Fehler, wenn der Benutzer nicht Mitglied ist
if ($_.Exception.Message -match "Das angegebene Konto ist kein Mitglied der Gruppe") {
Write-Log -Message "Benutzer ${displayName} ist bereits nicht in ${LocalGroupName}." -Level Info
} else {
$ReportData += [PSCustomObject]@{
Server = $ComputerName
Aktion = "${MSG_Remove_User_Fail}: $($_.Exception.Message). Möglicherweise Tier-1-Zugriffsbeschränkung. Führe das Skript mit Tier-1-Berechtigungen oder als NT AUTHORITY\SYSTEM aus."
Objekt = $displayName
}
$ChangesDetected = $true
Write-Log -Message "Fehler beim Entfernen von ${displayName} aus ${LocalGroupName}: $($_.Exception.Message). Möglicherweise Tier-1-Zugriffsbeschränkung." -Level Error
}
}
}
}
# ================================================================
# ========================== E-MAIL SENDEN =========================
if ($EnableMailNotification -and $ReportData.Count -gt 0) {
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$tempFile = "${env:TEMP}\ADScripts_GroupManagement_${timestamp}.txt"
$subjectSuffix = ""
if ($ReportData | Where-Object { $_.Aktion -like "${TAG_New}*" }) {
$subjectSuffix += $SUBJECT_TAG_New
}
if ($ReportData | Where-Object { $_.Aktion -like "${TAG_Added}*" -or $_.Aktion -like "${TAG_Removed}*" }) {
$subjectSuffix += $SUBJECT_TAG_Changes
}
if ($ReportData | Where-Object { $_.Aktion -like "${TAG_Error}*" }) {
$subjectSuffix += $SUBJECT_TAG_Errors
}
$SMTPSubject = "[${SUBJECT_PREFIX} ${GroupSuffix}] ${subjectSuffix}"
$styledRows = $ReportData | ForEach-Object {
$rowClass = if ($_.Aktion -like "*${TAG_Error}*") {
' class="error"'
} elseif ($_.Aktion -like "*${TAG_New}*" -or ($_.Aktion -like "*${TAG_Added}*" -and $_.Aktion -ne $MSG_Add_DLG_Local)) {
' class="added"'
} elseif ($_.Aktion -like "*${TAG_Removed}*") {
' class="removed"'
} elseif ($_.Aktion -eq $MSG_Nest_GG_Into_DLG -or $_.Aktion -eq $MSG_Add_DLG_Local) {
' class="nested"'
} else {
''
}
# Debug-Ausgabe, um die Zuweisung der Klassen zu überprüfen
Write-Log -Message "E-Mail-Zeile: Aktion '$($_.Aktion)' → CSS-Klasse '$rowClass'" -Level Info
"<tr${rowClass}><td>$($_.Server)</td><td>$($_.Aktion)</td><td>$($_.Objekt)</td></tr>"
}
$htmlBody = @"
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
body { font-family: "Segoe UI", sans-serif; font-size: 13px; background-color: #f4f4f4; padding: 20px; }
h2 { color: #333333; font-size: 16px; }
table { width: 100%; border-collapse: collapse; background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 6px rgba(0,0,0,0.1); font-size: 13px; }
th, td { padding: 8px 12px; border-bottom: 1px solid #dddddd; text-align: left; }
th { background-color: #eeeeee; }
tr:last-child td { border-bottom: none; }
.error { color: #b00020; font-weight: bold; background-color: #ffe5e5; }
.added { color: #006600; font-weight: bold; background-color: #e6ffe6; }
.removed { color: #cc0000; font-weight: bold; background-color: #ffe5e5; }
.nested { color: #e68a00; font-weight: bold; background-color: #fff5e6; }
</style>
</head>
<body>
<h2>Gruppenverwaltung: ${GroupSuffix} ($($ReportData.Count) Änderungen)</h2>
<table>
<thead>
<tr>
<th>Server</th>
<th>Aktion</th>
<th>Objekt</th>
</tr>
</thead>
<tbody>
$($styledRows -join "`n")
</tbody>
</table>
</body>
</html>
"@
$ReportData | Export-Csv -Path $tempFile -NoTypeInformation -Encoding UTF8 -Delimiter ";"
function Send-UTF8Mail {
param (
[string]$From,
[string]$To,
[string]$Subject,
[string]$Body,
[string]$SmtpServer,
[int]$Port = 25,
[pscredential]$Credential = $null,
[string]$AttachmentPath
)
try {
$smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port)
if ($Credential) {
$smtpClient.Credentials = $Credential
$smtpClient.EnableSsl = $true
}
$mailMessage = New-Object System.Net.Mail.MailMessage
$mailMessage.From = $From
$mailMessage.To.Add($To)
$mailMessage.Subject = $Subject
$mailMessage.Body = $Body
$mailMessage.IsBodyHtml = $true
$mailMessage.BodyEncoding = [System.Text.Encoding]::UTF8
$mailMessage.SubjectEncoding = [System.Text.Encoding]::UTF8
if ($AttachmentPath -and (Test-Path $AttachmentPath)) {
$attachment = New-Object System.Net.Mail.Attachment($AttachmentPath)
$mailMessage.Attachments.Add($attachment)
}
$smtpClient.Send($mailMessage)
Write-Log -Message "E-Mail erfolgreich gesendet an ${To}" -Level Success
} catch {
Write-Log -Message "Fehler beim Senden der E-Mail: $($_.Exception.Message)" -Level Error
} finally {
if ($mailMessage) {
if ($mailMessage.Attachments.Count -gt 0) {
$mailMessage.Attachments.Dispose()
}
$mailMessage.Dispose()
}
}
}
Write-Log -Message "Sende HTML-Bericht mit Anhang an ${SMTPTo} über ${SMTPServer}: Port ${SMTPPort}" -Level Info
try {
if ($SMTPPort -eq 587) {
Send-UTF8Mail `
-From $SMTPFrom `
-To $SMTPTo `
-Subject $SMTPSubject `
-Body $htmlBody `
-SmtpServer $SMTPServer `
-Port $SMTPPort `
-Credential $Credential `
-AttachmentPath $tempFile
} else {
Send-UTF8Mail `
-From $SMTPFrom `
-To $SMTPTo `
-Subject $SMTPSubject `
-Body $htmlBody `
-SmtpServer $SMTPServer `
-Port $SMTPPort `
-AttachmentPath $tempFile
}
} finally {
if (Test-Path $tempFile) {
Remove-Item $tempFile -Force
}
}
} else {
Write-Log -Message "Keine Änderungen festgestellt oder E-Mail-Versand deaktiviert (EnableMailNotification=${EnableMailNotification}, ReportData.Count=$($ReportData.Count))." -Level Info
}
if ($ReportData.Count -eq 0) {
Write-Log -Message "Skript erfolgreich abgeschlossen, keine Änderungen erforderlich." -Level Info
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment