From 3767d5a0db540663a993a66d59f88db2549bbea4 Mon Sep 17 00:00:00 2001 From: Charles Date: Thu, 5 Feb 2026 16:25:55 +0100 Subject: [PATCH] Script PowerShell de suppression des fichiers firmware DFF via API REST - Lecture CSV depuis export XpressNetwork Utilities - Detection automatique du delimiteur (virgule ou point-virgule) - Support HTTP et HTTPS avec ports configurables - Authentification Basic Auth - Mode dry-run pour tester sans supprimer - Fichier de log avec succes et erreurs --- .gitignore | 2 + README.md | 107 +++++++++++++++++++ delete-firmware.ps1 | 253 ++++++++++++++++++++++++++++++++++++++++++++ exempleExport.csv | 4 + 4 files changed, 366 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 delete-firmware.ps1 create mode 100644 exempleExport.csv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60531c7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea +/.claude \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f9bd51 --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# Script de suppression des fichiers firmware DFF + +Ce script permet de supprimer automatiquement les fichiers `.DFF` (firmware) sur plusieurs controleurs Distech Controls. + +## Pre-requis + +- Windows 10 ou Windows 11 +- Fichier CSV exporte depuis **XpressNetwork Utilities** + +## Etape 1 : Exporter la liste des controleurs + +1. Ouvrir **XpressNetwork Utilities** +2. Selectionner les controleurs souhaites +3. Exporter la liste en CSV + +## Etape 2 : Ajouter les identifiants au fichier CSV + +Le fichier CSV exporte ne contient pas les identifiants de connexion. Il faut les ajouter manuellement. + +1. Ouvrir le fichier CSV avec **Excel** ou un editeur de texte +2. Trouver les colonnes `Username` et `Password` (dernieres colonnes) +3. Remplir ces colonnes pour chaque controleur + +Exemple : +``` +"Username";"Password" +"admin";"MonMotDePasse" +"admin";"MonMotDePasse" +``` + +4. Enregistrer le fichier + +## Etape 3 : Executer le script + +### Ouvrir PowerShell dans le bon dossier + +1. Ouvrir l'**Explorateur de fichiers** +2. Naviguer vers le dossier contenant le script et le fichier CSV +3. **Maintenir la touche Shift** et faire un **clic droit** dans le dossier (pas sur un fichier) +4. Cliquer sur **"Ouvrir la fenetre PowerShell ici"** ou **"Ouvrir dans le terminal"** + +### Option A : Mode test (recommande en premier) + +Ce mode affiche les fichiers qui seraient supprimes **sans les supprimer**. + +Copier-coller la commande suivante : + +```powershell +powershell -ExecutionPolicy Bypass -File delete-firmware.ps1 -CsvPath "exempleExport.csv" -DryRun +``` + +### Option B : Suppression reelle + +Une fois le mode test valide, executer sans `-DryRun` : + +```powershell +powershell -ExecutionPolicy Bypass -File delete-firmware.ps1 -CsvPath "exempleExport.csv" +``` + +## Exemple complet + +Si le script et le CSV sont dans le meme dossier, ouvrir PowerShell dans ce dossier puis : + +```powershell +# Mode test +powershell -ExecutionPolicy Bypass -File delete-firmware.ps1 -CsvPath "exempleExport.csv" -DryRun + +# Suppression reelle +powershell -ExecutionPolicy Bypass -File delete-firmware.ps1 -CsvPath "exempleExport.csv" +``` + +## Fichier de log + +Un fichier de log est cree automatiquement dans le meme dossier que le CSV : +``` +delete-firmware_2026-02-05_15-45-16.log +``` + +Ce fichier contient : +- Les controleurs traites +- Les fichiers supprimes +- Les erreurs rencontrees + +## Comprendre les resultats + +| Message | Signification | +|---------|---------------| +| `SUPPRIME: xxx.DFF` | Fichier supprime avec succes | +| `[DRY-RUN] A supprimer: xxx.DFF` | Fichier qui serait supprime (mode test) | +| `Aucun fichier DFF trouve` | Pas de fichier .DFF sur ce controleur | +| `ECHEC connexion` | Controleur injoignable (verifier IP/reseau) | + +## Depannage + +### "Impossible de se connecter au serveur distant" +- Verifier que le controleur est allume et accessible sur le reseau +- Verifier l'adresse IP dans le fichier CSV + +### "Erreur 401" ou "Non autorise" +- Verifier le nom d'utilisateur et mot de passe dans le CSV + +### Le script ne se lance pas +- Verifier que la commande commence bien par `powershell -ExecutionPolicy Bypass` + +## Support + +Pour toute question, contacter le support technique. diff --git a/delete-firmware.ps1 b/delete-firmware.ps1 new file mode 100644 index 0000000..32e8c1a --- /dev/null +++ b/delete-firmware.ps1 @@ -0,0 +1,253 @@ +# delete-firmware.ps1 +# Script de suppression des fichiers DFF via API REST +# Usage: powershell -ExecutionPolicy Bypass -File delete-firmware.ps1 -CsvPath "chemin\vers\export.csv" +# Dry-run: powershell -ExecutionPolicy Bypass -File delete-firmware.ps1 -CsvPath "chemin\vers\export.csv" -DryRun + +param( + [Parameter(Mandatory=$true)] + [string]$CsvPath, + + [int]$Timeout = 30, + + [switch]$DryRun +) + +# Fichier de log avec timestamp (dans le meme dossier que le CSV) +$CsvFolder = Split-Path $CsvPath -Parent +if ([string]::IsNullOrEmpty($CsvFolder)) { + $CsvFolder = Get-Location +} +$LogFile = Join-Path $CsvFolder ("delete-firmware_" + (Get-Date -Format "yyyy-MM-dd_HH-mm-ss") + ".log") + +# Detecter le delimiteur du CSV (virgule ou point-virgule) +function Get-CsvDelimiter { + param([string]$FilePath) + + $firstLine = Get-Content -Path $FilePath -TotalCount 1 + $semicolonCount = ($firstLine.ToCharArray() | Where-Object { $_ -eq ';' }).Count + $commaCount = ($firstLine.ToCharArray() | Where-Object { $_ -eq ',' }).Count + + if ($semicolonCount -gt $commaCount) { + return ';' + } + else { + return ',' + } +} + +# Fonction de log +function Write-Log { + param([string]$Message, [string]$Level = "INFO") + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $logLine = "[$timestamp] [$Level] $Message" + Write-Host $logLine + Add-Content -Path $LogFile -Value $logLine -Encoding UTF8 +} + +# Desactiver la verification SSL pour les certificats auto-signes +function Disable-SSLValidation { + if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy').Type) { + Add-Type @" +using System.Net; +using System.Security.Cryptography.X509Certificates; +public class TrustAllCertsPolicy : ICertificatePolicy { + public bool CheckValidationResult( + ServicePoint srvPoint, X509Certificate certificate, + WebRequest request, int certificateProblem) { + return true; + } +} +"@ + } + [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 +} + +# Determiner l'URL de base selon HttpPort et HttpsPort +function Get-BaseUrl { + param( + [string]$IP, + [string]$HttpPort, + [string]$HttpsPort + ) + + if ($HttpsPort -and $HttpsPort -ne "-1") { + return "https://${IP}:${HttpsPort}" + } + elseif ($HttpPort -and $HttpPort -ne "-1") { + return "http://${IP}:${HttpPort}" + } + else { + return "https://${IP}:443" + } +} + +# Creer les headers d'authentification Basic +function Get-AuthHeaders { + param( + [string]$Username, + [string]$Password + ) + + $pair = "${Username}:${Password}" + $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) + $base64 = [System.Convert]::ToBase64String($bytes) + + return @{ + "Authorization" = "Basic $base64" + "Content-Type" = "application/json" + } +} + +# GET fichiers firmware +function Get-FirmwareFiles { + param( + [string]$BaseUrl, + [hashtable]$Headers + ) + + $url = "$BaseUrl/api/rest/v1/files/firmware?encode=json" + + try { + $response = Invoke-RestMethod -Uri $url -Method Get -Headers $Headers -TimeoutSec $Timeout + return $response + } + catch { + throw "Erreur GET $url : $($_.Exception.Message)" + } +} + +# DELETE fichier firmware +function Remove-FirmwareFile { + param( + [string]$BaseUrl, + [hashtable]$Headers, + [string]$FileName + ) + + $url = "$BaseUrl/api/rest/v1/files/firmware/$FileName" + + try { + Invoke-RestMethod -Uri $url -Method Delete -Headers $Headers -TimeoutSec $Timeout | Out-Null + return $true + } + catch { + throw "Erreur DELETE $url : $($_.Exception.Message)" + } +} + +# === SCRIPT PRINCIPAL === + +Write-Log "=== Demarrage du script de suppression des fichiers DFF ===" +if ($DryRun) { + Write-Log "*** MODE DRY-RUN ACTIF - Aucune suppression ne sera effectuee ***" "WARN" +} +Write-Log "Fichier CSV: $CsvPath" +Write-Log "Fichier log: $LogFile" + +# Verifier que le fichier CSV existe +if (-not (Test-Path $CsvPath)) { + Write-Log "ERREUR: Le fichier CSV n'existe pas: $CsvPath" "ERROR" + exit 1 +} + +# Desactiver la validation SSL +Disable-SSLValidation +Write-Log "Validation SSL desactivee" + +# Detecter le delimiteur et importer le CSV +try { + $delimiter = Get-CsvDelimiter -FilePath $CsvPath + Write-Log "Delimiteur detecte: '$delimiter'" + $devices = Import-Csv -Path $CsvPath -Delimiter $delimiter -Encoding UTF8 + Write-Log "CSV importe: $($devices.Count) equipement(s) trouve(s)" +} +catch { + Write-Log "ERREUR lors de l'import du CSV: $($_.Exception.Message)" "ERROR" + exit 1 +} + +# Compteurs +$totalDevices = 0 +$totalFilesDeleted = 0 +$totalErrors = 0 + +# Traiter chaque equipement +foreach ($device in $devices) { + $totalDevices++ + + $ip = $device."Current Ip" + $httpPort = $device."HttpPort" + $httpsPort = $device."HttpsPort" + $username = $device."Username" + $password = $device."Password" + $hostname = $device."Hostname" + + Write-Log "--- Traitement de $hostname ($ip) ---" + + # Determiner l'URL de base + $baseUrl = Get-BaseUrl -IP $ip -HttpPort $httpPort -HttpsPort $httpsPort + Write-Log "URL de base: $baseUrl" + + # Creer les headers d'authentification + $headers = Get-AuthHeaders -Username $username -Password $password + + # GET fichiers firmware + try { + $response = Get-FirmwareFiles -BaseUrl $baseUrl -Headers $headers + + if ($response.files -and $response.files.Count -gt 0) { + Write-Log "Fichiers trouves: $($response.files.Count)" + + # Filtrer les fichiers .DFF (forcer en tableau avec @()) + $dffFiles = @($response.files | Where-Object { $_.path.name -like "*.DFF" }) + + if ($dffFiles.Count -gt 0) { + Write-Log "Fichiers DFF a supprimer: $($dffFiles.Count)" + + foreach ($file in $dffFiles) { + $fileName = $file.path.name + + if ($DryRun) { + Write-Log "[DRY-RUN] A supprimer: $fileName" "DRYRUN" + $totalFilesDeleted++ + } + else { + try { + Remove-FirmwareFile -BaseUrl $baseUrl -Headers $headers -FileName $fileName | Out-Null + Write-Log "SUPPRIME: $fileName" "SUCCESS" + $totalFilesDeleted++ + } + catch { + Write-Log "ECHEC suppression $fileName : $($_.Exception.Message)" "ERROR" + $totalErrors++ + } + } + } + } + else { + Write-Log "Aucun fichier DFF trouve" + } + } + else { + Write-Log "Aucun fichier dans le dossier firmware" + } + } + catch { + Write-Log "ECHEC connexion a $hostname ($ip): $($_.Exception.Message)" "ERROR" + $totalErrors++ + } +} + +# Rapport final +Write-Log "=== RAPPORT FINAL ===" +if ($DryRun) { + Write-Log "*** MODE DRY-RUN - Aucun fichier n'a ete supprime ***" "WARN" + Write-Log "Fichiers DFF qui seraient supprimes: $totalFilesDeleted" +} +else { + Write-Log "Fichiers DFF supprimes: $totalFilesDeleted" +} +Write-Log "Equipements traites: $totalDevices" +Write-Log "Erreurs: $totalErrors" +Write-Log "=== Fin du script ===" diff --git a/exempleExport.csv b/exempleExport.csv new file mode 100644 index 0000000..9d9d958 --- /dev/null +++ b/exempleExport.csv @@ -0,0 +1,4 @@ +"Hostname";"Current Ip";"Mac Address";"WifiMac";"Location";"Description";"SoftwareVersion";"HostID";"ControllerName";"DeviceID";"ApiRevision";"HttpPort";"HttpsPort";"ModelName";"ModelType";"RestServiceURL";"RestServiceVersion";"Type";"Username";"Password" +"TS-ECY_PTU_077_V1";"192.168.1.77";"A8:10:87:E9:EE:C5";"N/A";"";"";"1.19.25163.970";"ECYPTU107-AC1E19EF-6BA3-5097-8047-EF78B5683692";"ECY-PTU107-E9EEC5";"105077";"8,0";"-1";"-1";"ECY-PTU107 Rev 1.0A";"10016C000505048F";"";"";"Eclypse";"admin";"Distech123" +"TS-ECY_PTU_078_V1";"192.168.1.78";"A8:10:87:E9:EE:C6";"N/A";"";"";"1.19.25163.970";"ECYPTU107-AC1E19EF-6BA3-5097-8047-EF78B5683692";"ECY-PTU107-E9EEC6";"105078";"8,0";"8080";"-1";"ECY-PTU107 Rev 1.0A";"10016C000505048F";"";"";"Eclypse";"admin";"Distech123" +"TS-ECY_PTU_079_V1";"192.168.1.79";"A8:10:87:E9:EE:C7";"N/A";"";"";"1.19.25163.970";"ECYPTU107-AC1E19EF-6BA3-5097-8047-EF78B5683692";"ECY-PTU107-E9EEC7";"105079";"8,0";"-1";"4433";"ECY-PTU107 Rev 1.0A";"10016C000505048F";"";"";"Eclypse";"admin";"Distech123"