powershell export vpn gateway

<pre class="wp-block-syntaxhighlighter-code">

<#
.SYNOPSIS
    Extrait la configuration complète d'un vServer VPN Gateway NetScaler pour migration

.DESCRIPTION
    Ce script parse la configuration NetScaler et extrait tous les objets liés à un vServer VPN Gateway

.PARAMETER ConfigFile
    Chemin vers le fichier ns.conf (par défaut: c:\logs\ns.conf)
    
.PARAMETER VServerName
    Nom du vServer VPN Gateway à analyser
    
.PARAMETER OutputPath
    Chemin où sauvegarder le rapport (optionnel)
    
.PARAMETER ExportCommands
    Si spécifié, génère aussi les commandes CLI pour recréer la configuration
    
.EXAMPLE
    .\Export-NetScalerVPNGatewayConfig.ps1 -VServerName "VPNGW_Production"
    
.EXAMPLE
    .\Export-NetScalerVPNGatewayConfig.ps1 -VServerName "VPNGW_Prod" -OutputPath "C:\logs\" -ExportCommands
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory=$false)]
    [string]$ConfigFile = "c:\logs\ns.conf",
    
    [Parameter(Mandatory=$true)]
    [string]$VServerName,
    
    [Parameter(Mandatory=$false)]
    [string]$OutputPath,
    
    [Parameter(Mandatory=$false)]
    [switch]$ExportCommands
)

class VPNGatewayConfig {
    [string]$VServerName
    [hashtable]$VServerConfig
    [System.Collections.ArrayList]$SSLCertificates
    [System.Collections.ArrayList]$AuthenticationPolicies
    [System.Collections.ArrayList]$AuthenticationActions
    [System.Collections.ArrayList]$AuthenticationServers
    [System.Collections.ArrayList]$SessionPolicies
    [System.Collections.ArrayList]$SessionActions
    [System.Collections.ArrayList]$SessionProfiles
    [System.Collections.ArrayList]$EPAPolicies
    [System.Collections.ArrayList]$STAServers
    
    VPNGatewayConfig() {
        $this.SSLCertificates = New-Object System.Collections.ArrayList
        $this.AuthenticationPolicies = New-Object System.Collections.ArrayList
        $this.AuthenticationActions = New-Object System.Collections.ArrayList
        $this.AuthenticationServers = New-Object System.Collections.ArrayList
        $this.SessionPolicies = New-Object System.Collections.ArrayList
        $this.SessionActions = New-Object System.Collections.ArrayList
        $this.SessionProfiles = New-Object System.Collections.ArrayList
        $this.EPAPolicies = New-Object System.Collections.ArrayList
        $this.STAServers = New-Object System.Collections.ArrayList
    }
}

function Read-NetScalerConfig {
    param([string]$FilePath)
    
    if (-not (Test-Path $FilePath)) {
        throw "Fichier de configuration non trouvé : $FilePath"
    }
    
    Write-Host "Lecture de la configuration NetScaler..." -ForegroundColor Cyan
    $content = Get-Content $FilePath -Raw
    return $content
}

function Get-VServerConfiguration {
    param([string]$Config, [string]$VServerName)
    
    Write-Host "Recherche du vServer VPN Gateway : $VServerName" -ForegroundColor Cyan
    
    $vserverPattern = "add vpn vserver $VServerName (.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
    
    if ($Config -match $vserverPattern) {
        $vserverConfig = @{
            Name = $VServerName
            FullCommand = $Matches[0]
            Parameters = $Matches[1]
        }
        
        if ($vserverConfig.Parameters -match "(\d+\.\d+\.\d+\.\d+)") {
            $vserverConfig.IPAddress = $Matches[1]
        }
        if ($vserverConfig.Parameters -match "\s+(\d+)\s") {
            $vserverConfig.Port = $Matches[1]
        }
        
        Write-Host "  vServer trouvé : $($vserverConfig.IPAddress):$($vserverConfig.Port)" -ForegroundColor Green
        return $vserverConfig
    }
    
    throw "vServer VPN Gateway '$VServerName' non trouvé dans la configuration"
}

