PowerShell: Автоматический мониторинг и переподключение VPN на Windows

📅
⏱️ 4 min read
#windows-server #powershell #vpn

Недавно столкнулся с необходимостью поднять VPN между Windows Server и MikroTik. Казалось бы, настроил, подключил, всё работает. Но нет! В текущих реалиях подключение начало падать в совершенно рандомные моменты времени. То час работает, то через 15 минут отваливается. Пришлось делать костыли и костыльки. Получилось вот такое решение. 😅

Что за проблема?

VPN-туннель между Windows Server и MikroTik работал нестабильно. Причины могли быть разные:

  • Нестабильный интернет
  • Таймауты на стороне MikroTik
  • Особенности работы Windows RAS
  • Фаза луны и день недели (шутка, но иногда казалось именно так)

Ручками переподключать каждый раз надоело на второй день. Нужна была автоматизация.

Архитектура костылей

Решение состоит из трех скриптов:

  1. AutoConnectVPN.ps1 - основной скрипт, который коннектится к VPN и настраивает маршруты
  2. MonitorVPN.ps1 - следит за VPN и дергает первый скрипт, если что-то пошло не так
  3. Setup_MonitorVPN_Task.ps1 - создает задачу в планировщике Windows, которая запускает мониторинг каждые 2 минуты

Скрипт 1: AutoConnectVPN.ps1

Этот скрипт отвечает за подключение к VPN и настройку маршрутизации.

# ============================================
# КОНФИГУРАЦИЯ - измените под свои параметры
# ============================================
$VPN_NAME = "MikroTik-Domain"
$VPN_USERNAME = "vpnuser"
$VPN_PASSWORD = "vpnpassword"
$TARGET_NETWORK = "192.168.0.0"
$NETWORK_MASK = "255.255.255.0"
$VPN_GATEWAY = "172.16.66.1"
$TEST_IP = "192.168.0.254"
$LOG_FILE = "C:\Scripts\VPN_Connection.log"
$NETWORK_LOAD_DELAY = 15
$CONNECTION_DELAY = 5
$PING_DELAY = 2

# ============================================
# ОСНОВНОЙ КОД
# ============================================

# Логирование
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output "[$timestamp] Запуск скрипта автоподключения VPN" | Out-File $LOG_FILE -Append

# Ждём загрузки сетевых служб
Start-Sleep -Seconds $NETWORK_LOAD_DELAY
Write-Output "[$timestamp] Ожидание завершено" | Out-File $LOG_FILE -Append

# Подключаемся к VPN
try {
    $output = rasdial $VPN_NAME $VPN_USERNAME $VPN_PASSWORD 2>&1 | Out-String
    Write-Output "[$timestamp] Результат подключения: $output" | Out-File $LOG_FILE -Append
    Write-Output "[$timestamp] VPN подключен успешно" | Out-File $LOG_FILE -Append
} catch {
    Write-Output "[$timestamp] Исключение при подключении: $_" | Out-File $LOG_FILE -Append
}

# Ждём установки соединения
Start-Sleep -Seconds $CONNECTION_DELAY

# Удаляем старый маршрут если есть (игнорируем ошибки)
try {
    cmd /c "route DELETE $TARGET_NETWORK" 2>$null | Out-Null
    Write-Output "[$timestamp] Старый маршрут удалён" | Out-File $LOG_FILE -Append
} catch {
    Write-Output "[$timestamp] Старого маршрута не было" | Out-File $LOG_FILE -Append
}

# Добавляем маршрут
try {
    $routeOutput = cmd /c "route ADD $TARGET_NETWORK MASK $NETWORK_MASK $VPN_GATEWAY -p" 2>&1 | Out-String
    Write-Output "[$timestamp] Результат добавления маршрута: $routeOutput" | Out-File $LOG_FILE -Append
    Write-Output "[$timestamp] Маршрут добавлен успешно" | Out-File $LOG_FILE -Append
} catch {
    Write-Output "[$timestamp] Исключение при добавлении маршрута: $_" | Out-File $LOG_FILE -Append
}

# Проверяем маршрут
$routes = route PRINT | Select-String $TARGET_NETWORK
Write-Output "[$timestamp] Текущие маршруты к ${TARGET_NETWORK}:" | Out-File $LOG_FILE -Append
Write-Output "$routes" | Out-File $LOG_FILE -Append

