starface-outlook-sync-addin/installer/setup.ps1

500 lines
20 KiB
PowerShell

#Requires -RunAsAdministrator
<#
.SYNOPSIS
Setup-Script fuer Starface Outlook Sync Add-in.
.DESCRIPTION
- Fragt Starface-Host ab (zum Import des SSL-Zertifikats)
- Fragt lokalen Port ab fuer den Webserver
- Extrahiert das SSL-Zertifikat der Starface und importiert es als vertrauenswuerdig
- Erstellt eine lokale CA und ein localhost-Zertifikat
- Installiert den lokalen HTTPS-Webserver als Windows Scheduled Task
- Registriert das Outlook Add-in
Login-Daten werden NICHT benoetigt - diese werden spaeter im Add-in selbst
in den Sync-Profilen konfiguriert.
#>
param(
[string]$InstallDir = "$env:ProgramFiles\StarfaceOutlookSync"
)
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
# ============================================================
# Hilfsfunktionen
# ============================================================
function Write-Header($text) {
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " $text" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host ""
}
function Write-Step($text) {
Write-Host " [OK] $text" -ForegroundColor Green
}
function Write-Warn($text) {
Write-Host " [!] $text" -ForegroundColor Yellow
}
function Write-Err($text) {
Write-Host " [X] $text" -ForegroundColor Red
}
function Test-NodeJs {
try {
$version = & node --version 2>$null
if ($version) {
Write-Step "Node.js gefunden: $version"
return $true
}
} catch {}
return $false
}
function Test-PortAvailable($port) {
$listener = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue
return ($null -eq $listener)
}
function Import-StarfaceCert($host_, $port_) {
Write-Step "Verbinde mit ${host_}:${port_} ..."
$tcpClient = New-Object System.Net.Sockets.TcpClient
$tcpClient.Connect($host_, $port_)
$sslStream = New-Object System.Net.Security.SslStream(
$tcpClient.GetStream(),
$false,
{ $true } # Alle Zertifikate akzeptieren fuer den Abruf
)
$sslStream.AuthenticateAsClient($host_)
$remoteCert = $sslStream.RemoteCertificate
$x509Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($remoteCert)
$sslStream.Close()
$tcpClient.Close()
Write-Step "Zertifikat erhalten: $($x509Cert.Subject)"
Write-Step "Aussteller: $($x509Cert.Issuer)"
Write-Step "Gueltig bis: $($x509Cert.NotAfter)"
# In den Trusted Root Store importieren
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store(
[System.Security.Cryptography.X509Certificates.StoreName]::Root,
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
)
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$store.Add($x509Cert)
$store.Close()
Write-Step "Starface-Zertifikat als vertrauenswuerdig importiert."
return $x509Cert
}
# ============================================================
# Voraussetzungen pruefen
# ============================================================
Write-Header "Starface Outlook Sync - Setup"
# Node.js pruefen und ggf. installieren
if (-not (Test-NodeJs)) {
Write-Warn "Node.js ist nicht installiert."
Write-Host " Node.js wird fuer den lokalen Webserver benoetigt." -ForegroundColor Gray
Write-Host ""
$installNode = Read-Host "Node.js jetzt automatisch herunterladen und installieren? (j/n)"
if ($installNode -eq "j") {
Write-Step "Ermittle System-Architektur ..."
$arch = if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" }
$nodeVersion = "v22.14.0" # LTS
$msiUrl = "https://nodejs.org/dist/$nodeVersion/node-$nodeVersion-$arch.msi"
$msiPath = Join-Path $env:TEMP "node-$nodeVersion-$arch.msi"
Write-Step "Lade Node.js $nodeVersion ($arch) herunter ..."
Write-Host " $msiUrl" -ForegroundColor Gray
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # Beschleunigt den Download
Invoke-WebRequest -Uri $msiUrl -OutFile $msiPath -UseBasicParsing
if (-not (Test-Path $msiPath)) {
Write-Err "Download fehlgeschlagen."
exit 1
}
$fileSize = [math]::Round((Get-Item $msiPath).Length / 1MB, 1)
Write-Step "Download abgeschlossen ($fileSize MB)"
Write-Step "Installiere Node.js (bitte warten) ..."
$msiArgs = "/i `"$msiPath`" /qn /norestart"
$process = Start-Process -FilePath "msiexec.exe" -ArgumentList $msiArgs -Wait -PassThru
if ($process.ExitCode -ne 0) {
Write-Err "Installation fehlgeschlagen (Exit-Code: $($process.ExitCode))"
Write-Host " Bitte Node.js manuell installieren: https://nodejs.org/" -ForegroundColor Yellow
exit 1
}
# PATH aktualisieren (MSI fuegt Node.js zum System-PATH hinzu)
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
# Aufraeumen
Remove-Item $msiPath -Force -ErrorAction SilentlyContinue
if (Test-NodeJs) {
Write-Step "Node.js erfolgreich installiert!"
} else {
Write-Err "Node.js wurde installiert, ist aber nicht im PATH gefunden."
Write-Host " Bitte das Setup nach einem Neustart erneut ausfuehren." -ForegroundColor Yellow
exit 1
}
} catch {
Write-Err "Fehler beim Download/Installation: $_"
Write-Host " Bitte Node.js manuell installieren: https://nodejs.org/" -ForegroundColor Yellow
exit 1
}
} else {
Write-Host " Bitte Node.js manuell installieren: https://nodejs.org/" -ForegroundColor Yellow
Write-Host " (LTS-Version empfohlen)" -ForegroundColor Yellow
Read-Host "Eingabetaste zum Beenden"
exit 1
}
}
# ============================================================
# Benutzereingaben
# ============================================================
Write-Header "Starface-Verbindung"
Write-Host " Das Add-in kommuniziert per HTTPS mit der Starface." -ForegroundColor Gray
Write-Host " Da die Starface meist ein selbstsigniertes Zertifikat verwendet," -ForegroundColor Gray
Write-Host " muss dieses Zertifikat (CA) einmalig als vertrauenswuerdig" -ForegroundColor Gray
Write-Host " importiert werden. Dafuer wird hier nur die Adresse benoetigt." -ForegroundColor Gray
Write-Host "" -ForegroundColor Gray
Write-Host " Login-Daten werden hier NICHT benoetigt - diese konfigurieren" -ForegroundColor Gray
Write-Host " Sie spaeter im Add-in in den Sync-Profilen." -ForegroundColor Gray
Write-Host ""
$starfaceHost = Read-Host "Starface Host/IP-Adresse (z.B. 192.168.1.100 oder pbx.firma.local)"
if ([string]::IsNullOrWhiteSpace($starfaceHost)) {
Write-Err "Kein Host angegeben."
exit 1
}
$starfacePortInput = Read-Host "Starface HTTPS-Port [443]"
$starfacePort = if ([string]::IsNullOrWhiteSpace($starfacePortInput)) { 443 } else { [int]$starfacePortInput }
Write-Host ""
Write-Header "Lokaler Webserver"
$localPortInput = Read-Host "Lokaler HTTPS-Port fuer das Add-in [444]"
$localPort = if ([string]::IsNullOrWhiteSpace($localPortInput)) { 444 } else { [int]$localPortInput }
if (-not (Test-PortAvailable $localPort)) {
Write-Warn "Port $localPort ist bereits belegt!"
$localPortInput = Read-Host "Bitte einen anderen Port waehlen"
$localPort = [int]$localPortInput
if (-not (Test-PortAvailable $localPort)) {
Write-Err "Port $localPort ist ebenfalls belegt. Abbruch."
exit 1
}
}
Write-Step "Verwende Port: $localPort"
# ============================================================
# Schritt 1: Starface SSL-Zertifikat extrahieren und importieren
# ============================================================
Write-Header "Schritt 1: Starface-Zertifikat importieren"
try {
Import-StarfaceCert $starfaceHost $starfacePort
} catch {
Write-Err "Fehler beim Abrufen des Starface-Zertifikats: $_"
Write-Warn "Die Verbindung zur Starface API koennte fehlschlagen."
Write-Warn "Sie koennen das Zertifikat spaeter nachholen mit:"
Write-Host " .\import-cert.ps1 -StarfaceHost $starfaceHost -Port $starfacePort" -ForegroundColor White
Write-Host ""
Write-Host " Moechten Sie trotzdem fortfahren? (j/n)" -ForegroundColor Yellow
$continue = Read-Host
if ($continue -ne "j") { exit 1 }
}
# ============================================================
# Schritt 2: Lokale CA und localhost-Zertifikat erstellen
# ============================================================
Write-Header "Schritt 2: Lokale Zertifikate erstellen"
$certDir = Join-Path $InstallDir "certs"
New-Item -ItemType Directory -Path $certDir -Force | Out-Null
# Lokale CA erstellen
Write-Step "Erstelle lokale CA ..."
$caParams = @{
Subject = "CN=Starface Outlook Sync Local CA"
KeyLength = 2048
KeyAlgorithm = "RSA"
HashAlgorithm = "SHA256"
KeyExportPolicy = "Exportable"
NotAfter = (Get-Date).AddYears(10)
CertStoreLocation = "Cert:\LocalMachine\My"
KeyUsage = "CertSign", "CRLSign"
TextExtension = @("2.5.29.19={text}CA=true&pathlength=1")
}
$caCert = New-SelfSignedCertificate @caParams
Write-Step "Lokale CA erstellt: $($caCert.Thumbprint)"
# CA in Trusted Root Store importieren
$rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store(
[System.Security.Cryptography.X509Certificates.StoreName]::Root,
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
)
$rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$rootStore.Add($caCert)
$rootStore.Close()
Write-Step "Lokale CA als vertrauenswuerdig importiert."
# Localhost-Zertifikat erstellen, signiert von der lokalen CA
Write-Step "Erstelle localhost-Zertifikat ..."
$localhostParams = @{
Subject = "CN=localhost"
KeyLength = 2048
KeyAlgorithm = "RSA"
HashAlgorithm = "SHA256"
KeyExportPolicy = "Exportable"
NotAfter = (Get-Date).AddYears(5)
CertStoreLocation = "Cert:\LocalMachine\My"
Signer = $caCert
DnsName = "localhost", "127.0.0.1"
TextExtension = @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") # Server Authentication
}
$localhostCert = New-SelfSignedCertificate @localhostParams
Write-Step "Localhost-Zertifikat erstellt: $($localhostCert.Thumbprint)"
# Zertifikat als PFX exportieren (fuer Node.js)
# PFX funktioniert auf allen Windows-Versionen (.NET Framework + .NET Core)
Write-Step "Exportiere Zertifikate fuer den Webserver ..."
$pfxPassword = [guid]::NewGuid().ToString()
$pfxSecure = ConvertTo-SecureString -String $pfxPassword -Force -AsPlainText
$pfxPath = Join-Path $certDir "localhost.pfx"
Export-PfxCertificate -Cert $localhostCert -FilePath $pfxPath -Password $pfxSecure | Out-Null
# Passwort in config speichern (wird vom Server gelesen)
Set-Content -Path (Join-Path $certDir "pfx-password.txt") -Value $pfxPassword -Encoding ASCII
Write-Step "PFX-Zertifikat exportiert."
# CA-Zertifikat exportieren (fuer Deinstallation)
$caCertPem = "-----BEGIN CERTIFICATE-----`n"
$caCertPem += [Convert]::ToBase64String($caCert.RawData, [Base64FormattingOptions]::InsertLineBreaks)
$caCertPem += "`n-----END CERTIFICATE-----"
Set-Content -Path (Join-Path $certDir "local-ca.crt") -Value $caCertPem -Encoding ASCII
# Thumbprints speichern (fuer Deinstallation)
$certInfo = @{
caThumbprint = $caCert.Thumbprint
localhostThumbprint = $localhostCert.Thumbprint
}
$certInfo | ConvertTo-Json | Set-Content (Join-Path $InstallDir "cert-info.json") -Encoding UTF8
Write-Step "Zertifikate exportiert nach: $certDir"
# ============================================================
# Schritt 3: Add-in Dateien installieren
# ============================================================
Write-Header "Schritt 3: Add-in Dateien installieren"
$webrootDir = Join-Path $InstallDir "webroot"
New-Item -ItemType Directory -Path $webrootDir -Force | Out-Null
# Pruefen ob dist-Ordner existiert (bereits gebaut)
$distDir = Join-Path $scriptDir "..\dist"
if (-not (Test-Path $distDir)) {
Write-Step "Baue Add-in (npm run build) ..."
Push-Location (Join-Path $scriptDir "..")
& npm run build 2>&1 | Out-Null
Pop-Location
}
if (-not (Test-Path $distDir)) {
Write-Err "Build fehlgeschlagen. dist-Ordner nicht gefunden."
exit 1
}
# Dateien kopieren
Copy-Item -Path "$distDir\*" -Destination $webrootDir -Recurse -Force
Write-Step "Add-in Dateien kopiert nach: $webrootDir"
# manifest.xml mit korrektem Port anpassen
$manifestPath = Join-Path $webrootDir "manifest.xml"
if (Test-Path $manifestPath) {
$manifestContent = Get-Content $manifestPath -Raw -Encoding UTF8
$manifestContent = $manifestContent -replace "https://localhost:3000", "https://localhost:$localPort"
Set-Content -Path $manifestPath -Value $manifestContent -Encoding UTF8
Write-Step "manifest.xml angepasst (Port: $localPort)"
}
# Auch eine Kopie der manifest.xml ins Installationsverzeichnis
Copy-Item -Path $manifestPath -Destination (Join-Path $InstallDir "manifest.xml") -Force
# ============================================================
# Schritt 4: Server-Konfiguration und Installation
# ============================================================
Write-Header "Schritt 4: Lokalen Webserver einrichten"
# Server-Dateien kopieren
Copy-Item -Path (Join-Path $scriptDir "local-server.js") -Destination $InstallDir -Force
# import-cert.ps1 ins Installationsverzeichnis kopieren (fuer spaetere Nutzung)
$importCertSrc = Join-Path $scriptDir "import-cert.ps1"
if (Test-Path $importCertSrc) {
Copy-Item -Path $importCertSrc -Destination $InstallDir -Force
}
# uninstall.ps1 ins Installationsverzeichnis kopieren
$uninstallSrc = Join-Path $scriptDir "uninstall.ps1"
if (Test-Path $uninstallSrc) {
Copy-Item -Path $uninstallSrc -Destination $InstallDir -Force
}
# config.json erstellen
$serverConfig = @{
port = $localPort
}
$serverConfig | ConvertTo-Json | Set-Content (Join-Path $InstallDir "config.json") -Encoding UTF8
Write-Step "Server-Konfiguration erstellt."
# Windows Scheduled Task erstellen (startet beim Systemstart)
$taskName = "StarfaceOutlookSyncServer"
# Bestehenden Task entfernen falls vorhanden
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
$nodeExe = (Get-Command node).Source
$serverScript = Join-Path $InstallDir "local-server.js"
$action = New-ScheduledTaskAction -Execute $nodeExe -Argument "`"$serverScript`"" -WorkingDirectory $InstallDir
$trigger = New-ScheduledTaskTrigger -AtStartup
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit ([TimeSpan]::Zero)
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "Lokaler HTTPS-Server fuer Starface Outlook Sync Add-in" | Out-Null
# Task sofort starten
Start-ScheduledTask -TaskName $taskName
Start-Sleep -Seconds 2
# Pruefen ob der Server laeuft
try {
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
$response = Invoke-WebRequest -Uri "https://localhost:$localPort/taskpane.html" -UseBasicParsing -TimeoutSec 5
if ($response.StatusCode -eq 200) {
Write-Step "Lokaler Webserver laeuft auf https://localhost:$localPort"
}
} catch {
Write-Warn "Server-Test fehlgeschlagen. Server startet moeglicherweise verzoegert."
}
# ============================================================
# Schritt 5: Outlook Add-in registrieren
# ============================================================
Write-Header "Schritt 5: Outlook Add-in registrieren"
# Manifest-Datei vorbereiten
$manifestDir = Join-Path $InstallDir "manifest-catalog"
New-Item -ItemType Directory -Path $manifestDir -Force | Out-Null
Copy-Item -Path (Join-Path $InstallDir "manifest.xml") -Destination $manifestDir -Force
$manifestFile = Join-Path $manifestDir "manifest.xml"
# Outlook Add-ins werden pro Benutzer ueber den Browser registriert.
# Das Add-in wird am Microsoft 365 Konto des Benutzers gespeichert,
# nicht am Computer. Auf einem Terminal Server muss jeder Benutzer
# das Add-in einmalig selbst hinzufuegen.
Write-Host " Das Add-in muss pro Benutzer einmalig in Outlook" -ForegroundColor White
Write-Host " hinzugefuegt werden. Es wird am Microsoft 365 Konto" -ForegroundColor White
Write-Host " gespeichert und ist dann auf allen Geraeten verfuegbar." -ForegroundColor White
Write-Host ""
Write-Host " So geht's:" -ForegroundColor Cyan
Write-Host " 1. Im Browser oeffnen: https://aka.ms/olksideload" -ForegroundColor White
Write-Host " 2. Mit dem eigenen Microsoft 365 Konto anmelden" -ForegroundColor White
Write-Host " 3. 'Meine Add-Ins' -> 'Benutzerdefinierte Add-Ins'" -ForegroundColor White
Write-Host " 4. 'Benutzerdefiniertes Add-In hinzufuegen' -> 'Aus Datei'" -ForegroundColor White
Write-Host " 5. Diese Datei waehlen:" -ForegroundColor White
Write-Host " $manifestFile" -ForegroundColor Yellow
Write-Host ""
Write-Host " Alternativ direkt in Outlook:" -ForegroundColor Cyan
Write-Host " Classic: Datei -> Info -> 'Add-Ins verwalten'" -ForegroundColor White
Write-Host " Neu: Einstellungen -> Add-Ins verwalten" -ForegroundColor White
Write-Host ""
$openBrowser = Read-Host "Sideload-Seite jetzt im Browser oeffnen? (j/n)"
if ($openBrowser -eq "j") {
Start-Process "https://aka.ms/olksideload"
Write-Step "Browser geoeffnet."
Write-Host " Bitte Add-in wie oben beschrieben hinzufuegen." -ForegroundColor White
Write-Host " Manifest-Datei: $manifestFile" -ForegroundColor Yellow
}
# ============================================================
# Schritt 6: Installationsinfo speichern
# ============================================================
$installInfo = @{
installDir = $InstallDir
localPort = $localPort
taskName = $taskName
installedAt = (Get-Date).ToString("o")
manifestDir = $manifestDir
manifestFile = $manifestFile
manifestUrl = "https://localhost:$localPort"
caThumbprint = $caCert.Thumbprint
localhostThumbprint = $localhostCert.Thumbprint
}
$installInfo | ConvertTo-Json | Set-Content (Join-Path $InstallDir "install-info.json") -Encoding UTF8
# ============================================================
# Zusammenfassung
# ============================================================
Write-Header "Installation abgeschlossen!"
Write-Host " Installationsverzeichnis: $InstallDir" -ForegroundColor White
Write-Host " Lokaler Server: https://localhost:$localPort" -ForegroundColor White
Write-Host " Manifest: $manifestFile" -ForegroundColor White
Write-Host ""
Write-Host " [!] Jeder Benutzer muss das Add-in einmalig in Outlook" -ForegroundColor Yellow
Write-Host " hinzufuegen: https://aka.ms/olksideload" -ForegroundColor Yellow
Write-Host " Manifest-Datei: $manifestFile" -ForegroundColor Yellow
Write-Host ""
Write-Host " Naechster Schritt: Im Add-in ein Sync-Profil mit den" -ForegroundColor White
Write-Host " Starface-Zugangsdaten einrichten." -ForegroundColor White
Write-Host ""
Write-Host " Weitere Starface-Anlagen einbinden? Zertifikat importieren mit:" -ForegroundColor Gray
Write-Host " .\import-cert.ps1 -StarfaceHost <host> [-Port <port>]" -ForegroundColor Gray
Write-Host ""
Write-Host " Deinstallation: uninstall.ps1 als Administrator ausfuehren" -ForegroundColor Gray
Write-Host ""
# Firewall-Regel fuer den lokalen Port (nur localhost, rein vorsichtshalber)
New-NetFirewallRule -DisplayName "Starface Outlook Sync" -Direction Inbound -LocalPort $localPort -Protocol TCP -Action Allow -Profile Private -ErrorAction SilentlyContinue | Out-Null
Read-Host "Eingabetaste zum Beenden"