function Get-VServerBindings {
    param([string]$Config, [string]$VServerName)
    
    Write-Host "Extraction des bindings du vServer..." -ForegroundColor Cyan
    
    $bindings = @{
        SSLCertKeys = @()
        AuthPolicies = @()
        SessionPolicies = @()
        STAServers = @()
    }
    
    $sslPattern = "bind ssl vserver $VServerName\s+-certkeyName\s+(\S+)"
    $sslMatches = [regex]::Matches($Config, $sslPattern)
    foreach ($match in $sslMatches) {
        $bindings.SSLCertKeys += $match.Groups[1].Value
    }
    Write-Host "  Certificats SSL : $($bindings.SSLCertKeys.Count)" -ForegroundColor Yellow
    
    $authPattern = "bind vpn vserver $VServerName\s+-policy\s+(\S+)\s+-priority\s+(\d+)(?:\s+-secondary)?(?:\s+-groupExtraction\s+(\S+))?"
    $authMatches = [regex]::Matches($Config, $authPattern)
    foreach ($match in $authMatches) {
        $bindings.AuthPolicies += @{
            Name = $match.Groups[1].Value
            Priority = $match.Groups[2].Value
            GroupExtraction = $match.Groups[3].Value
        }
    }
    Write-Host "  Authentication Policies : $($bindings.AuthPolicies.Count)" -ForegroundColor Yellow
    
    $sessionPattern = "bind vpn vserver $VServerName\s+-policy\s+(\S+)\s+-priority\s+(\d+)(?:\s+-type\s+(\S+))?"
    $sessionMatches = [regex]::Matches($Config, $sessionPattern)
    foreach ($match in $sessionMatches) {
        $policyName = $match.Groups[1].Value
        if ($bindings.AuthPolicies.Name -notcontains $policyName) {
            $bindings.SessionPolicies += @{
                Name = $policyName
                Priority = $match.Groups[2].Value
                Type = $match.Groups[3].Value
            }
        }
    }
    Write-Host "  Session Policies : $($bindings.SessionPolicies.Count)" -ForegroundColor Yellow
    
    $staPattern = "bind vpn vserver $VServerName\s+-staServer\s+(\S+)"
    $staMatches = [regex]::Matches($Config, $staPattern)
    foreach ($match in $staMatches) {
        $bindings.STAServers += $match.Groups[1].Value
    }
    Write-Host "  STA Servers : $($bindings.STAServers.Count)" -ForegroundColor Yellow
    
    return $bindings
}

function Get-SSLCertificateChain {
    param([string]$Config, [string]$CertKeyName, [System.Collections.ArrayList]$ProcessedCerts = (New-Object System.Collections.ArrayList))
    
    if ($ProcessedCerts -contains $CertKeyName) {
        return @()
    }
    
    $certs = @()
    [void]$ProcessedCerts.Add($CertKeyName)
    
    $certPattern = "add ssl certKey $CertKeyName\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
    if ($Config -match $certPattern) {
        $certConfig = @{
            Name = $CertKeyName
            FullCommand = $Matches[0]
            Parameters = $Matches[1]
        }
        
        if ($certConfig.Parameters -match "-cert\s+""?([^""\s]+)""?") {
            $certConfig.CertFile = $Matches[1]
        }
        if ($certConfig.Parameters -match "-key\s+""?([^""\s]+)""?") {
            $certConfig.KeyFile = $Matches[1]
        }
        
        $certs += $certConfig
        
        $linkPattern = "link ssl certKey $CertKeyName\s+(\S+)"
        if ($Config -match $linkPattern) {
            $linkedCert = $Matches[1]
            $certs += Get-SSLCertificateChain -Config $Config -CertKeyName $linkedCert -ProcessedCerts $ProcessedCerts
        }
    }
    
    return $certs
}

function Get-AuthenticationPolicyDetails {
    param([string]$Config, [string]$PolicyName)
    
    $policyPattern = "add (?:authentication|vpn) (?:saml|ldap|radius|cert|negotiate|webAuth)?Policy $PolicyName\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
    
    if ($Config -match $policyPattern) {
        $policyConfig = @{
            Name = $PolicyName
            Type = "Authentication"
            FullCommand = $Matches[0]
            Parameters = $Matches[1]
        }
        
        if ($policyConfig.Parameters -match "-action\s+(\S+)") {
            $policyConfig.Action = $Matches[1]
        }
        if ($policyConfig.Parameters -match "-reqAction\s+(\S+)") {
            $policyConfig.ReqAction = $Matches[1]
        }
        if ($policyConfig.Parameters -match "-rule\s+(.+?)(?=\s+-(?:action|reqAction)|$)") {
            $policyConfig.Rule = $Matches[1].Trim()
        }
        
        return $policyConfig
    }
    
    return $null
}