# Проверяем связь
Start-Sleep -Seconds $PING_DELAY
if (Test-Connection -ComputerName $TEST_IP -Count 2 -Quiet) {
    Write-Output "[$timestamp] Проверка связи: SUCCESS (ping $TEST_IP)" | Out-File $LOG_FILE -Append
} else {
    Write-Output "[$timestamp] Проверка связи: FAILED (ping $TEST_IP)" | Out-File $LOG_FILE -Append
}

Write-Output "[$timestamp] Скрипт завершён" | Out-File $LOG_FILE -Append

      Важно! $VPN_NAME - это именно название вашего VPN-подключения, которое вы видите в "Сетевых подключениях" Windows. У меня это "MikroTik-Domain", у вас может быть "Офис", "VPN-Mikrotik" или что угодно еще.

Скрипт 2: MonitorVPN.ps1

Скрипт мониторинга проверяет состояние VPN и переподключается при необходимости.

# ============================================
# КОНФИГУРАЦИЯ - измените под свои параметры
# ============================================
$VPN_NAME = "MikroTik-Domain"
$TEST_IP = "192.168.0.254"
$LOG_FILE = "C:\Scripts\VPN_Monitor.log"
$RECONNECT_SCRIPT = "C:\Scripts\AutoConnectVPN.ps1"
$DISCONNECT_DELAY = 2
$LOG_SUCCESS_PROBABILITY = 30  # Логировать успешные проверки с вероятностью 1 к 30

# ============================================
# ОСНОВНОЙ КОД
# ============================================

# Логирование
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output "[$timestamp] Запустился монитор VPN" | Out-File $LOG_FILE -Append

# Проверяем статус VPN
$vpnStatus = rasdial | Select-String $VPN_NAME

if (-not $vpnStatus) {
    # VPN отключен - переподключаем
    Write-Output "[$timestamp] ⚠️ VPN ОТКЛЮЧЕН! Запуск переподключения..." | Out-File $LOG_FILE -Append
    
    # Отключаем на всякий случай
    rasdial $VPN_NAME /disconnect 2>$null | Out-Null
    Start-Sleep -Seconds $DISCONNECT_DELAY
    
    # Запускаем основной скрипт подключения
    try {
        & $RECONNECT_SCRIPT
        Write-Output "[$timestamp] ✅ Переподключение выполнено" | Out-File $LOG_FILE -Append
    } catch {
        Write-Output "[$timestamp] ❌ Ошибка переподключения: $_" | Out-File $LOG_FILE -Append
    }
} else {
    Write-Output "[$timestamp] ✓ VPN активен" | Out-File $LOG_FILE -Append
    
    # VPN активен - проверяем реальную связь
    if (Test-Connection -ComputerName $TEST_IP -Count 2 -Quiet) {
        # Связь есть - всё хорошо (логируем случайно, чтобы не переполнять лог)
        $random = Get-Random -Minimum 1 -Maximum $LOG_SUCCESS_PROBABILITY
        if ($random -eq 1) {
            Write-Output "[$timestamp] ✓ VPN активен, связь с $TEST_IP есть" | Out-File $LOG_FILE -Append
        }
    } else {
        # VPN подключен, но связи нет - переподключаем
        Write-Output "[$timestamp] ⚠️ VPN подключен, но нет связи с $TEST_IP! Переподключение..." | Out-File $LOG_FILE -Append
        
        rasdial $VPN_NAME /disconnect 2>$null | Out-Null
        Start-Sleep -Seconds $DISCONNECT_DELAY
        
        try {
            & $RECONNECT_SCRIPT
            Write-Output "[$timestamp] ✅ Переподключение выполнено" | Out-File $LOG_FILE -Append
        } catch {
            Write-Output "[$timestamp] ❌ Ошибка переподключения: $_" | Out-File $LOG_FILE -Append
        }
    }
}

Фишка в том, что скрипт проверяет не только статус VPN (он может показывать "подключено"), но и реально пингует хост на той стороне. Бывает, что VPN формально активен, а пакеты не ходят. Тут мы ловим и такие случаи!

Скрипт 3: Setup_MonitorVPN_Task.ps1

Этот скрипт создает задачу в планировщике Windows для автоматического запуска мониторинга.

# ============================================
# КОНФИГУРАЦИЯ - измените под свои параметры
# ============================================
$TASK_NAME = "MonitorVPN"
$MONITOR_SCRIPT_PATH = "C:\Scripts\MonitorVPN.ps1"
$CHECK_INTERVAL_MINUTES = 2
$EXECUTION_TIME_LIMIT_MINUTES = 1
$RESTART_COUNT = 3
$RESTART_INTERVAL_MINUTES = 1
$RUN_AS_USER = "SYSTEM"

