Files
rpa_vision_v3/deploy/gpo/Deploy-LeaUpdate.ps1
Dom 48b1fc2800
Some checks failed
tests / Lint (ruff + black) (push) Failing after 1m58s
tests / Tests unitaires (sans GPU) (push) Failing after 2m8s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-07-06 10:35:22 +02:00

182 lines
8.2 KiB
PowerShell

<#
.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
}