# Failover Mikrotik Fase 1 — ISP1 primary, ISP2 backup (check-gateway) # Usage: .\scripts\apply-failover-mikrotik.ps1 # .\scripts\apply-failover-mikrotik.ps1 -DryRun # .\scripts\apply-failover-mikrotik.ps1 -TestFailover # tes singkat failover param( [string]$HostAddr = "10.100.1.1", [switch]$DryRun, [switch]$TestFailover ) $SshPort = 255 $SshUser = "admin" $KeyFile = Join-Path $env:USERPROFILE ".ssh\id_rsa_mikrotik" $RscLocal = Join-Path (Split-Path $PSScriptRoot -Parent) "server\mikrotik-failover.rsc" $RscRemote = "failover.rsc" $SshBase = @( "-o", "BatchMode=yes", "-o", "ConnectTimeout=15", "-o", "IdentitiesOnly=yes", "-i", $KeyFile, "-o", "PubkeyAcceptedAlgorithms=rsa-sha2-256,rsa-sha2-512,ssh-rsa", "-o", "StrictHostKeyChecking=accept-new", "-m", "hmac-sha1", "-p", $SshPort, "${SshUser}@${HostAddr}" ) function Invoke-Mikrotik { param([string]$Cmd) if ($DryRun) { Write-Host "[dry-run] $Cmd" return $true } $out = & ssh @SshBase $Cmd 2>&1 foreach ($line in $out) { Write-Host $line } return ($LASTEXITCODE -eq 0) } Write-Host "=== Apply Failover Mikrotik (Fase 1) ===" Write-Host "Target: $HostAddr" if (-not $DryRun) { Write-Host "`n--- Fase 0: Backup config ---" if (-not (Invoke-Mikrotik "/export file=pre-failover-backup")) { throw "Backup export gagal" } Write-Host "`n--- Fase 1-3: Upload & import failover.rsc ---" & scp -P $SshPort -o MACs=hmac-sha1 -o StrictHostKeyChecking=accept-new ` -i $KeyFile $RscLocal "${SshUser}@${HostAddr}:$RscRemote" if ($LASTEXITCODE -ne 0) { throw "SCP gagal" } if (-not (Invoke-Mikrotik "/import file=$RscRemote")) { throw "Import failover gagal" } Invoke-Mikrotik "/file remove $RscRemote" | Out-Null Write-Host "`n--- Tunggu DHCP ISP2 (max 30s) ---" $bound = $false for ($i = 0; $i -lt 6; $i++) { Start-Sleep -Seconds 5 $dhcp = & ssh @SshBase '/ip dhcp-client print detail where interface=sfp-sfpplus1' 2>&1 if ($dhcp -match 'status=bound') { $bound = $true; break } Write-Host " ... menunggu DHCP ($($i+1)/6)" } if (-not $bound) { Write-Warning "DHCP ISP2 belum bound — cek kabel/modem ISP2" } } Write-Host "`n=== Verifikasi ===" $checks = @( '/interface ethernet print where name=sfp-sfpplus1', '/ip dhcp-client print where interface=sfp-sfpplus1', '/ping 192.168.1.1 count=3', '/ip route print detail where dst-address=0.0.0.0/0', '/ip firewall nat print where chain=srcnat', '/ping 8.8.8.8 count=3' ) foreach ($c in $checks) { Write-Host "--- $c ---" Invoke-Mikrotik $c | Out-Null Write-Host "" } if ($TestFailover -and -not $DryRun) { Write-Host "=== Tes failover terkontrol (disable route ISP1 sementara) ===" Invoke-Mikrotik '/ip route disable [find comment="failover ISP1 primary"]' | Out-Null Start-Sleep -Seconds 3 Write-Host "--- Ping internet via ISP2 ---" Invoke-Mikrotik '/ping 8.8.8.8 count=3' | Out-Null Invoke-Mikrotik '/ip route enable [find comment="failover ISP1 primary"]' | Out-Null Write-Host "Route ISP1 di-enable kembali." } Write-Host "Selesai. Rollback: .\scripts\rollback-single-wan-mikrotik.ps1"