# ============================================
# ОСНОВНОЙ КОД
# ============================================

# Удаляем старую задачу
Unregister-ScheduledTask -TaskName $TASK_NAME -Confirm:$false -ErrorAction SilentlyContinue

# Создаём действие
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" `
    -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$MONITOR_SCRIPT_PATH`""

# Триггер БЕЗ RepetitionDuration (бесконечное повторение)
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date
$trigger.Repetition = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $CHECK_INTERVAL_MINUTES) | Select-Object -ExpandProperty Repetition

$principal = New-ScheduledTaskPrincipal -UserId $RUN_AS_USER `
    -LogonType ServiceAccount -RunLevel Highest

$settings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable `
    -ExecutionTimeLimit (New-TimeSpan -Minutes $EXECUTION_TIME_LIMIT_MINUTES) `
    -RestartCount $RESTART_COUNT `
    -RestartInterval (New-TimeSpan -Minutes $RESTART_INTERVAL_MINUTES)

# Регистрируем задачу
Register-ScheduledTask -TaskName $TASK_NAME `
    -Action $action `
    -Trigger $trigger `
    -Principal $principal `
    -Settings $settings `
    -Description "Проверка VPN соединения каждые $CHECK_INTERVAL_MINUTES минуты и автоматическое переподключение" `
    -Force

Write-Host "✅ Задача $TASK_NAME создана успешно!" -ForegroundColor Green
Write-Host "Мониторинг запускается каждые $CHECK_INTERVAL_MINUTES минуты" -ForegroundColor Cyan

Установка и настройка

Шаг 1: Создаем папку для скриптов

New-Item -Path "C:\Scripts" -ItemType Directory -Force

Шаг 2: Копируем туда все три скрипта

  • C:\Scripts\AutoConnectVPN.ps1
  • C:\Scripts\MonitorVPN.ps1
  • C:\Scripts\Setup_MonitorVPN_Task.ps1

Шаг 3: Правим конфиги

В начале каждого скрипта есть секция КОНФИГУРАЦИЯ. Там меняем:

  • VPN_NAME - название вашего VPN-подключения (посмотрите в "Сетевых подключениях")
  • VPN_USERNAME и VPN_PASSWORD - ваши учетные данные для VPN
  • TARGET_NETWORK - сеть на той стороне туннеля (у меня 192.168.0.0/24)
  • VPN_GATEWAY - шлюз VPN-интерфейса (можно посмотреть через ipconfig когда VPN подключен)
  • TEST_IP - IP какого-нибудь хоста на той стороне, который точно должен пинговаться

Шаг 4: Запустите скрипт создания задачи

Откройте PowerShell от имени администратора и выполните:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
C:\Scripts\Setup_MonitorVPN_Task.ps1

5. Проверяем

Открываем планировщик задач (Win+Rtaskschd.msc) и ищем задачу MonitorVPN. Она должна запускаться каждые 2 минуты.

Как это работает в реальной жизни

  1. Планировщик Windows каждые 2 минуты запускает MonitorVPN.ps1
  2. Скрипт проверяет: а жив ли вообще VPN? (через rasdial)
  3. Если жив, то пингует хост на той стороне (у меня это 192.168.0.254)
  4. Если что-то не так - дергает AutoConnectVPN.ps1
  5. Тот переподключается к VPN и настраивает маршруты заново

Логи - наше всё

Все действия пишутся в логи:

  • C:\Scripts\VPN_Connection.log - что происходило при подключении
  • C:\Scripts\VPN_Monitor.log - что видел монитор

Смотреть логи в реальном времени:

Get-Content C:\Scripts\VPN_Monitor.log -Wait -Tail 20

Добавил фишку: успешные проверки логируются не каждый раз (а то лог распухнет), а случайно с вероятностью 1 к 30. Зато все проблемы логируются обязательно!

Что в итоге?

После установки этих скриптов прошло уже несколько недель. VPN всё так же иногда отваливается, но теперь я об этом даже не знаю - система сама переподключается за пару секунд. Красота! 🎉

Костыли? Да. Работает? Ещё как! Иногда самые простые решения - самые надежные.

P.S.

Если у вас IPsec вместо обычного L2TP/PPTP, можно переделать под него. Я делал версию и для IPsec - там немного другая логика подключения через Add-VpnConnection, но суть та же.  Сам скрипт можете найти в блоге, где то валяется, благо статей пока мало )