function Get-AuthenticationActionDetails {
    param([string]$Config, [string]$ActionName)
    
    $actionTypes = @("ldap", "radius", "saml", "cert", "negotiate", "webAuth", "tacacsAction", "dfaAction", "oauthAction", "webAuthAction", "epaAction")
    
    foreach ($type in $actionTypes) {
        $actionPattern = "add authentication ${type}Action $ActionName\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
        
        if ($Config -match $actionPattern) {
            $actionConfig = @{
                Name = $ActionName
                Type = $type
                FullCommand = $Matches[0]
                Parameters = $Matches[1]
            }
            
            if ($actionConfig.Parameters -match "-serverName\s+(\S+)") {
                $actionConfig.ServerName = $Matches[1]
            }
            if ($actionConfig.Parameters -match "-serverIP\s+(\S+)") {
                $actionConfig.ServerIP = $Matches[1]
            }
            
            return $actionConfig
        }
    }
    
    return $null
}

function Get-AuthenticationServerDetails {
    param([string]$Config, [string]$ServerName)
    
    $serverTypes = @("ldap", "radius", "tacacs", "saml")
    
    foreach ($type in $serverTypes) {
        $serverPattern = "add authentication ${type}Action $ServerName\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
        
        if ($Config -match $serverPattern) {
            return @{
                Name = $ServerName
                Type = $type
                FullCommand = $Matches[0]
                Parameters = $Matches[1]
            }
        }
    }
    
    return $null
}

function Get-SessionPolicyDetails {
    param([string]$Config, [string]$PolicyName)
    
    $policyPattern = "add vpn sessionPolicy $PolicyName\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
    
    if ($Config -match $policyPattern) {
        $policyConfig = @{
            Name = $PolicyName
            Type = "Session"
            FullCommand = $Matches[0]
            Parameters = $Matches[1]
        }
        
        if ($policyConfig.Parameters -match "-action\s+(\S+)") {
            $policyConfig.Action = $Matches[1]
        }
        if ($policyConfig.Parameters -match "^""?(.+?)""?\s+-action") {
            $policyConfig.Expression = $Matches[1].Trim()
        }
        
        return $policyConfig
    }
    
    return $null
}

function Get-SessionActionDetails {
    param([string]$Config, [string]$ActionName)
    
    $actionPattern = "add vpn sessionAction $ActionName\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
    
    if ($Config -match $actionPattern) {
        $actionConfig = @{
            Name = $ActionName
            Type = "SessionAction"
            FullCommand = $Matches[0]
            Parameters = $Matches[1]
        }
        
        if ($actionConfig.Parameters -match "-profileName\s+(\S+)") {
            $actionConfig.ProfileName = $Matches[1]
        }
        
        return $actionConfig
    }
    
    return $null
}

function Get-SessionProfileDetails {
    param([string]$Config, [string]$ProfileName)
    
    $profilePattern = "add vpn sessionProfile $ProfileName\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
    
    if ($Config -match $profilePattern) {
        return @{
            Name = $ProfileName
            Type = "SessionProfile"
            FullCommand = $Matches[0]
            Parameters = $Matches[1]
        }
    }
    
    return $null
}

function Get-EPAPolicyDetails {
    param([string]$Config)
    
    $epaPattern = "add vpn epaProfile\s+(\S+)\s+(.+?)(?=\r?\n(?:add|bind|set|enable|disable|#|\z))"
    $epaMatches = [regex]::Matches($Config, $epaPattern)
    
    $epaProfiles = @()
    foreach ($match in $epaMatches) {
        $epaProfiles += @{
            Name = $match.Groups[1].Value
            Type = "EPAProfile"
            FullCommand = $match.Value
            Parameters = $match.Groups[2].Value
        }
    }
    
    return $epaProfiles
}

