Maintaining uninterrupted VPN connections on Windows servers is critical for remote administration and secure communications. When connections drop due to server reboots, network issues, or manual disconnects, automated reconnection becomes essential.
Windows includes some basic persistence features:
rasdial "VPN Connection Name" username password /phonebook:%userprofile%\AppData\Roaming\Microsoft\Network\Connections\Pbk\rasphone.pbk
However, this only works for initial connection and doesn't handle drops.
This PowerShell script monitors and reconnects automatically:
# VPN Auto-Reconnect Script
$VPNName = "YourVPNConnectionName"
$username = "vpnuser"
$password = "securepassword"
while ($true) {
$connection = Get-VpnConnection -Name $VPNName
if ($connection.ConnectionStatus -ne "Connected") {
rasdial $VPNName $username $password
Start-Sleep -Seconds 10
}
Start-Sleep -Seconds 30
}
Create a scheduled task that runs at system startup:
# Create scheduled task
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\VPNReconnect.ps1"
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName "VPN Auto-Reconnect" -Action $action -Trigger $trigger -RunLevel Highest
Enhanced version with logging and retry logic:
function Write-Log {
param([string]$message)
Add-Content -Path "C:\VPNLog.txt" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $message"
}
$maxRetries = 5
$retryCount = 0
while ($true) {
try {
$connection = Get-VpnConnection -Name $VPNName -ErrorAction Stop
if ($connection.ConnectionStatus -ne "Connected") {
Write-Log "Attempting VPN connection..."
rasdial $VPNName $username $password | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Log "VPN connected successfully"
$retryCount = 0
} else {
$retryCount++
Write-Log "Connection failed (Attempt $retryCount)"
if ($retryCount -ge $maxRetries) {
Write-Log "Max retries reached, waiting before next attempt"
Start-Sleep -Seconds 300
$retryCount = 0
}
}
}
} catch {
Write-Log "Error: $_"
}
Start-Sleep -Seconds 30
}
For enterprise environments, consider tools like:
- AlwaysUp (commercial service manager)
- NSSM (Non-Sucking Service Manager)
- DameWare Remote Everywhere (enterprise solution)
Maintaining persistent VPN connections on Windows Server environments (2008 R2, 2012 R2, and newer) requires robust handling of various disconnection scenarios. The primary pain points include:
- Unexpected network interruptions
- Server reboots during maintenance windows
- Temporary credential expirations
- Manual disconnections by administrators
The built-in VPN client offers some persistence features through connection properties:
# Configure persistent connection in PowerShell
Add-VpnConnection -Name "CorporateVPN" -ServerAddress "vpn.example.com" \
-AllUserConnection -RememberCredential -IdleDisconnectSeconds 0 \
-CustomConfiguration "@{DnsSuffix=corp.example.com; UseRasCredentials=1}"
However, this doesn't cover all scenarios, particularly manual disconnections or credential rotations.
Create a scheduled task that runs this script every 5 minutes:
# VPN-Watchdog.ps1
$connectionName = "CorporateVPN"
$vpn = Get-VpnConnection -Name $connectionName -ErrorAction SilentlyContinue
if ($vpn.ConnectionStatus -ne "Connected") {
Write-Output "$(Get-Date) - VPN disconnected. Reconnecting..."
rasdial $connectionName /disconnect
Start-Sleep -Seconds 2
rasdial $connectionName
# For credential-based connections:
# rasdial $connectionName [username] [password]
} else {
Write-Output "$(Get-Date) - VPN connection healthy"
}
# Log results to event viewer
if ($vpn.ConnectionStatus -ne "Connected") {
Write-EventLog -LogName "Application" -Source "VPN Watchdog" -EntryType Information -EventId 1001 -Message "Reconnected VPN $connectionName"
}
For real-time monitoring without polling, use Windows Event Log triggers:
# Create event filter for VPN disconnections
$query = @'
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">
*[System[Provider[@Name='RasClient'] and (EventID=20267)]]
</Select>
</Query>
</QueryList>
'@
# Create the event subscription
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\VPN-Reconnect.ps1"
$trigger = New-ScheduledTaskTrigger -AtLogon -RandomDelay "00:00:30"
Register-ScheduledTask -TaskName "VPN Auto-Reconnect" -Action $action -Trigger $trigger -Description "Automatically reconnects VPN on disconnection" -User "SYSTEM"
For critical environments, consider these enhancements:
# Multi-interface failover script
$primaryVPN = "Primary-VPN"
$backupVPN = "Backup-VPN"
$testHost = "internal-server.corp.example.com"
if (-not (Test-Connection -ComputerName $testHost -Count 1 -Quiet)) {
Write-Output "Primary VPN failed, testing backup..."
rasdial $primaryVPN /disconnect
if (Test-Connection -ComputerName $testHost -Count 1 -Quiet) {
Write-Output "Backup VPN already active"
} else {
rasdial $backupVPN
Start-Sleep -Seconds 5
if (-not (Test-Connection -ComputerName $testHost -Count 1 -Quiet)) {
# Alert operations team
Send-MailMessage -To "noc@example.com" -From "vpnmonitor@example.com" -Subject "VPN Connection Failure" -Body "Both primary and backup VPN connections failed" -SmtpServer "smtp.corp.example.com"
}
}
}
When implementing automated VPN reconnections:
- Store credentials securely using Windows Credential Manager
- Implement appropriate certificate-based authentication where possible
- Set script execution policies to RemoteSigned or AllSigned
- Log all reconnection attempts for audit purposes
# Example of secure credential handling
$credential = Get-StoredCredential -Target "CorporateVPN"
rasdial "CorporateVPN" $credential.UserName $credential.GetNetworkCredential().Password
Enhance your solution with proper monitoring:
# Prometheus exporter for VPN status
$vpnStatus = (Get-VpnConnection -Name "CorporateVPN").ConnectionStatus
$metrics = @"
# HELP vpn_connection_status Current VPN connection state (0=disconnected, 1=connected)
# TYPE vpn_connection_status gauge
vpn_connection_status{name="CorporateVPN"} $(if ($vpnStatus -eq "Connected") {1} else {0})
"@
$metrics | Out-File "C:\metrics\vpn_metrics.prom" -Encoding utf8