182 lines
9.1 KiB
PowerShell
182 lines
9.1 KiB
PowerShell
# ============================================================
|
|
# Omika Diagnostic Center — Déploiement Phase 5B2 (SERVER uniquement)
|
|
# ============================================================
|
|
# CIBLE : Windows Server 2025, PowerShell ADMINISTRATEUR.
|
|
# NE PAS exécuter depuis le poste de développement.
|
|
#
|
|
# Ce script :
|
|
# 1. Vérifie les prérequis (admin + runtime .NET 10 présent).
|
|
# 2. Crée C:\Omika\Apps\DiagnosticsWorker et C:\Omika\Apps\DiagnosticsWeb.
|
|
# 3. Copie les publications (binaires framework-dependent win-x64).
|
|
# 4. Sauvegarde l'ancienne version avant remplacement.
|
|
# 5. Copie le Worker (Windows Service REEL). StartupType MANUAL + STOPPED :
|
|
# JAMAIS de demarrage avant Phase 5C. Si le service n'existe pas, sa
|
|
# creation est DIFFEREE a la Phase 5C (compte A_DETERMINER_EN_PHASE_5C,
|
|
# aucun compte LocalSystem/NetworkService fige automatiquement).
|
|
# 6. Préserve la configuration serveur déjà injectée (jamais écrasée).
|
|
#
|
|
# Ce script NE FAIT PAS (interdictions Phase 5B2A) :
|
|
# - AUCUN mot de passe PostgreSQL en dur ; AUCUNE donnée secrète écrite.
|
|
# - AUCUNE ouverture de port 5432 ni 4318 ; AUCUNE règle firewall.
|
|
# - AUCUNE modification du Collector OpenTelemetry.
|
|
# - AUCUNE modification de HFSQL ni de Omika.Api.
|
|
# - AUCUN démarrage de la Phase 5C.
|
|
# - AUCUN démarrage du Worker (service MANUAL + STOPPED, jamais Automatic).
|
|
# - AUCUN choix d'hébergement Web (IIS vs Kestrel) : le Web est copié seulement.
|
|
#
|
|
# IDEMPOTENT : une seconde exécution ne crée pas de service en double, ne
|
|
# ré-applique pas le schéma, n'écrase pas la config secrète, ne multiplie pas
|
|
# les dossiers, et ne perd ni logs ni checkpoints.
|
|
# ============================================================
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$PackageRoot,
|
|
[string]$ServiceName = 'OmikaDiagnosticsWorker',
|
|
[string]$DisplayName = 'Omika Diagnostics Worker',
|
|
[string]$WorkerTarget = 'C:\Omika\Apps\DiagnosticsWorker',
|
|
[string]$WebTarget = 'C:\Omika\Apps\DiagnosticsWeb',
|
|
[string]$BackupRoot = 'C:\Omika\Backups'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
# --- Resolution de PackageRoot APRES le bloc param (bug : $PSScriptRoot est
|
|
# vide dans une valeur par defaut de param()). Valeur par defaut : dossier
|
|
# PARENT du dossier du script (le package phase5b2 contient Deploy\ +
|
|
# DiagnosticsWorker\ + DiagnosticsWeb\ + Database\).
|
|
if (-not $PackageRoot) {
|
|
if ($PSScriptRoot) {
|
|
$PackageRoot = Split-Path -Parent $PSScriptRoot
|
|
}
|
|
}
|
|
if (-not $PackageRoot -or -not (Test-Path -LiteralPath $PackageRoot)) {
|
|
Write-Error "PackageRoot introuvable : '$PackageRoot'. Fournissez -PackageRoot '<chemin de phase5b2>' explicitement."
|
|
exit 1
|
|
}
|
|
|
|
function Write-Step([string]$msg) {
|
|
Write-Host "== $msg ==" -ForegroundColor Cyan
|
|
}
|
|
|
|
function Assert-Admin {
|
|
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = New-Object Security.Principal.WindowsPrincipal($id)
|
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
Write-Error "Ce script doit être exécuté en PowerShell ADMINISTRATEUR."
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
function Deploy-Folder([string]$Name, [string]$Source, [string]$Target, [string[]]$Preserve) {
|
|
$backup = $null
|
|
|
|
if (Test-Path -LiteralPath $Target) {
|
|
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
|
$backup = Join-Path $BackupRoot ("{0}-{1}" -f $Name, $stamp)
|
|
New-Item -ItemType Directory -Force -Path $backup | Out-Null
|
|
Copy-Item -Path (Join-Path $Target '*') -Destination $backup -Recurse -Force -ErrorAction SilentlyContinue
|
|
Write-Host " Ancienne version sauvegardée : $backup"
|
|
}
|
|
|
|
New-Item -ItemType Directory -Force -Path $Target | Out-Null
|
|
Copy-Item -Path (Join-Path $Source '*') -Destination $Target -Recurse -Force
|
|
|
|
# Préserver la configuration serveur déjà injectée (secret en 5B2B) :
|
|
# on restaure les fichiers de config depuis la sauvegarde s'ils existaient.
|
|
if ($backup) {
|
|
foreach ($file in $Preserve) {
|
|
$oldFile = Join-Path $backup $file
|
|
if (Test-Path -LiteralPath $oldFile) {
|
|
Copy-Item -Path $oldFile -Destination (Join-Path $Target $file) -Force
|
|
Write-Host " Configuration serveur préservée : $file"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- 1. Prérequis ---------------------------------------------------------
|
|
Write-Step 'Prérequis'
|
|
Assert-Admin
|
|
|
|
$dotnet = Get-Command dotnet -ErrorAction SilentlyContinue
|
|
if (-not $dotnet) {
|
|
Write-Error "Runtime .NET introuvable. Installez 'ASP.NET Core Runtime 10.x' (hébergement) sur le serveur."
|
|
exit 1
|
|
}
|
|
|
|
$hasAspNetCore10 = (& $dotnet.Source --list-runtimes 2>$null) -match 'Microsoft\.AspNetCore\.App\s+10\.'
|
|
if (-not $hasAspNetCore10) {
|
|
Write-Warning "Runtime 'Microsoft.AspNetCore.App 10.x' non détecté. Le Worker et le Web (framework-dependent) en ont besoin. Installez le ASP.NET Core Runtime 10.x avant de démarrer les applications."
|
|
}
|
|
|
|
$workerSource = Join-Path $PackageRoot 'DiagnosticsWorker'
|
|
$webSource = Join-Path $PackageRoot 'DiagnosticsWeb'
|
|
if (-not (Test-Path -LiteralPath $workerSource)) { Write-Error "Publication Worker introuvable : $workerSource"; exit 1 }
|
|
if (-not (Test-Path -LiteralPath $webSource)) { Write-Error "Publication Web introuvable : $webSource"; exit 1 }
|
|
|
|
# --- 2+3+4. Dossiers + copie + sauvegarde ---------------------------------
|
|
Write-Step 'Copie des publications (avec sauvegarde)'
|
|
New-Item -ItemType Directory -Force -Path $BackupRoot | Out-Null
|
|
|
|
Deploy-Folder 'DiagnosticsWorker' $workerSource $WorkerTarget @('appsettings.json', 'appsettings.Production.json')
|
|
Deploy-Folder 'DiagnosticsWeb' $webSource $WebTarget @('appsettings.json', 'appsettings.Production.json')
|
|
|
|
# --- 5+6. Service Windows (Worker uniquement) -----------------------------
|
|
Write-Step 'Service Windows Worker'
|
|
$workerExe = Join-Path $WorkerTarget 'Omika.Diagnostics.Worker.exe'
|
|
if (-not (Test-Path -LiteralPath $workerExe)) {
|
|
Write-Error "Exécutable Worker introuvable : $workerExe"
|
|
exit 1
|
|
}
|
|
|
|
# Binaire = VRAI Windows Service ?
|
|
$serviceCapable = Test-Path -LiteralPath (Join-Path $WorkerTarget 'Microsoft.Extensions.Hosting.WindowsServices.dll')
|
|
if (-not $serviceCapable) {
|
|
Write-Error "Le binaire Worker fourni n'est PAS un Windows Service (Microsoft.Extensions.Hosting.WindowsServices.dll absent). Re-publier avec EnableWindowsService=true. Aucun service ne sera cree."
|
|
exit 1
|
|
}
|
|
|
|
$existing = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
|
$serviceInstalled = $false
|
|
|
|
if ($existing) {
|
|
# Deja installe : NE JAMAIS demarrer. Forcer Manual + Stopped tant que la
|
|
# Phase 5C n'a pas valide la source JSONL distante.
|
|
Set-Service -Name $ServiceName -StartupType Manual
|
|
Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue
|
|
Write-Host " Service '$ServiceName' existant : force en Manual + Stopped (jamais demarre avant Phase 5C)."
|
|
$serviceInstalled = $true
|
|
} else {
|
|
# Compte de service definitif = A_DETERMINER_EN_PHASE_5C. Pour ne JAMAIS
|
|
# figer LocalSystem/NetworkService automatiquement, la creation du service
|
|
# est DIFFEREE a la Phase 5C. Seuls les fichiers sont installes ici.
|
|
Write-Warning "Creation du service '$ServiceName' DIFFEREE a la Phase 5C (compte definitif = A_DETERMINER_EN_PHASE_5C). Fichiers installes uniquement, aucun service cree, aucun compte fige."
|
|
}
|
|
|
|
# --- Résumé ----------------------------------------------------------------
|
|
Write-Host ''
|
|
Write-Host 'DEPLOIEMENT 5B2 TERMINE (fichiers copies ; service STOPPE / Manual).' -ForegroundColor Green
|
|
Write-Host " Worker : $WorkerTarget"
|
|
Write-Host " Web : $WebTarget (hébergement NON décidé ici — voir README-DEPLOYMENT.txt)"
|
|
Write-Host ''
|
|
if ($serviceInstalled) {
|
|
Write-Host 'OmikaDiagnosticsWorker installed but intentionally stopped.' -ForegroundColor Yellow
|
|
Write-Host 'Phase 5C is required before enabling or starting the service.' -ForegroundColor Yellow
|
|
} else {
|
|
Write-Host 'OmikaDiagnosticsWorker service creation deferred to Phase 5C.' -ForegroundColor Yellow
|
|
Write-Host 'No service installed: no Windows restart can start the Worker before Phase 5C.' -ForegroundColor Yellow
|
|
Write-Host 'Phase 5C is required before enabling or starting the service.' -ForegroundColor Yellow
|
|
}
|
|
Write-Host ''
|
|
Write-Host 'RAPPELS (a faire manuellement, jamais automatise ici) :' -ForegroundColor Yellow
|
|
Write-Host " - Injecter la connection string PostgreSQL via variable d'environnement"
|
|
Write-Host " ConnectionStrings__DiagnosticsDatabase (ou appsettings.Production.json local, ACL restreinte)."
|
|
Write-Host " - Définir le dossier JSONL : Observability__Remote__Directory."
|
|
Write-Host " - Appliquer le schéma : Deploy\Install-DatabaseSchema.ps1 (mot de passe jamais en dur)."
|
|
Write-Host " - Durcir PostgreSQL (003) : Deploy\Harden-DatabaseRoles.ps1 (contexte administrateur)."
|
|
Write-Host " - Décider l'hébergement Web (IIS ou Kestrel) après vérification des rôles serveur."
|
|
Write-Host " - Aucune règle firewall 5432/4318 n'a été ni ne doit être ajoutée."
|
|
Write-Host " - Phase 5C : determiner le compte de service (A_DETERMINER_EN_PHASE_5C),"
|
|
Write-Host " creer/activer le service, puis seulement alors demarrer le Worker."
|