$TsExe = "C:\Program Files\Tailscale\tailscale.exe"
$AuthKey = "tskey-auth-ТВОЙ_REUSABLE_КЛЮЧ"
$Host1 = "deka"
$LogFile = "C:\srv\ts-guard.log"
$Grace = 90
function Write-Log($m) {
if ((Test-Path $LogFile) -and ((Get-Item $LogFile).Length -gt 3MB)) {
Move-Item $LogFile "$LogFile.old" -Force -EA SilentlyContinue
}
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') $m" | Out-File $LogFile -Append -Encoding utf8
}
function Get-TsState {
try {
$raw = & $TsExe status --json 2>$null
if (-not $raw) { return [pscustomobject]@{State='NOREPLY'; Online=$false; Health=''} }
$j = $raw | ConvertFrom-Json
return [pscustomobject]@{
State = $j.BackendState
Online = [bool]$j.Self.Online
Health = (@($j.Health) -join ' | ')
}
} catch {
return [pscustomobject]@{State='NOREPLY'; Online=$false; Health='parse error'}
}
}
Write-Log "=== guard v3 started as $(whoami) ==="
$badSince = $null
$stage = 0
while ($true) {
$svc = Get-Service Tailscale -EA SilentlyContinue
if ($svc -and $svc.Status -ne 'Running') {
Write-Log "service $($svc.Status) -> start"
Start-Service Tailscale -EA SilentlyContinue
Start-Sleep -Seconds 15
}
$ts = Get-TsState
$healthy = ($ts.State -eq 'Running' -and $ts.Online)
if ($healthy) {
if ($stage -ne 0) { Write-Log "recovered at stage $stage" }
$badSince = $null; $stage = 0
Start-Sleep -Seconds 30
continue
}
if (-not $badSince) {
$badSince = Get-Date
Write-Log "unhealthy: state=$($ts.State) online=$($ts.Online) health=$($ts.Health)"
}
$down = ((Get-Date) - $badSince).TotalSeconds
if ($down -gt $Grace) {
if ($stage -eq 0) {
Write-Log "stage 1: restart service"
Restart-Service Tailscale -Force -EA SilentlyContinue
$stage = 1
Start-Sleep -Seconds 25
}
elseif ($stage -eq 1) {
Write-Log "stage 2: logout+login"
& $TsExe logout 2>&1 | ForEach-Object { Write-Log "logout: $_" }
Start-Sleep -Seconds 5
& $TsExe login --authkey=$AuthKey --hostname=$Host1 2>&1 | ForEach-Object { Write-Log "login: $_" }
Start-Sleep -Seconds 10
& $TsExe set --unattended --accept-dns=false 2>&1 | ForEach-Object { Write-Log "set: $_" }
$stage = 2
Start-Sleep -Seconds 20
}
else {
$t = Get-TsState
Write-Log "still unhealthy: state=$($t.State) online=$($t.Online) health=$($t.Health) — resetting cycle"
$badSince = $null; $stage = 0
}
}
Start-Sleep -Seconds 10
}