222 lines
7.1 KiB
PowerShell
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 ` |