feat(deploy): install silencieuse (dashboard) + MAJ GPO clients Léa + procédure Julien — 2026-07-06
Deux canaux de déploiement des clients Léa, en s'appuyant sur l'installeur EXE
v1.0.2 existant (per-user, upgrade-safe, mode /VERYSILENT /CONFIG=).
- Route dashboard `GET /api/fleet/installer` (web_dashboard/app.py, insertion pure,
125 lignes, 0 suppression) : sert un ZIP {EXE + install_silencieux.bat + enroll.txt
(SERVER_URL/token injectés via _resolve_public_server_url) + LISEZMOI} pour un poste
NEUF. L'EXE génère lui-même le machine_id → pas de pré-enregistrement (distinct de
la voie ZIP download_agent_package). Tri EXE en semver (1.0.10 > 1.0.2). Testée e2e.
- deploy/installer/silent/ : lanceur d'install silencieuse + gabarit enroll + notice.
- deploy/gpo/Deploy-LeaUpdate.ps1 : MAJ upgrade-safe via GPO **logon script user-context**
(install per-user invisible au contexte SYSTEM). Détection version = **DisplayVersion
du registre HKCU** (primaire pour install EXE ; le config.txt EXE ne porte PAS la
version — vérifié sur poste réel), fallback RPA_AGENT_VERSION (installs ZIP).
Comparaison semver, idempotent, journalisation best-effort silencieuse, non bloquant
à l'ouverture de session. Options -DryRun / -TargetVersionOverride / -LogShareOverride.
**Validé sur poste réel .11 (Windows PowerShell 5.1)** : branche « déjà à jour » et
branche « MAJ requise » (DryRun, 0 install), détection = 1.0.2.
- docs/PROCEDURE_DEPLOIEMENT_LEA_JULIEN.md : procédure (poste neuf / MAJ manuelle / GPO / rollback).
- EXE binaire jamais versionné (deploy/installer/releases/.gitignore).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
181
deploy/gpo/Deploy-LeaUpdate.ps1
Normal file
181
deploy/gpo/Deploy-LeaUpdate.ps1
Normal file
@@ -0,0 +1,181 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Met a jour l'agent Lea (installe per-user) via un script d'OUVERTURE DE SESSION GPO.
|
||||
|
||||
.DESCRIPTION
|
||||
============================================================================
|
||||
A DEPLOYER EN : User Configuration > Policies > Windows Settings >
|
||||
Scripts (Logon/Logoff) > Logon.
|
||||
ET PAS EN "Computer > Startup".
|
||||
============================================================================
|
||||
RAISON (non negociable) : l'installeur Lea est PER-USER
|
||||
(%LOCALAPPDATA%\Programs\Lea, cle de desinstallation sous HKCU). Un script de
|
||||
DEMARRAGE MACHINE tourne en contexte SYSTEM : il ne verrait NI l'installation
|
||||
NI HKCU du collaborateur -> il ne mettrait jamais rien a jour. Le contexte
|
||||
UTILISATEUR est donc obligatoire.
|
||||
|
||||
Le script, a chaque ouverture de session :
|
||||
1. Detecte la version installee (DisplayVersion de la cle de desinstallation
|
||||
Inno dans HKCU — primaire pour un install EXE ; fallback RPA_AGENT_VERSION
|
||||
du config.txt, present uniquement sur les installs via ZIP).
|
||||
2. La compare a la version cible (comparaison semver correcte :
|
||||
1.0.2 < 1.0.10, contrairement a une comparaison de chaines).
|
||||
3. Si plus ancienne : lance l'installeur en mode SILENCIEUX + UPGRADE-SAFE
|
||||
(SANS /CONFIG -> machine_id, serveur et token du poste PRESERVES).
|
||||
4. Journalise le resultat. IDEMPOTENT : ne refait rien si deja a jour.
|
||||
|
||||
Ne leve JAMAIS d'exception bloquant l'ouverture de session (tout en try/catch,
|
||||
sortie 0 sur erreur non geree).
|
||||
|
||||
.NOTES
|
||||
Execution : configurer la GPO pour lancer
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File Deploy-LeaUpdate.ps1
|
||||
|
||||
Test sur un poste (lecture seule, ne telecharge/installe RIEN) :
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File Deploy-LeaUpdate.ps1 -DryRun
|
||||
#>
|
||||
|
||||
param(
|
||||
# -DryRun : teste la DECISION (version installee vs cible) et journalise ce
|
||||
# qui SERAIT fait, sans rien telecharger ni installer. Pour valider un poste.
|
||||
[switch]$DryRun,
|
||||
# Overrides optionnels (sinon = valeurs du bloc PARAMETRES ci-dessous).
|
||||
# Utiles pour tester un poste (cible bumpee, journal local) sans editer le script.
|
||||
[string]$TargetVersionOverride,
|
||||
[string]$LogShareOverride
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# PARAMETRES A ADAPTER PAR LE DSI / Julien (les 3 seules lignes a toucher)
|
||||
# ============================================================================
|
||||
|
||||
# Source de l'installeur : chemin UNC du domaine (recommande) OU URL http(s).
|
||||
# UNC : \\SRV-FICHIERS\Deploiement$\Lea\Lea-Setup-v1.0.2.exe
|
||||
# HTTPS : https://lea.labs.laurinebazin.design/api/fleet/installer/exe
|
||||
$ExeSource = '\\SRV-FICHIERS\Deploiement$\Lea\Lea-Setup-v1.0.2.exe'
|
||||
|
||||
# Version cible : DOIT correspondre a l'EXE ci-dessus.
|
||||
$TargetVersion = '1.0.2'
|
||||
|
||||
# Dossier de journalisation partage (audit Julien). Vide => journal local %TEMP%.
|
||||
$LogShare = '\\SRV-FICHIERS\Deploiement$\Lea\logs'
|
||||
|
||||
# ============================================================================
|
||||
# Ne rien modifier en dessous de cette ligne.
|
||||
# ============================================================================
|
||||
|
||||
# Overrides de ligne de commande (prioritaires sur le bloc PARAMETRES).
|
||||
if ($TargetVersionOverride) { $TargetVersion = $TargetVersionOverride }
|
||||
if ($PSBoundParameters.ContainsKey('LogShareOverride')) { $LogShare = $LogShareOverride }
|
||||
|
||||
$InstallDir = Join-Path $env:LOCALAPPDATA 'Programs\Lea'
|
||||
$ConfigTxt = Join-Path $InstallDir 'config.txt'
|
||||
$AppUninst = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B3F9A1E2-5C4D-4E7F-9A1B-2C3D4E5F6789}_is1'
|
||||
|
||||
function Write-Log([string]$msg) {
|
||||
$line = '{0} {1}\{2} {3}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $env:COMPUTERNAME, $env:USERNAME, $msg
|
||||
Write-Output $line
|
||||
# Journalisation best-effort : ne DOIT jamais perturber l'ouverture de session.
|
||||
# -ErrorAction SilentlyContinue car un partage injoignable produit une erreur
|
||||
# NON-terminante (non capturee par try/catch) — verifie sur poste reel .11.
|
||||
try {
|
||||
$dir = if ($LogShare) { $LogShare } else { Join-Path $env:TEMP 'Lea-update-logs' }
|
||||
if (-not (Test-Path $dir -ErrorAction SilentlyContinue)) {
|
||||
New-Item -ItemType Directory -Path $dir -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
}
|
||||
$file = Join-Path $dir ('{0}_{1}.log' -f $env:COMPUTERNAME, $env:USERNAME)
|
||||
Add-Content -Path $file -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue
|
||||
} catch { }
|
||||
}
|
||||
|
||||
function Get-InstalledVersion {
|
||||
# 1. Source de verite pour un install EXE (Inno) : DisplayVersion dans HKCU
|
||||
# (install per-user). Toujours ecrite par l'installeur -> methode primaire.
|
||||
# Verifie sur poste reel .11 : le config.txt genere par l'EXE ne contient
|
||||
# PAS de ligne de version, seul le registre fait foi.
|
||||
if (Test-Path $AppUninst) {
|
||||
$dv = (Get-ItemProperty -Path $AppUninst -Name DisplayVersion -ErrorAction SilentlyContinue).DisplayVersion
|
||||
if ($dv) { return $dv }
|
||||
}
|
||||
# 2. Fallback : RPA_AGENT_VERSION dans config.txt (present sur les installs
|
||||
# via ZIP/Python ; absent des installs via EXE).
|
||||
if (Test-Path $ConfigTxt) {
|
||||
$m = Select-String -Path $ConfigTxt -Pattern '^\s*RPA_AGENT_VERSION\s*=\s*(.+?)\s*$' -ErrorAction SilentlyContinue
|
||||
if ($m) { return $m.Matches[0].Groups[1].Value.Trim() }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function ConvertTo-VersionParts([string]$v) {
|
||||
$clean = ($v -replace '^[vV]', '').Trim()
|
||||
$parts = New-Object System.Collections.Generic.List[int]
|
||||
foreach ($p in ($clean -split '\.')) {
|
||||
$n = 0
|
||||
if ([int]::TryParse($p, [ref]$n)) { $parts.Add($n) } else { $parts.Add(0) }
|
||||
}
|
||||
while ($parts.Count -lt 3) { $parts.Add(0) }
|
||||
return $parts
|
||||
}
|
||||
|
||||
function Compare-Semver([string]$a, [string]$b) {
|
||||
# -1 si a<b, 0 si egal, 1 si a>b. Tolerant (piege 1.0.2 < 1.0.10).
|
||||
$pa = ConvertTo-VersionParts $a
|
||||
$pb = ConvertTo-VersionParts $b
|
||||
$max = [Math]::Max($pa.Count, $pb.Count)
|
||||
for ($i = 0; $i -lt $max; $i++) {
|
||||
$va = if ($i -lt $pa.Count) { $pa[$i] } else { 0 }
|
||||
$vb = if ($i -lt $pb.Count) { $pb[$i] } else { 0 }
|
||||
if ($va -lt $vb) { return -1 }
|
||||
if ($va -gt $vb) { return 1 }
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
try {
|
||||
$installed = Get-InstalledVersion
|
||||
if (-not $installed) {
|
||||
Write-Log 'Lea non installe sur ce profil -> ignore (les nouveaux postes passent par le dashboard).'
|
||||
exit 0
|
||||
}
|
||||
if ((Compare-Semver $installed $TargetVersion) -ge 0) {
|
||||
Write-Log ('Deja a jour (installe={0}, cible={1}) -> rien a faire.' -f $installed, $TargetVersion)
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($DryRun) {
|
||||
Write-Log ('[DryRun] MAJ qui SERAIT appliquee : {0} -> {1}. Aucune action (ni telechargement ni installation).' -f $installed, $TargetVersion)
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Log ('MAJ requise : installe={0} < cible={1}. Recuperation de l''installeur...' -f $installed, $TargetVersion)
|
||||
|
||||
# Recuperer l'EXE (UNC ou URL) vers un fichier temporaire local.
|
||||
$localExe = Join-Path $env:TEMP ('Lea-Setup-{0}.exe' -f $TargetVersion)
|
||||
if ($ExeSource -match '^(?i)https?://') {
|
||||
Invoke-WebRequest -Uri $ExeSource -OutFile $localExe -UseBasicParsing
|
||||
} else {
|
||||
Copy-Item -Path $ExeSource -Destination $localExe -Force
|
||||
}
|
||||
if (-not (Test-Path $localExe)) {
|
||||
Write-Log ('ECHEC : installeur introuvable apres recuperation ({0}).' -f $ExeSource)
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Installation SILENCIEUSE + UPGRADE-SAFE (sans /CONFIG : identite du poste preservee).
|
||||
# L'installeur arrete Lea (via lock), sauvegarde <app>_backup, puis met a jour.
|
||||
$logInstall = Join-Path $env:TEMP ('Lea-upgrade-{0}.log' -f (Get-Date -Format 'yyyyMMdd_HHmmss'))
|
||||
$installArgs = @('/VERYSILENT', '/SUPPRESSMSGBOXES', '/NORESTART', ('/LOG={0}' -f $logInstall))
|
||||
$proc = Start-Process -FilePath $localExe -ArgumentList $installArgs -Wait -PassThru
|
||||
$rc = $proc.ExitCode
|
||||
|
||||
if ($rc -eq 0) {
|
||||
$now = Get-InstalledVersion
|
||||
Write-Log ('MAJ OK : {0} -> {1} (code 0).' -f $installed, $now)
|
||||
} else {
|
||||
Write-Log ('ECHEC MAJ : code {0}. Journal installeur : {1}.' -f $rc, $logInstall)
|
||||
}
|
||||
exit $rc
|
||||
} catch {
|
||||
Write-Log ('ERREUR non geree : ' + $_.Exception.Message)
|
||||
exit 0 # ne JAMAIS bloquer l'ouverture de session
|
||||
}
|
||||
Reference in New Issue
Block a user