function Export-VPNGatewayConfiguration {
    param([string]$ConfigFile, [string]$VServerName)
    
    $config = Read-NetScalerConfig -FilePath $ConfigFile
    $vpnConfig = [VPNGatewayConfig]::new()
    $vpnConfig.VServerName = $VServerName
    
    Write-Host "`n===============================================" -ForegroundColor Magenta
    Write-Host "ANALYSE DU VSERVER VPN GATEWAY" -ForegroundColor Magenta
    Write-Host "===============================================" -ForegroundColor Magenta
    $vpnConfig.VServerConfig = Get-VServerConfiguration -Config $config -VServerName $VServerName
    
    Write-Host "`n===============================================" -ForegroundColor Magenta
    Write-Host "ANALYSE DES BINDINGS" -ForegroundColor Magenta
    Write-Host "===============================================" -ForegroundColor Magenta
    $bindings = Get-VServerBindings -Config $config -VServerName $VServerName
    
    if ($bindings.SSLCertKeys.Count -gt 0) {
        Write-Host "`nExtraction des certificats SSL..." -ForegroundColor Cyan
        $processedCerts = New-Object System.Collections.ArrayList
        foreach ($certName in $bindings.SSLCertKeys) {
            $certChain = Get-SSLCertificateChain -Config $config -CertKeyName $certName -ProcessedCerts $processedCerts
            foreach ($cert in $certChain) {
                [void]$vpnConfig.SSLCertificates.Add($cert)
            }
        }
        Write-Host "  Total certificats : $($vpnConfig.SSLCertificates.Count)" -ForegroundColor Green
    }
    
    if ($bindings.AuthPolicies.Count -gt 0) {
        Write-Host "`nExtraction des Authentication Policies..." -ForegroundColor Cyan
        foreach ($policyBinding in $bindings.AuthPolicies) {
            $policy = Get-AuthenticationPolicyDetails -Config $config -PolicyName $policyBinding.Name
            if ($policy) {
                $policy.Priority = $policyBinding.Priority
                $policy.GroupExtraction = $policyBinding.GroupExtraction
                [void]$vpnConfig.AuthenticationPolicies.Add($policy)
                
                if ($policy.Action) {
                    $action = Get-AuthenticationActionDetails -Config $config -ActionName $policy.Action
                    if ($action -and ($vpnConfig.AuthenticationActions.Name -notcontains $action.Name)) {
                        [void]$vpnConfig.AuthenticationActions.Add($action)
                        
                        if ($action.ServerName) {
                            $server = Get-AuthenticationServerDetails -Config $config -ServerName $action.ServerName
                            if ($server -and ($vpnConfig.AuthenticationServers.Name -notcontains $server.Name)) {
                                [void]$vpnConfig.AuthenticationServers.Add($server)
                            }
                        }
                    }
                }
            }
        }
        Write-Host "  Policies: $($vpnConfig.AuthenticationPolicies.Count) | Actions: $($vpnConfig.AuthenticationActions.Count) | Servers: $($vpnConfig.AuthenticationServers.Count)" -ForegroundColor Green
    }
    
    if ($bindings.SessionPolicies.Count -gt 0) {
        Write-Host "`nExtraction des Session Policies..." -ForegroundColor Cyan
        foreach ($policyBinding in $bindings.SessionPolicies) {
            $policy = Get-SessionPolicyDetails -Config $config -PolicyName $policyBinding.Name
            if ($policy) {
                $policy.Priority = $policyBinding.Priority
                [void]$vpnConfig.SessionPolicies.Add($policy)
                
                if ($policy.Action) {
                    $action = Get-SessionActionDetails -Config $config -ActionName $policy.Action
                    if ($action -and ($vpnConfig.SessionActions.Name -notcontains $action.Name)) {
                        [void]$vpnConfig.SessionActions.Add($action)
                        
                        if ($action.ProfileName) {
                            $profile = Get-SessionProfileDetails -Config $config -ProfileName $action.ProfileName
                            if ($profile -and ($vpnConfig.SessionProfiles.Name -notcontains $profile.Name)) {
                                [void]$vpnConfig.SessionProfiles.Add($profile)
                            }
                        }
                    }
                }
            }
        }
        Write-Host "  Policies: $($vpnConfig.SessionPolicies.Count) | Actions: $($vpnConfig.SessionActions.Count) | Profiles: $($vpnConfig.SessionProfiles.Count)" -ForegroundColor Green
    }
    
    if ($bindings.STAServers.Count -gt 0) {
        foreach ($sta in $bindings.STAServers) {
            [void]$vpnConfig.STAServers.Add(@{URL = $sta})
        }
    }
    
    Write-Host "`nExtraction des EPA Profiles..." -ForegroundColor Cyan
    $epaProfiles = Get-EPAPolicyDetails -Config $config
    foreach ($epa in $epaProfiles) {
        [void]$vpnConfig.EPAPolicies.Add($epa)
    }
    if ($vpnConfig.EPAPolicies.Count -gt 0) {
        Write-Host "  EPA Profiles : $($vpnConfig.EPAPolicies.Count)" -ForegroundColor Green
    }
    
    return $vpnConfig
}

