<# .SYNOPSIS CLI d'activation/desactivation IPv6 pour automates Distech Controls Eclypse Facilities (API v2). .DESCRIPTION IPv6ManagerCLI active ou desactive l'IPv6 sur toutes les interfaces reseau IPv6-capables (secondary, auxiliary, wireless, bridge, primary...) de chaque automate Distech Controls Eclypse Facilities liste dans un CSV d'entree. Pour chaque automate : 1. GET /api/rest/v2/services/platform/network/interfaces - recupere la liste des interfaces 2. Filtre les interfaces possedant une section "ipv6" (peu importe son contenu) 3. POST /api/rest/v2/services/platform/network/interfaces/{name}/ipv6 avec {"enabled": true|false} pour chaque interface IPv6-capable detectee Compatible uniquement avec les automates Eclypse Facilities supportant l'API v2 (pas de fallback API v1). .PARAMETER Action Etat IPv6 a appliquer sur toutes les interfaces IPv6-capables detectees : Enable - Active l'IPv6 Disable - Desactive l'IPv6 (defaut) .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. .EXAMPLE .\IPv6ManagerCLI.ps1 -Action Disable -CsvInput ".\automates.csv" Desactive l'IPv6 sur toutes les interfaces IPv6-capables de chaque automate du CSV. .EXAMPLE .\IPv6ManagerCLI.ps1 -Action Enable -CsvInput ".\automates.csv" -Password "MonMotDePasse" Active l'IPv6 sur toutes les interfaces IPv6-capables de chaque automate du CSV. .NOTES Prerequis : PowerShell 5.1+ (inclus dans Windows 10/11) API : Distech Controls Eclypse Facilities REST API v2 uniquement (platform/network/interfaces) - pas de fallback v1 Securite : TLS 1.0/1.1/1.2, certificats auto-signes acceptes #> param( [ValidateSet("Enable", "Disable")] [string]$Action = "Disable", [Parameter(Mandatory)] [string]$CsvInput, [string]$Username = "admin", [string]$Password = "" ) # Performance : desactiver la barre de progression Invoke-WebRequest $ProgressPreference = 'SilentlyContinue' # ===================================================================== # UTILITAIRES # ===================================================================== $script:LogFilePath = Join-Path (Get-Location) "IPv6ManagerCLI_$(Get-Date -Format 'yyyy-MM-dd_HH-mm').log" 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 Add-Content -Path $script:LogFilePath -Value "$ts $tag $Message" -Encoding UTF8 } # ===================================================================== # FONCTION : Initialize-ApiClient # Force TLS 1.2 et accepte les certificats auto-signes (equivalent curl -k) # ===================================================================== $script:CurlAvailable = $false function Initialize-ApiClient { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls 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 $script:CurlAvailable = [bool](Get-Command curl.exe -ErrorAction SilentlyContinue) } function Test-SslError { param([System.Exception]$Exception) $msg = $Exception.Message return ($msg -match "SSL" -or $msg -match "TLS" -or $msg -match "confiance" -or $msg -match "trust" -or $msg -match "certificate" -or $msg -match "envoi") } function Invoke-CurlGet { param( [string]$Url, [string]$Username, [AllowEmptyString()][string]$Password ) $output = & curl.exe -s -k -u "${Username}:${Password}" -H "Accept: application/json" $Url 2>&1 if ($LASTEXITCODE -ne 0) { throw "curl GET erreur (exit code $LASTEXITCODE): $output" } return ($output | Out-String) } function Invoke-CurlPost { param( [string]$Url, [string]$Username, [AllowEmptyString()][string]$Password, [string]$BodyJson ) $output = & curl.exe -s -k -u "${Username}:${Password}" -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d $BodyJson $Url 2>&1 if ($LASTEXITCODE -ne 0) { throw "curl POST erreur (exit code $LASTEXITCODE): $output" } return ($output | Out-String) } # ===================================================================== # 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" = "application/json" } } # ===================================================================== # FONCTIONS : API v2 - interfaces reseau IPv6 # ===================================================================== function Get-NetworkInterfacesUrl { param([string]$BaseUrl) return "$BaseUrl/api/rest/v2/services/platform/network/interfaces" } function Get-InterfaceIpv6Url { param([string]$BaseUrl, [string]$InterfaceName) return "$BaseUrl/api/rest/v2/services/platform/network/interfaces/$InterfaceName/ipv6" } function Invoke-NetworkInterfacesGet { param( [string]$Url, [string]$Username, [AllowEmptyString()][string]$Password ) $headers = Get-AuthHeader -Username $Username -Password $Password try { $response = Invoke-WebRequest -Uri $Url -Method GET -Headers $headers -UseBasicParsing -TimeoutSec 30 return $response.Content } catch { if ((Test-SslError -Exception $_.Exception) -and $script:CurlAvailable) { Write-Log -Message "Bascule sur curl.exe -k (echec TLS .NET)" -Level WARN return Invoke-CurlGet -Url $Url -Username $Username -Password $Password } throw } } function Invoke-InterfaceIpv6Post { param( [string]$Url, [string]$Username, [AllowEmptyString()][string]$Password, [bool]$Enabled ) $headers = Get-AuthHeader -Username $Username -Password $Password $bodyJson = (@{ enabled = $Enabled } | ConvertTo-Json -Compress) try { Invoke-WebRequest -Uri $Url -Method POST -Headers $headers -ContentType "application/json" -Body $bodyJson -UseBasicParsing -TimeoutSec 30 | Out-Null } catch { if ((Test-SslError -Exception $_.Exception) -and $script:CurlAvailable) { Write-Log -Message "Bascule sur curl.exe -k (echec TLS .NET)" -Level WARN Invoke-CurlPost -Url $Url -Username $Username -Password $Password -BodyJson $bodyJson | Out-Null return } throw } } function Get-Ipv6CapableInterfaces { param([PSCustomObject]$InterfacesObject) $result = @() foreach ($prop in $InterfacesObject.PSObject.Properties) { if ($prop.Value.PSObject.Properties.Name -contains "ipv6") { $result += [PSCustomObject]@{ Name = $prop.Name OldState = $prop.Value.ipv6.enabled } } } return $result } # ===================================================================== # INITIALISATION # ===================================================================== Initialize-ApiClient Write-Log -Message "=== IPv6ManagerCLI demarre - Action: $Action ===" -Level INFO $automates = Read-AutomateCsv -CsvPath $CsvInput $desiredState = ($Action -eq "Enable") $statsAutomates = 0 $statsInterfacesOk = 0 $statsInterfacesError = 0 $resultRows = @() # ===================================================================== # TRAITEMENT DE CHAQUE AUTOMATE # ===================================================================== foreach ($automate in $automates) { $hostname = $automate.Hostname $ip = $automate."Current Ip" $statsAutomates++ $baseUrl = Get-BaseUrl -Automate $automate if (-not $baseUrl) { Write-Log -Message "[$hostname] Aucun port HTTP/HTTPS valide - ignore" -Level WARN $resultRows += [PSCustomObject]@{ Hostname = $hostname; IP = $ip; Interface = "" AncienEtat = ""; NouvelEtat = "" Statut = "Erreur"; Message = "Aucun port HTTP/HTTPS valide" } continue } $creds = Get-Credentials -Automate $automate -DefaultUsername $Username -DefaultPassword $Password try { $interfacesUrl = Get-NetworkInterfacesUrl -BaseUrl $baseUrl Write-Log -Message "[$hostname] GET $interfacesUrl (user: $($creds.Username))" -Level INFO $content = Invoke-NetworkInterfacesGet -Url $interfacesUrl -Username $creds.Username -Password $creds.Password $interfaces = $content | ConvertFrom-Json $capableInterfaces = Get-Ipv6CapableInterfaces -InterfacesObject $interfaces Write-Log -Message "[$hostname] $($capableInterfaces.Count) interface(s) IPv6-capable(s) detectee(s)" -Level INFO foreach ($iface in $capableInterfaces) { $ipv6Url = Get-InterfaceIpv6Url -BaseUrl $baseUrl -InterfaceName $iface.Name try { Write-Log -Message "[$hostname] POST $ipv6Url (enabled=$desiredState)" -Level INFO Invoke-InterfaceIpv6Post -Url $ipv6Url -Username $creds.Username -Password $creds.Password -Enabled $desiredState Write-Log -Message "[$hostname] Interface '$($iface.Name)' IPv6 -> $Action" -Level OK $statsInterfacesOk++ $resultRows += [PSCustomObject]@{ Hostname = $hostname; IP = $ip; Interface = $iface.Name AncienEtat = $iface.OldState; NouvelEtat = $desiredState Statut = "Succes"; Message = "" } } catch { Write-Log -Message "[$hostname] ERREUR interface '$($iface.Name)' : $($_.Exception.Message)" -Level ERROR $statsInterfacesError++ $resultRows += [PSCustomObject]@{ Hostname = $hostname; IP = $ip; Interface = $iface.Name AncienEtat = $iface.OldState; NouvelEtat = $desiredState Statut = "Erreur"; Message = $_.Exception.Message } } } } catch { Write-Log -Message "[$hostname] ERREUR : $($_.Exception.Message)" -Level ERROR $resultRows += [PSCustomObject]@{ Hostname = $hostname; IP = $ip; Interface = "" AncienEtat = ""; NouvelEtat = "" Statut = "Erreur"; Message = $_.Exception.Message } } } # ===================================================================== # ECRITURE DU CSV DE RESULTAT # ===================================================================== $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm" $outputFile = Join-Path (Get-Location) "ipv6manager_$timestamp.csv" if ($resultRows.Count -gt 0) { $resultRows | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8 -Delimiter ";" Write-Log -Message "CSV de resultat ecrit : $outputFile ($($resultRows.Count) ligne(s))" -Level OK } else { Write-Log -Message "Aucune donnee a ecrire dans le CSV de resultat" -Level WARN } # ===================================================================== # RESUME FINAL # ===================================================================== Write-Log -Message "========== RESUME ==========" -Level INFO Write-Log -Message "Automates traites : $statsAutomates" -Level INFO Write-Log -Message "Interfaces modifiees avec succes : $statsInterfacesOk" -Level INFO Write-Log -Message "Interfaces en erreur : $statsInterfacesError" -Level $(if ($statsInterfacesError -gt 0) { "WARN" } else { "INFO" }) Write-Log -Message "============================" -Level INFO