From 4ae9d903907689a316cda0731525e7844f78bf9c Mon Sep 17 00:00:00 2001 From: "manuel.maier" Date: Tue, 13 Oct 2026 12:06:38 +0200 Subject: [PATCH] Setup-SMTPGraphRelay.ps1 aktualisiert --- Setup-SMTPGraphRelay.ps1 | 187 +++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 94 deletions(-) diff --git a/Setup-SMTPGraphRelay.ps1 b/Setup-SMTPGraphRelay.ps1 index 9feb04e..05288bc 100644 --- a/Setup-SMTPGraphRelay.ps1 +++ b/Setup-SMTPGraphRelay.ps1 @@ -5,19 +5,19 @@ SMTPGraphRelay Installer / Repair / Update .DESCRIPTION - Einheitliches Verwaltungswerkzeug für SMTPGraphRelay. + Einheitliches Verwaltungswerkzeug fuer SMTPGraphRelay. Modi: 1 - Neuinstallation 2 - Installation reparieren 3 - Relay aktualisieren - 4 - Entra App & Berechtigungen prüfen + 4 - Entra App & Berechtigungen pruefen 5 - Zertifikat erneuern - 6 - Health Check ausführen + 6 - Health Check ausfuehren 7 - Deinstallieren WICHTIG: - Dieses Skript muss mit Windows PowerShell 5.1 ausgeführt werden. + Dieses Skript muss mit Windows PowerShell 5.1 ausgefuehrt werden. #> [CmdletBinding()] @@ -41,7 +41,7 @@ $RemoteVersionUrl = "$RepoBaseUrl/raw/branch/main/version.json" $RemoteArchiveUrl = "$RepoBaseUrl/archive/main.zip" $RemoteRelayUrl = "$RepoBaseUrl/raw/branch/main/SMTPGraphRelay.ps1" -# Dateien, die ein Update verändern darf. +# Dateien, die ein Update veraendern darf. # config.json, Queue, Logs und sonstige lokale Daten sind absichtlich NICHT enthalten. $ManagedReleaseFiles = @( "SMTPGraphRelay.ps1", @@ -83,7 +83,7 @@ function Confirm-Yes { function Assert-WindowsPowerShell51 { if ($PSVersionTable.PSEdition -ne "Desktop" -or $PSVersionTable.PSVersion.Major -ne 5) { Write-Title "FALSCHE POWERSHELL-VERSION" - Write-Fail "Dieses Tool muss mit Windows PowerShell 5.1 ausgeführt werden." + Write-Fail "Dieses Tool muss mit Windows PowerShell 5.1 ausgefuehrt werden." Write-Host "" Write-Host "Aktuell erkannt:" Write-Host " Edition: $($PSVersionTable.PSEdition)" @@ -116,31 +116,30 @@ function Ensure-Modules { "Microsoft.Graph.Applications" ) + $allUsersPath = Join-Path $env:ProgramFiles "WindowsPowerShell\Modules" + foreach ($module in $modules) { + # Pruefe explizit, ob das Modul im AllUsers-Pfad liegt $existing = Get-Module -ListAvailable -Name $module | + Where-Object { $_.ModuleBase -like "$allUsersPath*" } | Sort-Object Version -Descending | Select-Object -First 1 if (-not $existing) { - Write-Info "$module fehlt. Installation für AllUsers..." + Write-Info "$module fehlt (System-weit). Installation fuer AllUsers..." Install-Module $module -Scope AllUsers -Repository PSGallery -Force -AllowClobber + $existing = Get-Module -ListAvailable -Name $module | + Where-Object { $_.ModuleBase -like "$allUsersPath*" } | Sort-Object Version -Descending | Select-Object -First 1 } if (-not $existing) { - throw "Modul '$module' konnte nicht installiert/gefunden werden." + throw "Modul '$module' konnte nicht fuer AllUsers installiert/gefunden werden." } - # Schutz gegen den bereits beobachteten PowerShell-7-Pfad. - if ($existing.ModuleBase -notmatch '\\WindowsPowerShell\\Modules\\') { - Write-Warn "$module wurde gefunden, aber nicht im Windows-PowerShell-Modulpfad: $($existing.ModuleBase)" - Write-Info "Installiere das Modul nochmals explizit aus Windows PowerShell 5.1..." - Install-Module $module -Scope AllUsers -Repository PSGallery -Force -AllowClobber - } - - Write-Ok "$module verfügbar." + Write-Ok "$module verfuegbar (System-weit)." } } @@ -175,10 +174,10 @@ function New-RepoStagingArea { $releaseRoot = Find-ExtractedReleaseRoot -ExtractPath $extract - # Pflichtdateien prüfen. + # Pflichtdateien pruefen. foreach ($required in @("SMTPGraphRelay.ps1", "version.json")) { if (-not (Test-Path -LiteralPath (Join-Path $releaseRoot $required))) { - throw "Repository-Archiv ist unvollständig: '$required' fehlt." + throw "Repository-Archiv ist unvollstaendig: '$required' fehlt." } } @@ -188,7 +187,7 @@ function New-RepoStagingArea { -Encoding UTF8 | ConvertFrom-Json if (-not $versionInfo.Version) { - throw "version.json aus dem Repository enthält keine Version." + throw "version.json aus dem Repository enthaelt keine Version." } Write-Ok "Repository-Version $($versionInfo.Version) geladen." @@ -562,7 +561,7 @@ function Get-RemoteVersion { $remote = Get-Content -LiteralPath $tempFile -Raw -Encoding UTF8 | ConvertFrom-Json if (-not $remote.Version) { - throw "Remote version.json enthält keine Version." + throw "Remote version.json enthaelt keine Version." } return $remote @@ -596,7 +595,7 @@ function Find-ExtractedReleaseRoot { [Parameter(Mandatory)][string]$ExtractPath ) - # Gitea kann beim Archiv einen zusätzlichen Root-Ordner erzeugen. + # Gitea kann beim Archiv einen zusaetzlichen Root-Ordner erzeugen. # Daher suchen wir nach der Kombination aus Relay + version.json statt # einen konkreten Archivordnernamen vorauszusetzen. $relayFiles = Get-ChildItem ` @@ -613,7 +612,7 @@ function Find-ExtractedReleaseRoot { } } - throw "Im heruntergeladenen Archiv wurde kein gültiges SMTPGraphRelay-Release gefunden." + throw "Im heruntergeladenen Archiv wurde kein gueltiges SMTPGraphRelay-Release gefunden." } function Backup-ManagedFiles { @@ -666,7 +665,7 @@ function Install-ExtractedRelease { foreach ($name in $required) { if (-not (Test-Path -LiteralPath (Join-Path $ReleaseRoot $name))) { - throw "Updatepaket ist unvollständig: '$name' fehlt." + throw "Updatepaket ist unvollstaendig: '$name' fehlt." } } @@ -688,7 +687,7 @@ function Invoke-PostUpdateHealthCheck { $health = Join-Path $TargetPath $HealthFileName if (-not (Test-Path -LiteralPath $health)) { - Write-Warn "Health Check ist nicht installiert; automatische Nachprüfung entfällt." + Write-Warn "Health Check ist nicht installiert; automatische Nachpruefung entfaellt." return 0 } @@ -703,7 +702,7 @@ function Install-New { if (Test-Path -LiteralPath (Join-Path $InstallPath $ConfigFileName)) { Write-Warn "Es existiert bereits eine config.json unter:" Write-Host " $InstallPath" - Write-Warn "Neuinstallation würde eine neue App/Zertifikat erzeugen." + Write-Warn "Neuinstallation wuerde eine neue App/Zertifikat erzeugen." if (-not (Confirm-Yes "Trotzdem fortfahren?")) { return @@ -718,7 +717,7 @@ function Install-New { Ensure-Modules Ensure-Directories -TargetPath $InstallPath - # Nur Programmdateien aus Git übernehmen. + # Nur Programmdateien aus Git uebernehmen. Install-RepoProgramFiles ` -ReleaseRoot $staging.ReleaseRoot ` -TargetPath $InstallPath @@ -729,7 +728,7 @@ function Install-New { $senderMailbox = Read-Host "Standard-Absenderpostfach (Fallback/Test, z.B. info@firma.de)" while ([string]::IsNullOrWhiteSpace($senderMailbox) -or $senderMailbox -notmatch '^[^@\s]+@[^@\s]+\.[^@\s]+$') { - $senderMailbox = Read-Host "Bitte eine gültige Mailadresse eingeben" + $senderMailbox = Read-Host "Bitte eine gueltige Mailadresse eingeben" } $listenAddress = Read-Default "Lokale Listen-IP" "0.0.0.0" @@ -791,7 +790,7 @@ function Install-New { -PrincipalId $sp.Id ` -ResourceId $graphSp.Id ` -AppRoleId $mailSendRole.Id | Out-Null - Write-Ok "Globale 'Mail.Send' Application Role (ohne Einschränkung) erfolgreich vergeben." + Write-Ok "Globale 'Mail.Send' Application Role (ohne Einschraenkung) erfolgreich vergeben." } else { Write-Ok "Globale 'Mail.Send' Berechtigung war bereits vergeben." } @@ -861,7 +860,7 @@ function Install-New { $ctx = Get-MgContext if (-not $ctx -or $ctx.AuthType -ne "AppOnly") { - throw "App-only Anmeldung konnte nicht bestätigt werden." + throw "App-only Anmeldung konnte nicht bestaetigt werden." } Write-Ok "App-only Anmeldung funktioniert." @@ -871,12 +870,12 @@ function Install-New { $health = Join-Path $InstallPath $HealthFileName if (Test-Path -LiteralPath $health) { - Write-Info "Starte abschließenden Health Check..." + Write-Info "Starte abschliessenden Health Check..." & $health -ConfigPath (Join-Path $InstallPath $ConfigFileName) $healthExit = $LASTEXITCODE if ($healthExit -ge 2) { - Write-Warn "Installation abgeschlossen, Health Check meldet Fehler. Bitte Ausgabe prüfen." + Write-Warn "Installation abgeschlossen, Health Check meldet Fehler. Bitte Ausgabe pruefen." } } @@ -903,7 +902,7 @@ function Repair-Installation { $config = Get-RelayConfig -TargetPath $InstallPath if (-not $config) { - Write-Fail "Keine config.json gefunden. Repair ist nur für bestehende Installationen gedacht." + Write-Fail "Keine config.json gefunden. Repair ist nur fuer bestehende Installationen gedacht." Write-Info "Bitte Neuinstallation verwenden." return } @@ -925,7 +924,7 @@ function Repair-Installation { Write-Warn "Repair erzeugt absichtlich kein neues Credential. Nutze Zertifikat erneuern." } elseif (-not $cert.HasPrivateKey) { - Write-Fail "Konfiguriertes Zertifikat besitzt keinen privaten Schlüssel." + Write-Fail "Konfiguriertes Zertifikat besitzt keinen privaten Schluessel." } else { Write-Ok "Zertifikat vorhanden und besitzt Private Key." @@ -952,7 +951,7 @@ function Repair-Installation { } if ($healthExit -eq 1) { - Write-Warn "Repair abgeschlossen, Health Check enthält Warnungen." + Write-Warn "Repair abgeschlossen, Health Check enthaelt Warnungen." } else { Write-Ok "Repair Health Check erfolgreich." @@ -990,7 +989,7 @@ function Update-Relay { $config = Get-RelayConfig -TargetPath $InstallPath if (-not $config) { Write-Fail "Keine bestehende config.json gefunden." - Write-Info "Für eine neue Installation bitte 'Neuinstallation' wählen." + Write-Info "Fuer eine neue Installation bitte 'Neuinstallation' waehlen." return } @@ -1005,12 +1004,12 @@ function Update-Relay { Write-Warn "Keine installierte version.json gefunden." $installedVersion = Read-Host "Installierte Version manuell eingeben (z.B. 1.5.0)" if ([string]::IsNullOrWhiteSpace($installedVersion)) { - Write-Fail "Ohne lokale Versionsinformation kann kein sicheres Online-Update durchgeführt werden." + Write-Fail "Ohne lokale Versionsinformation kann kein sicheres Online-Update durchgefuehrt werden." return } } - Write-Info "Prüfe Gitea auf neue Version..." + Write-Info "Pruefe Gitea auf neue Version..." Write-Info "Repository: $RepoBaseUrl" try { @@ -1039,13 +1038,13 @@ function Update-Relay { if ($comparison -lt 0) { Write-Warn "Die installierte Version ($installedVersion) ist neuer als main ($remoteVersion)." - if (-not (Confirm-Yes "Downgrade auf $remoteVersion durchführen?")) { + if (-not (Confirm-Yes "Downgrade auf $remoteVersion durchfuehren?")) { return } } else { Write-Host "" - Write-Host "Update verfügbar:" -ForegroundColor Green + Write-Host "Update verfuegbar:" -ForegroundColor Green Write-Host " Installiert: $installedVersion" Write-Host " Neu: $remoteVersion" -ForegroundColor Yellow Write-Host "" @@ -1096,11 +1095,11 @@ function Update-Relay { -Encoding UTF8 | ConvertFrom-Json if (-not $downloadedVersionInfo.Version) { - throw "version.json im Archiv enthält keine Version." + throw "version.json im Archiv enthaelt keine Version." } if ([string]$downloadedVersionInfo.Version -ne $remoteVersion) { - throw "Versionskonflikt: version.json-URL meldet $remoteVersion, Archiv enthält $($downloadedVersionInfo.Version)." + throw "Versionskonflikt: version.json-URL meldet $remoteVersion, Archiv enthaelt $($downloadedVersionInfo.Version)." } $task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue @@ -1132,7 +1131,7 @@ function Update-Relay { } if ($healthExit -eq 1) { - Write-Warn "Update erfolgreich, Health Check enthält Warnungen." + Write-Warn "Update erfolgreich, Health Check enthaelt Warnungen." } else { Write-Ok "Health Check nach Update erfolgreich." @@ -1148,7 +1147,7 @@ function Update-Relay { Write-Fail "Update fehlgeschlagen: $($_.Exception.Message)" if ($backupPath -and (Test-Path -LiteralPath $backupPath)) { - Write-Warn "Automatischer Rollback wird durchgeführt..." + Write-Warn "Automatischer Rollback wird durchgefuehrt..." try { Stop-RelayTask @@ -1173,7 +1172,7 @@ function Update-Relay { } function Verify-CloudPermissions { - Write-Title "SMTPGraphRelay - Entra Berechtigungen prüfen" + Write-Title "SMTPGraphRelay - Entra Berechtigungen pruefen" $config = Get-RelayConfig -TargetPath $InstallPath if (-not $config) { @@ -1215,7 +1214,7 @@ function Verify-CloudPermissions { if ($globalAssig) { Write-Ok "Erfolg: App besitzt die erforderliche GLOBALE (tenant-weite) Microsoft Graph Mail.Send Berechtigung in Entra ID." } else { - Write-Fail "Fehler: Der App fehlt die GLOBALE Microsoft Graph Mail.Send Berechtigung. Bitte in Entra ID hinzufügen und zustimmen." + Write-Fail "Fehler: Der App fehlt die GLOBALE Microsoft Graph Mail.Send Berechtigung. Bitte in Entra ID hinzufuegen und zustimmen." } } finally { @@ -1236,7 +1235,7 @@ function Invoke-CertificateRenewal { } function Invoke-HealthCheck { - Write-Title "SMTPGraphRelay - Health Check" + Write-Title "SMTPGraphRelay - Health Check ausfuehren" $health = Join-Path $InstallPath $HealthFileName if (-not (Test-Path -LiteralPath $health)) { @@ -1273,7 +1272,7 @@ function Invoke-Pbkdf2Sha256 { throw "Iterations must be greater than zero." } - # Auf unterstützten .NET-Framework-Versionen verwenden wir die native, + # Auf unterstuetzten .NET-Framework-Versionen verwenden wir die native, # schnelle PBKDF2-SHA256-Implementierung. try { $derive = New-Object System.Security.Cryptography.Rfc2898DeriveBytes( @@ -1291,7 +1290,7 @@ function Invoke-Pbkdf2Sha256 { } } catch { - # Kompatibilitäts-Fallback für ältere .NET-Framework-Stände. + # Kompatibilitaets-Fallback fuer aeltere .NET-Framework-Staende. $hmac = New-Object System.Security.Cryptography.HMACSHA256 $hmac.Key = [Text.Encoding]::UTF8.GetBytes($Password) @@ -1487,7 +1486,7 @@ function Retry-FailedQueueEntry { $targetMeta = "$targetEml.json" if (Test-Path -LiteralPath $targetEml) { - throw "Pending enthält bereits '$($Entry.FileName)'." + throw "Pending enthaelt bereits '$($Entry.FileName)'." } if ($Entry.Meta) { @@ -1551,9 +1550,9 @@ function Manage-FailedQueue { Write-Host " [2] Details einer Mail anzeigen" Write-Host " [3] Eine Mail erneut zustellen" Write-Host " [4] Alle Mails erneut zustellen" - Write-Host " [5] Eine Mail endgültig löschen" - Write-Host " [6] Alle Failed-Mails endgültig löschen" - Write-Host " [0] Zurück" + Write-Host " [5] Eine Mail endgueltig loeschen" + Write-Host " [6] Alle Failed-Mails endgueltig loeschen" + Write-Host " [0] Zurueck" Write-Host "" $choice = Read-Host "Auswahl" @@ -1592,7 +1591,7 @@ function Manage-FailedQueue { Write-Host "An: $($entry.Recipients)" Write-Host "Auth-User: $($entry.AuthenticatedUser)" Write-Host "Status: $($entry.LastStatusCode)" - Write-Host "Größe: $($entry.SizeKB) KB" + Write-Host "Groesse: $($entry.SizeKB) KB" Write-Host "" Write-Host "Letzter Fehler:" -ForegroundColor Yellow Write-Host $entry.LastError @@ -1613,7 +1612,7 @@ function Manage-FailedQueue { } else { Retry-FailedQueueEntry -Config $config -Entry $entry - Write-Ok "[$($entry.QueueId)] zurück nach pending verschoben." + Write-Ok "[$($entry.QueueId)] zurueck nach pending verschoben." } Read-Host "Enter" @@ -1638,7 +1637,7 @@ function Manage-FailedQueue { } } - Write-Ok "$ok Mail(s) zurück nach pending verschoben." + Write-Ok "$ok Mail(s) zurueck nach pending verschoben." if ($failed -gt 0) { Write-Warn "$failed Mail(s) konnten nicht verschoben werden." } @@ -1657,9 +1656,9 @@ function Manage-FailedQueue { if (-not $entry) { Write-Fail "Queue-ID '$id' nicht gefunden." } - elseif (Confirm-Yes "[$($entry.QueueId)] endgültig aus Failed löschen?") { + elseif (Confirm-Yes "[$($entry.QueueId)] endgueltig aus Failed loeschen?") { Remove-FailedQueueEntry -Entry $entry - Write-Ok "[$($entry.QueueId)] gelöscht." + Write-Ok "[$($entry.QueueId)] geloescht." } Read-Host "Enter" @@ -1669,8 +1668,8 @@ function Manage-FailedQueue { if ($entries.Count -eq 0) { Write-Ok "Failed Queue ist leer." } - elseif (Confirm-Yes "WIRKLICH alle $($entries.Count) Failed-Mails endgültig löschen?") { - if (Confirm-Yes "Endgültiges Löschen nochmals bestätigen?") { + elseif (Confirm-Yes "WIRKLICH alle $($entries.Count) Failed-Mails endgueltig loeschen?") { + if (Confirm-Yes "Endgueltiges Loeschen nochmals bestaetigen?") { $ok = 0 foreach ($entry in $entries) { @@ -1679,11 +1678,11 @@ function Manage-FailedQueue { $ok++ } catch { - Write-Warn "[$($entry.QueueId)] konnte nicht gelöscht werden." + Write-Warn "[$($entry.QueueId)] konnte nicht geloescht werden." } } - Write-Ok "$ok Failed-Mail(s) endgültig gelöscht." + Write-Ok "$ok Failed-Mail(s) endgueltig geloescht." } } @@ -1695,7 +1694,7 @@ function Manage-FailedQueue { } default { - Write-Warn "Ungültige Auswahl." + Write-Warn "Ungueltige Auswahl." Start-Sleep -Seconds 1 } } @@ -1727,13 +1726,13 @@ function Manage-SmtpAuth { Write-Host "Ohne Auth erlaubte Netze: $(@($config.Smtp.AllowUnauthenticatedNetworks) -join ', ')" Write-Host "" Write-Host " [1] SMTP-AUTH erforderlich EIN/AUS" - Write-Host " [2] Benutzer hinzufügen" + Write-Host " [2] Benutzer hinzufuegen" Write-Host " [3] Benutzer anzeigen" - Write-Host " [4] Passwort ändern" - Write-Host " [5] Benutzer löschen" + Write-Host " [4] Passwort aendern" + Write-Host " [5] Benutzer loeschen" Write-Host " [6] Netze ohne Auth verwalten" - Write-Host " [7] Max. Fehlversuche ändern" - Write-Host " [0] Zurück" + Write-Host " [7] Max. Fehlversuche aendern" + Write-Host " [0] Zurueck" Write-Host "" $choice = Read-Host "Auswahl" @@ -1747,7 +1746,7 @@ function Manage-SmtpAuth { } Save-SmtpAuthConfigAndRestart -Config $config - Write-Ok "SMTP-AUTH Status geändert." + Write-Ok "SMTP-AUTH Status geaendert." Read-Host "Enter" } @@ -1755,7 +1754,7 @@ function Manage-SmtpAuth { $username = Read-Host "Benutzername" if ([string]::IsNullOrWhiteSpace($username) -or $username -notmatch '^[A-Za-z0-9._@-]{1,128}$') { - Write-Fail "Ungültiger Benutzername." + Write-Fail "Ungueltiger Benutzername." Read-Host "Enter" continue } @@ -1773,8 +1772,8 @@ function Manage-SmtpAuth { $p1 = Read-Host "Passwort" -AsSecureString $p2 = Read-Host "Passwort wiederholen" -AsSecureString - $plain1 = ConvertTo-PlainText -SecureString $p1 - $plain2 = ConvertTo-PlainText -SecureString $p2 + $plain1 = ConvertTo-PlainText -SecureString$p1 + $plain2 = ConvertTo-PlainText -SecureString$p2 try { if ($plain1.Length -lt 8) { @@ -1784,7 +1783,7 @@ function Manage-SmtpAuth { } if ($plain1 -cne$plain2) { - Write-Fail "Passwörter stimmen nicht überein." + Write-Fail "Passwoerter stimmen nicht ueberein." Read-Host "Enter" continue } @@ -1852,7 +1851,7 @@ function Manage-SmtpAuth { } if ($plain1 -cne$plain2) { - Write-Fail "Passwörter stimmen nicht überein." + Write-Fail "Passwoerter stimmen nicht ueberein." Read-Host "Enter" continue } @@ -1868,7 +1867,7 @@ function Manage-SmtpAuth { $user.Iterations =$record.Iterations Save-SmtpAuthConfigAndRestart -Config $config - Write-Ok "Passwort für '$username' geändert." + Write-Ok "Passwort fuer '$username' geaendert." Read-Host "Enter" } @@ -1884,13 +1883,13 @@ function Manage-SmtpAuth { continue } - if (Confirm-Yes "Benutzer '$username' wirklich löschen?") { + if (Confirm-Yes "Benutzer '$username' wirklich loeschen?") { $config.Smtp.AuthUsers = @($config.Smtp.AuthUsers | Where-Object { -not ([string]$_.Username).Equals($username, [StringComparison]::OrdinalIgnoreCase) } ) Save-SmtpAuthConfigAndRestart -Config $config - Write-Ok "Benutzer '$username' gelöscht." + Write-Ok "Benutzer '$username' geloescht." } Read-Host "Enter" @@ -1898,7 +1897,7 @@ function Manage-SmtpAuth { "6" { $current = @($config.Smtp.AllowUnauthenticatedNetworks) -join "," - $input = Read-Default "Netze/IPs ohne AUTH, Komma getrennt; '-' für keine" $(if ($current) {$current } else { "-" }) + $input = Read-Default "Netze/IPs ohne AUTH, Komma getrennt; '-' fuer keine" $(if ($current) {$current } else { "-" }) if ($input -eq "-") { $config.Smtp.AllowUnauthenticatedNetworks = @() @@ -1911,7 +1910,7 @@ function Manage-SmtpAuth { } Save-SmtpAuthConfigAndRestart -Config $config - Write-Ok "Ausnahmen für SMTP-AUTH gespeichert." + Write-Ok "Ausnahmen fuer SMTP-AUTH gespeichert." Read-Host "Enter" } @@ -1921,7 +1920,7 @@ function Manage-SmtpAuth { if ($value -match '^\d+$' -and [int]$value -ge 1 -and [int]$value -le 100) { $config.Smtp.AuthMaxFailures = [int]$value Save-SmtpAuthConfigAndRestart -Config $config - Write-Ok "Maximale Fehlversuche geändert." + Write-Ok "Maximale Fehlversuche geaendert." } else { Write-Fail "Bitte einen Wert zwischen 1 und 100 eingeben." @@ -1935,14 +1934,14 @@ function Manage-SmtpAuth { } default { - Write-Warn "Ungültige Auswahl." + Write-Warn "Ungueltige Auswahl." Start-Sleep -Seconds 1 } } - # Config nach jeder Änderung neu laden, damit Serialisierung/Arrays exakt + # Config nach jeder Aenderung neu laden, damit Serialisierung/Arrays exakt # dem installierten Zustand entsprechen. - $config = Get-RelayConfig -TargetPath $InstallPath + $config = Get-RelayConfig -TargetPath$InstallPath $config = Ensure-SmtpAuthConfig -Config$config } } @@ -1963,7 +1962,7 @@ function Invoke-FailureModeTests { function Uninstall-Relay { Write-Title "SMTPGraphRelay - Deinstallation" - $config = Get-RelayConfig -TargetPath $InstallPath + $config = Get-RelayConfig -TargetPath$InstallPath Write-Warn "Lokale Deinstallation entfernt Task, Firewallregel und auf Wunsch das lokale Zertifikat." if (-not (Confirm-Yes "Lokale SMTPGraphRelay-Installation wirklich entfernen?")) { @@ -1992,7 +1991,7 @@ function Uninstall-Relay { Write-Warn "Cloud-Cleanup ist destruktiv und betrifft die konfigurierte ClientId:" Write-Host " $($config.Graph.ClientId)" -ForegroundColor Yellow - if (Confirm-Yes "Cloud-Cleanup endgültig bestätigen?") { + if (Confirm-Yes "Cloud-Cleanup endgueltig bestaetigen?") { Connect-RelayGraphAdmin -TenantId $config.Graph.TenantId try { @@ -2015,8 +2014,8 @@ function Uninstall-Relay { } } - # Programmdateien. Wenn der Installer selbst aus dem Zielordner läuft, kann er - # sich nicht zuverlässig selbst löschen. Dann bleiben Setup + Ordner bis nach Ende stehen. + # Programmdateien. Wenn der Installer selbst aus dem Zielordner laeuft, kann er + # sich nicht zuverlaessig selbst loeschen. Dann bleiben Setup + Ordner bis nach Ende stehen. $currentInstaller = [IO.Path]::GetFullPath($PSCommandPath) $targetFull = [IO.Path]::GetFullPath($InstallPath) @@ -2035,14 +2034,14 @@ function Uninstall-Relay { Write-Ok "Lokale Programmdateien entfernt." if ($currentInstaller.StartsWith($targetFull, [StringComparison]::OrdinalIgnoreCase)) { - Write-Warn "Der aktuell laufende Installer bleibt übrig. Nach dem Beenden kann '$InstallPath' manuell gelöscht werden." + Write-Warn "Der aktuell laufende Installer bleibt uebrig. Nach dem Beenden kann '$InstallPath' manuell geloescht werden." } Write-Title "Deinstallation abgeschlossen" } function Show-Status { - Write-Title "SMTPGraphRelay - Status" + Write-Title "SMTPGraphRelay - Status anzeigen" $configPath = Join-Path $InstallPath$ConfigFileName Write-Host "Installationspfad: $InstallPath" @@ -2050,7 +2049,7 @@ function Show-Status { if (Test-Path -LiteralPath $configPath) { Write-Ok "config.json vorhanden." try { - $config = Get-RelayConfig -TargetPath $InstallPath + $config = Get-RelayConfig -TargetPath$InstallPath Write-Host " Sender: $($config.Graph.SenderMailbox) (ForceSender: $($config.Graph.ForceSender))" Write-Host " SMTP: $($config.Smtp.ListenAddress):$($config.Smtp.Port)" Write-Host " Client: $($config.Graph.ClientId)" @@ -2085,7 +2084,7 @@ function Show-Menu { Write-Title "SMTPGraphRelay - Bootstrap / Repair / Online Update" Write-Host "Installationspfad: $InstallPath" -ForegroundColor DarkGray - $installedVersion = Get-InstalledVersion -TargetPath $InstallPath + $installedVersion = Get-InstalledVersion -TargetPath$InstallPath if ($installedVersion -and$installedVersion.Version) { Write-Host "Installiert: $($installedVersion.Version)" -ForegroundColor DarkGray @@ -2096,9 +2095,9 @@ function Show-Menu { Write-Host " [1] Neuinstallation aus Gitea" Write-Host " [2] Installation aus Gitea reparieren" Write-Host " [3] Nach Online-Updates suchen" - Write-Host " [4] Entra App & Berechtigungen prüfen" + Write-Host " [4] Entra App & Berechtigungen pruefen" Write-Host " [5] Zertifikat erneuern" - Write-Host " [6] Health Check ausführen" + Write-Host " [6] Health Check ausfuehren" Write-Host " [7] Deinstallieren" Write-Host " [8] Status anzeigen" Write-Host " [9] SMTP-AUTH verwalten" @@ -2128,7 +2127,7 @@ while ($true) { "10" { Manage-FailedQueue } "11" { Invoke-FailureModeTests } "0" { break } - default { Write-Warn "Ungültige Auswahl." } + default { Write-Warn "Ungueltige Auswahl." } } } catch { @@ -2141,7 +2140,7 @@ while ($true) { if ($choice -ne "0") { Write-Host "" - Read-Host "Enter drücken, um zum Menü zurückzukehren" + Read-Host "Enter druecken, um zum Menue zurueckzukehren" } if ($choice -eq "0") {