function Format-ConfigurationReport {
    param([VPNGatewayConfig]$VPNConfig)
    
    $report = @"

===============================================================================
                 RAPPORT DE CONFIGURATION VPN GATEWAY
                         NetScaler / Citrix ADC
===============================================================================

Date : $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
vServer : $($VPNConfig.VServerName)

-------------------------------------------------------------------------------
VSERVER
-------------------------------------------------------------------------------

Nom   : $($VPNConfig.VServerConfig.Name)
IP    : $($VPNConfig.VServerConfig.IPAddress)
Port  : $($VPNConfig.VServerConfig.Port)

"@

    if ($VPNConfig.SSLCertificates.Count -gt 0) {
        $report += "`n-------------------------------------------------------------------------------`n"
        $report += "CERTIFICATS SSL ($($VPNConfig.SSLCertificates.Count))`n"
        $report += "-------------------------------------------------------------------------------`n`n"
        foreach ($cert in $VPNConfig.SSLCertificates) {
            $report += "Certificat : $($cert.Name)`n"
            $report += "  Cert : $($cert.CertFile)`n"
            $report += "  Key  : $($cert.KeyFile)`n`n"
        }
    }
    
    if ($VPNConfig.AuthenticationPolicies.Count -gt 0) {
        $report += "`n-------------------------------------------------------------------------------`n"
        $report += "AUTHENTICATION POLICIES ($($VPNConfig.AuthenticationPolicies.Count))`n"
        $report += "-------------------------------------------------------------------------------`n`n"
        foreach ($policy in $VPNConfig.AuthenticationPolicies | Sort-Object {[int]$_.Priority}) {
            $report += "Policy : $($policy.Name) (Priorité: $($policy.Priority))`n"
            $report += "  Action : $($policy.Action)`n"
            $report += "  Rule   : $($policy.Rule)`n`n"
        }
    }
    
    if ($VPNConfig.SessionPolicies.Count -gt 0) {
        $report += "`n-------------------------------------------------------------------------------`n"
        $report += "SESSION POLICIES ($($VPNConfig.SessionPolicies.Count))`n"
        $report += "-------------------------------------------------------------------------------`n`n"
        foreach ($policy in $VPNConfig.SessionPolicies | Sort-Object {[int]$_.Priority}) {
            $report += "Policy : $($policy.Name) (Priorité: $($policy.Priority))`n"
            $report += "  Action : $($policy.Action)`n`n"
        }
    }
    
    $report += "`n-------------------------------------------------------------------------------`n"
    $report += "RÉSUMÉ`n"
    $report += "-------------------------------------------------------------------------------`n`n"
    $report += "Certificats SSL         : $($VPNConfig.SSLCertificates.Count)`n"
    $report += "Authentication Policies : $($VPNConfig.AuthenticationPolicies.Count)`n"
    $report += "Authentication Actions  : $($VPNConfig.AuthenticationActions.Count)`n"
    $report += "Session Policies        : $($VPNConfig.SessionPolicies.Count)`n"
    $report += "Session Actions         : $($VPNConfig.SessionActions.Count)`n"
    $report += "Session Profiles        : $($VPNConfig.SessionProfiles.Count)`n"
    $report += "EPA Profiles            : $($VPNConfig.EPAPolicies.Count)`n"
    $report += "STA Servers             : $($VPNConfig.STAServers.Count)`n"
    $report += "`n===============================================================================`n"
    
    return $report
}

