<# .SYNOPSIS CLI d'export des journaux d'audit pour automates Distech Controls Eclypse. .DESCRIPTION AuditLogCLI recupere les journaux d'audit (audit-logs) de chaque automate Distech Controls Eclypse liste dans un CSV d'entree, via leur API REST, et les enregistre au format CSV (un fichier par automate, ou un fichier consolide unique). .PARAMETER CsvInput Chemin vers le fichier CSV d'entree (separateur point-virgule). Colonnes obligatoires : Hostname, Current Ip, HttpPort, HttpsPort. Colonnes optionnelles : Username, Password (surchargent -Username/-Password). .PARAMETER Username Nom d'utilisateur pour l'authentification API (defaut: admin). Peut etre surcharge par la colonne Username du CSV. .PARAMETER Password Mot de passe pour l'authentification API (defaut: vide). Peut etre surcharge par la colonne Password du CSV. .PARAMETER StartDate Date de debut (format yyyy-MM-dd) pour filtrer les journaux d'audit. Optionnel. .PARAMETER EndDate Date de fin (format yyyy-MM-dd) pour filtrer les journaux d'audit. Optionnel. .PARAMETER OutputFormat Format de sortie : PerAutomate - Un fichier CSV brut par automate, dans un dossier horodate (defaut) Consolidated - Un seul fichier CSV avec toutes les lignes, colonnes Hostname/IP ajoutees .EXAMPLE .\AuditLogCLI.ps1 -CsvInput ".\automates.csv" Recupere les journaux d'audit de tous les automates listes dans le CSV. Genere un fichier CSV par automate dans un dossier Audit_yyyy-MM-dd_HH-mm. .EXAMPLE .\AuditLogCLI.ps1 -CsvInput ".\automates.csv" -OutputFormat Consolidated -StartDate 2026-01-01 -EndDate 2026-07-09 Recupere les journaux d'audit filtres sur la periode donnee et les consolide dans un seul fichier CSV. .NOTES Prerequis : PowerShell 5.1+ (inclus dans Windows 10/11) API : Distech Controls Eclypse REST API v1 Securite : TLS 1.2, certificats auto-signes acceptes Important : les noms de query params "startDate"/"endDate" sont une hypothese non validee contre la documentation officielle de l'API audit-logs. A verifier avant utilisation en production. #> param( [Parameter(Mandatory)] [string]$CsvInput, [string]$Username = "admin", [string]$Password = "", [string]$StartDate, [string]$EndDate, [ValidateSet("PerAutomate", "Consolidated")] [string]$OutputFormat = "PerAutomate" ) # Performance : desactiver la barre de progression Invoke-WebRequest $ProgressPreference = 'SilentlyContinue' # ===================================================================== # UTILITAIRES # ===================================================================== function Write-Log { param( [string]$Message, [ValidateSet("INFO", "WARN", "ERROR", "OK")] [string]$Level = "INFO" ) $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $tag = switch ($Level) { "INFO" {"[INFO ]"} "WARN" {"[WARN ]"} "ERROR" {"[ERR ]"} "OK" {"[ OK ]"} } $color = switch ($Level) { "INFO" {"Cyan"} "WARN" {"Yellow"} "ERROR" {"Red"} "OK" {"Green"} } Write-Host "$ts $tag $Message" -ForegroundColor $color } # ===================================================================== # FONCTION : Initialize-ApiClient # Force TLS 1.2 et accepte les certificats auto-signes (equivalent curl -k) # ===================================================================== function Initialize-ApiClient { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 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 } # ===================================================================== # FONCTION : Read-AutomateCsv # ===================================================================== function Read-AutomateCsv { param([string]$CsvPath) if (-not (Test-Path $CsvPath)) { throw "Fichier CSV introuvable : $CsvPath" } $rows = @(Import-Csv -Path $CsvPath -Delimiter ";" -Encoding UTF8) if ($rows.Count -eq 0) { throw "Fichier CSV vide : $CsvPath" } Write-Log -Message "CSV charge : $($rows.Count) ligne(s) depuis $CsvPath" -Level INFO return $rows } # ===================================================================== # FONCTION : Get-BaseUrl # HTTPS priorise sur HTTP, automate ignore si aucun port valide # ===================================================================== function Get-BaseUrl { param([PSCustomObject]$Automate) $ip = $Automate."Current Ip" $httpsPort = $Automate.HttpsPort $httpPort = $Automate.HttpPort if ($httpsPort -and $httpsPort -ne "" -and [int]$httpsPort -gt 0 -and [int]$httpsPort -ne -1) { if ([int]$httpsPort -eq 443) { return "https://$ip" } return "https://${ip}:$httpsPort" } if ($httpPort -and $httpPort -ne "" -and [int]$httpPort -gt 0 -and [int]$httpPort -ne -1) { if ([int]$httpPort -eq 80) { return "http://$ip" } return "http://${ip}:$httpPort" } return $null } # ===================================================================== # FONCTION : Get-Credentials # Identifiants CSV prioritaires sur les parametres globaux # ===================================================================== function Get-Credentials { param( [PSCustomObject]$Automate, [string]$DefaultUsername, [string]$DefaultPassword ) $username = $DefaultUsername $password = $DefaultPassword if ($Automate.Username -and $Automate.Username -ne "") { $username = $Automate.Username } if ($Automate.Password -and $Automate.Password -ne "") { $password = $Automate.Password } return @{ Username = $username; Password = $password } } function Get-AuthHeader { param( [string]$Username, [AllowEmptyString()][string]$Password ) $pair = "${Username}:${Password}" $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) $base64 = [System.Convert]::ToBase64String($bytes) return @{ "Authorization" = "Basic $base64" "Accept" = "text/csv, application/json, text/plain, */*" } } function Get-AuditLogUrl { param( [string]$BaseUrl, [string]$StartDate, [string]$EndDate ) $url = "$BaseUrl/api/rest/v1/system/audit-logs" $queryParams = @() if ($StartDate) { $queryParams += "startDate=$StartDate" } if ($EndDate) { $queryParams += "endDate=$EndDate" } if ($queryParams.Count -gt 0) { $url += "?" + ($queryParams -join "&") } return $url } function Invoke-AuditLogGet { param( [string]$Url, [string]$Username, [AllowEmptyString()][string]$Password ) $headers = Get-AuthHeader -Username $Username -Password $Password return Invoke-WebRequest -Uri $Url -Method GET -Headers $headers -UseBasicParsing -TimeoutSec 30 } # ===================================================================== # VALIDATION DES PARAMETRES DE DATE # ===================================================================== foreach ($dateParam in @( @{ Name = "StartDate"; Value = $StartDate }, @{ Name = "EndDate"; Value = $EndDate } )) { if ($dateParam.Value) { $parsed = [DateTime]::MinValue if (-not [DateTime]::TryParseExact($dateParam.Value, "yyyy-MM-dd", $null, [System.Globalization.DateTimeStyles]::None, [ref]$parsed)) { throw "Parametre -$($dateParam.Name) invalide : '$($dateParam.Value)' (format attendu : yyyy-MM-dd)" } } } # ===================================================================== # INITIALISATION # ===================================================================== Initialize-ApiClient Write-Log -Message "=== AuditLogCLI demarre - OutputFormat: $OutputFormat ===" -Level INFO $automates = Read-AutomateCsv -CsvPath $CsvInput $statsTotal = 0 $statsError = 0 $consolidatedRows = @() $outputDir = $null if ($OutputFormat -eq "PerAutomate") { $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm" $outputDir = Join-Path (Get-Location) "Audit_$timestamp" New-Item -ItemType Directory -Path $outputDir -Force | Out-Null Write-Log -Message "Dossier de sortie : $outputDir" -Level INFO } # ===================================================================== # TRAITEMENT DE CHAQUE AUTOMATE # ===================================================================== foreach ($automate in $automates) { $hostname = $automate.Hostname $ip = $automate."Current Ip" $statsTotal++ try { $baseUrl = Get-BaseUrl -Automate $automate if (-not $baseUrl) { Write-Log -Message "[$hostname] Aucun port HTTP/HTTPS valide - ignore" -Level WARN $statsError++ continue } $creds = Get-Credentials -Automate $automate -DefaultUsername $Username -DefaultPassword $Password $url = Get-AuditLogUrl -BaseUrl $baseUrl -StartDate $StartDate -EndDate $EndDate Write-Log -Message "[$hostname] GET $url (user: $($creds.Username))" -Level INFO $response = Invoke-AuditLogGet -Url $url -Username $creds.Username -Password $creds.Password if ($OutputFormat -eq "PerAutomate") { $filePath = Join-Path $outputDir "$ip.csv" Set-Content -Path $filePath -Value $response.Content -Encoding UTF8 Write-Log -Message "[$hostname] Journal d'audit ecrit : $filePath" -Level OK } else { # Suppose une reponse API au format CSV (comme le .bat d'origine qui # ecrit directement la reponse brute dans un fichier .csv) $rows = @($response.Content | ConvertFrom-Csv) foreach ($row in $rows) { $obj = [ordered]@{ Hostname = $hostname; IP = $ip } foreach ($prop in $row.PSObject.Properties) { $obj[$prop.Name] = $prop.Value } $consolidatedRows += [PSCustomObject]$obj } Write-Log -Message "[$hostname] $($rows.Count) entree(s) d'audit recuperee(s)" -Level OK } } catch { Write-Log -Message "[$hostname] ERREUR : $($_.Exception.Message)" -Level ERROR $statsError++ } } # ===================================================================== # ECRITURE DE LA SORTIE CONSOLIDEE # ===================================================================== if ($OutputFormat -eq "Consolidated") { $timestamp = Get-Date -Format "yyyy-MM-dd_HHhmm" $outputFile = Join-Path (Get-Location) "audit_$timestamp.csv" if ($consolidatedRows.Count -gt 0) { $consolidatedRows | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8 Write-Log -Message "CSV consolide ecrit : $outputFile ($($consolidatedRows.Count) ligne(s))" -Level OK } else { Write-Log -Message "Aucune donnee a ecrire dans le CSV consolide" -Level WARN } } # ===================================================================== # RESUME FINAL # ===================================================================== Write-Log -Message "========== RESUME ==========" -Level INFO Write-Log -Message "Automates traites : $statsTotal" -Level INFO Write-Log -Message "Automates en erreur : $statsError" -Level $(if ($statsError -gt 0) { "WARN" } else { "INFO" }) Write-Log -Message "============================" -Level INFO