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
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/.idea
|
||||||
|
/.claude
|
||||||
107
README.md
Normal file
107
README.md
Normal file
@@ -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.
|
||||||
253
delete-firmware.ps1
Normal file
253
delete-firmware.ps1
Normal file
@@ -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 ==="
|
||||||
4
exempleExport.csv
Normal file
4
exempleExport.csv
Normal file
@@ -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"
|
||||||
|
Reference in New Issue
Block a user