function Export-RecreationCommands {
    param([VPNGatewayConfig]$VPNConfig)
    
    $commands = "# ========================================================================`n"
    $commands += "# SCRIPT DE RECRÉATION - VPN Gateway : $($VPNConfig.VServerName)`n"
    $commands += "# Généré le : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n"
    $commands += "# ========================================================================`n`n"
    
    $commands += "# ÉTAPE 1 : Certificats SSL`n`n"
    foreach ($cert in $VPNConfig.SSLCertificates) {
        $commands += "$($cert.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 2 : Authentication Servers`n`n"
    foreach ($server in $VPNConfig.AuthenticationServers) {
        $commands += "$($server.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 3 : Authentication Actions`n`n"
    foreach ($action in $VPNConfig.AuthenticationActions) {
        $commands += "$($action.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 4 : Authentication Policies`n`n"
    foreach ($policy in $VPNConfig.AuthenticationPolicies) {
        $commands += "$($policy.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 5 : Session Profiles`n`n"
    foreach ($profile in $VPNConfig.SessionProfiles) {
        $commands += "$($profile.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 6 : Session Actions`n`n"
    foreach ($action in $VPNConfig.SessionActions) {
        $commands += "$($action.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 7 : Session Policies`n`n"
    foreach ($policy in $VPNConfig.SessionPolicies) {
        $commands += "$($policy.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 8 : EPA Profiles`n`n"
    foreach ($epa in $VPNConfig.EPAPolicies) {
        $commands += "$($epa.FullCommand)`n"
    }
    
    $commands += "`n# ÉTAPE 9 : vServer VPN Gateway`n`n"
    $commands += "$($VPNConfig.VServerConfig.FullCommand)`n"
    
    $commands += "`n# ÉTAPE 10 : Bindings`n`n"
    foreach ($policy in $VPNConfig.AuthenticationPolicies | Sort-Object {[int]$_.Priority}) {
        $bindCmd = "bind vpn vserver $($VPNConfig.VServerName) -policy $($policy.Name) -priority $($policy.Priority)"
        if ($policy.GroupExtraction) {
            $bindCmd += " -groupExtraction $($policy.GroupExtraction)"
        }
        $commands += "$bindCmd`n"
    }
    
    foreach ($policy in $VPNConfig.SessionPolicies | Sort-Object {[int]$_.Priority}) {
        $commands += "bind vpn vserver $($VPNConfig.VServerName) -policy $($policy.Name) -priority $($policy.Priority)`n"
    }
    
    return $commands
}

try {
    Write-Host "`n===============================================================================" -ForegroundColor Cyan
    Write-Host "  NetScaler VPN Gateway Configuration Extractor" -ForegroundColor Cyan
    Write-Host "===============================================================================`n" -ForegroundColor Cyan
    
    $vpnConfig = Export-VPNGatewayConfiguration -ConfigFile $ConfigFile -VServerName $VServerName
    $report = Format-ConfigurationReport -VPNConfig $vpnConfig
    Write-Host $report
    
    if ($OutputPath) {
        $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
        $reportFile = Join-Path $OutputPath "VPNGateway_$($VServerName)_Report_$timestamp.txt"
        $report | Out-File -FilePath $reportFile -Encoding UTF8
        Write-Host "Rapport sauvegardé : $reportFile" -ForegroundColor Green
        
        if ($ExportCommands) {
            $commandsFile = Join-Path $OutputPath "VPNGateway_$($VServerName)_Commands_$timestamp.txt"
            $commands = Export-RecreationCommands -VPNConfig $vpnConfig
            $commands | Out-File -FilePath $commandsFile -Encoding UTF8
            Write-Host "Commandes sauvegardées : $commandsFile" -ForegroundColor Green
        }
    }
    
    Write-Host "`nExtraction terminée.`n" -ForegroundColor Green
    
} catch {
    Write-Host "`nERREUR : $($_.Exception.Message)" -ForegroundColor Red
    Write-Host $_.ScriptStackTrace -ForegroundColor Red
    exit 1
}
</pre>