387 lines
15 KiB
PowerShell
387 lines
15 KiB
PowerShell
<#
|
|
.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
|