Files
SMTPGraphRelay/Setup-SMTPGraphRelay.ps1
T

222 lines
7.1 KiB
PowerShell

#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
SMTPGraphRelay Installer / Repair / Update
.DESCRIPTION
Einheitliches Verwaltungswerkzeug für SMTPGraphRelay.
Modi:
1 - Neuinstallation
2 - Installation reparieren
3 - Relay aktualisieren
4 - Entra App & Berechtigungen prüfen
5 - Zertifikat erneuern
6 - Health Check ausführen
7 - Deinstallieren
WICHTIG:
Dieses Skript muss mit Windows PowerShell 5.1 ausgeführt werden.
#>
[CmdletBinding()]
param(
[string]$InstallPath = "$env:ProgramFiles\SMTPGraphRelay"
)
$ErrorActionPreference = "Stop"
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$TaskName = "SMTPGraphRelay"
$AppDefaultName = "SMTPGraphRelay"
$RelayFileName = "SMTPGraphRelay.ps1"
$HealthFileName = "Test-SMTPGraphRelay.ps1"
$RenewFileName = "Renew-SMTPGraphRelayCertificate.ps1"
$ConfigFileName = "config.json"
# Zentrales Gitea-Repository / Updatequelle
$RepoBaseUrl = "https://me-gitea.maieredv.cloud/MAIEREDV/SMTPGraphRelay"
$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.
# config.json, Queue, Logs und sonstige lokale Daten sind absichtlich NICHT enthalten.
$ManagedReleaseFiles = @(
"SMTPGraphRelay.ps1",
"Test-SMTPGraphRelay.ps1",
"Test-SMTPGraphRelay-FailureModes.ps1",
"Renew-SMTPGraphRelayCertificate.ps1",
"Setup-SMTPGraphRelay.ps1",
"version.json",
"README.md"
)
function Write-Title {
param([string]$Text)
Write-Host ""
Write-Host "==========================================================" -ForegroundColor Cyan
Write-Host " $Text" -ForegroundColor Cyan
Write-Host "==========================================================" -ForegroundColor Cyan
Write-Host ""
}
function Write-Ok { param([string]$Text) Write-Host "[OK] $Text" -ForegroundColor Green }
function Write-Warn { param([string]$Text) Write-Host "[WARN] $Text" -ForegroundColor Yellow }
function Write-Fail { param([string]$Text) Write-Host "[FAIL] $Text" -ForegroundColor Red }
function Write-Info { param([string]$Text) Write-Host "[INFO] $Text" -ForegroundColor Cyan }
function Read-Default {
param([string]$Prompt, [string]$Default)$value = Read-Host "$Prompt [Standard:$Default]"
if ([string]::IsNullOrWhiteSpace($value)) { return$Default }
return $value
}
function Confirm-Yes {
param([string]$Prompt)
$answer = Read-Host "$Prompt [j/N]"
return ($answer -match '^(?i)j\vert{}ja\vert{}y\vert{}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-Host ""
Write-Host "Aktuell erkannt:"
Write-Host " Edition: $($PSVersionTable.PSEdition)"
Write-Host " Version: $($PSVersionTable.PSVersion)"
Write-Host ""
Write-Host "Bitte starten:"
Write-Host " C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ForegroundColor Yellow
exit 1
}
Write-Ok "Windows PowerShell $($PSVersionTable.PSVersion) erkannt."
}
function Ensure-PackageProvider {
try {
if (-not (Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) {
Write-Info "NuGet Package Provider wird installiert..."
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null
}
} catch {
Write-Warn "NuGet Provider konnte nicht automatisch vorbereitet werden: $($_.Exception.Message)"
}
}
function Ensure-Modules {
Ensure-PackageProvider
$modules = @(
"Microsoft.Graph.Authentication",
"Microsoft.Graph.Applications"
)
foreach ($module in$modules) {
$existing = Get-Module -ListAvailable -Name$module |
Sort-Object Version -Descending |
Select-Object -First 1
if (-not $existing) {
Write-Info "$module fehlt. Installation für AllUsers..."
Install-Module $module -Scope AllUsers -Repository PSGallery -Force -AllowClobber
$existing = Get-Module -ListAvailable -Name$module |
Sort-Object Version -Descending |
Select-Object -First 1
}
if (-not $existing) {
throw "Modul '$module' konnte nicht 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."
}
}
function New-RepoStagingArea {
Enable-Tls12
$root = Join-Path$env:TEMP ("SMTPGraphRelay-repo-{0}" -f [guid]::NewGuid().ToString("N"))
$archive = Join-Path$root "main.zip"
$extract = Join-Path$root "extract"
New-Item -ItemType Directory -Path $extract -Force | Out-Null
Write-Info "Lade aktuellen Stand aus Gitea..."
Write-Info "Quelle: $RemoteArchiveUrl"
Invoke-WebRequest `
-Uri $RemoteArchiveUrl `
-OutFile $archive `
-UseBasicParsing `
-TimeoutSec 120 `
-ErrorAction Stop
if (-not (Test-Path -LiteralPath $archive)) {
throw "Gitea-Archiv wurde nicht heruntergeladen."
}
Expand-Archive `
-LiteralPath $archive `
-DestinationPath $extract `
-Force
$releaseRoot = Find-ExtractedReleaseRoot -ExtractPath$extract
# Pflichtdateien prüfen.
foreach ($required in @("SMTPGraphRelay.ps1", "version.json")) {
if (-not (Test-Path -LiteralPath (Join-Path $releaseRoot$required))) {
throw "Repository-Archiv ist unvollständig: '$required' fehlt."
}
}
$versionInfo = Get-Content `
-LiteralPath (Join-Path $releaseRoot "version.json") `
-Raw `
-Encoding UTF8 | ConvertFrom-Json
if (-not $versionInfo.Version) {
throw "version.json aus dem Repository enthält keine Version."
}
Write-Ok "Repository-Version $($versionInfo.Version) geladen."
return [pscustomobject]@{
TempRoot = $root
ReleaseRoot = $releaseRoot
VersionInfo = $versionInfo
}
}
function Remove-RepoStagingArea {
param($Staging)
if ($Staging -and $Staging.TempRoot) {
Remove-Item -LiteralPath $Staging.TempRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}
function Install-RepoProgramFiles {
param(
[Parameter(Mandatory)][string]$ReleaseRoot,
[Parameter(Mandatory)][string]$TargetPath
)
New-Item -ItemType Directory -Path $TargetPath -Force | Out-Null
foreach ($name in $ManagedReleaseFiles) {
$source = Join-Path $ReleaseRoot $name
if (Test-Path -LiteralPath $source) {
Copy-Item `