#requires -Version 5.1 #requires -RunAsAdministrator <# .SYNOPSIS One-time setup для windows-host daily fallback backup → kreknin. Run on windows-host (DESKTOP-NSEF0UK) as Administrator. Idempotent. .DESCRIPTION 1. Generate ed25519 SSH key (C:\ProgramData\backup\kreknin-key). 2. Install rclone (single .exe). 3. Write rclone.conf (SFTP remote `kreknin`, no host-key validation). 4. Write config.env (ntfy + SMTP + MSSQL SA creds; ACL SYSTEM+Administrators). 5. Deploy run.ps1. 6. Register ScheduledTask 'WindowsHost-Backup-Daily' daily 03:00 MSK as SYSTEM, Wake-To-Run enabled чтобы запускался даже если машина в standby. User action required between step 1 and 6: добавить kreknin-key.pub в /volume1/homes/vitya/.ssh/authorized_keys на kreknin Synology. (Alt: dispatch via VDS pivot — см. .tasks/windows-host-fallback-backup-daily.md) #> [CmdletBinding()] param( [string]$NtfyUser = 'vitya', [string]$NtfyPass = 'Pryakhin9', [string]$NtfyUrl = 'https://ntfy.vds.kzntsv.site', [string]$NtfyTopic = 'vds-backup', [string]$SmtpHost = 'smtp.yandex.ru', [int]$SmtpPort = 587, [string]$SmtpFrom = 'noreply@snolla.com', [string]$SmtpUser = 'noreply@snolla.com', [Parameter(Mandatory=$true)][string]$SmtpPass, [Parameter(Mandatory=$true)][string]$MssqlSaPass, [string]$OpsNotifyEmail = 'vitya.kuznetsov@gmail.com', [string]$ScheduleAt = '03:00' ) $ErrorActionPreference = 'Stop' $base = 'C:\ProgramData\backup' New-Item -ItemType Directory -Path $base -Force | Out-Null # Locale-safe Administrators SID (RU/EN Windows compatible) $adminSid = '*S-1-5-32-544' # 1. SSH key $keyPath = "$base\kreknin-key" if (-not (Test-Path $keyPath)) { ssh-keygen -t ed25519 -f $keyPath -N '""' -C 'windows-host-backup' -q & cmd.exe /c "icacls `"$keyPath`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null Write-Host "[OK] SSH key generated: $keyPath" Write-Host "" Write-Host "ADD THIS PUBKEY TO KREKNIN /var/services/homes/vitya/.ssh/authorized_keys:" Get-Content "$keyPath.pub" Write-Host "" Read-Host "Press Enter after pubkey deployed to kreknin" } else { Write-Host "[SKIP] SSH key exists: $keyPath" } # 2. rclone — installed in ProgramData (не Program Files), не нужен elevated install $rcloneExe = "$base\rclone.exe" if (-not (Test-Path $rcloneExe)) { $ver = (Invoke-RestMethod 'https://downloads.rclone.org/version.txt' -UseBasicParsing).Trim() -replace '^rclone\s+v|^v','' $url = "https://downloads.rclone.org/v$ver/rclone-v$ver-windows-amd64.zip" $tmp = "$env:TEMP\rclone.zip" Invoke-WebRequest $url -OutFile $tmp -UseBasicParsing if (Test-Path "$env:TEMP\rclone") { Remove-Item "$env:TEMP\rclone" -Recurse -Force } Expand-Archive $tmp -DestinationPath "$env:TEMP\rclone" -Force Copy-Item "$env:TEMP\rclone\rclone-*-windows-amd64\rclone.exe" $rcloneExe -Force Write-Host "[OK] rclone installed: v$ver" } else { Write-Host "[SKIP] rclone installed: $(& $rcloneExe --version | Select-Object -First 1)" } # 3. rclone.conf (no known_hosts pin — rclone go-sftp doesn't parse keyscan cleanly; key-auth = sufficient trust) @" [kreknin] type = sftp host = 195.19.90.188 user = vitya key_file = $keyPath disable_hashcheck = true "@ | Set-Content "$base\rclone.conf" -Encoding ASCII -Force & cmd.exe /c "icacls `"$base\rclone.conf`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null Write-Host "[OK] rclone.conf written" # 4. config.env @" NTFY_URL=$NtfyUrl NTFY_TOPIC=$NtfyTopic NTFY_USER=$NtfyUser NTFY_PASS=$NtfyPass SMTP_HOST=$SmtpHost SMTP_PORT=$SmtpPort SMTP_FROM=$SmtpFrom SMTP_USER=$SmtpUser SMTP_PASS=$SmtpPass OPS_NOTIFY_EMAIL=$OpsNotifyEmail MSSQL_SA_PASS=$MssqlSaPass "@ | Set-Content "$base\config.env" -Encoding ASCII -Force & cmd.exe /c "icacls `"$base\config.env`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null Write-Host "[OK] config.env written (ntfy + SMTP + MSSQL_SA_PASS)" # 5. Deploy run.ps1 $srcRun = Join-Path $PSScriptRoot 'run.ps1' if (-not (Test-Path $srcRun)) { throw "run.ps1 not found alongside setup.ps1 ($srcRun)" } Copy-Item $srcRun "$base\run.ps1" -Force & cmd.exe /c "icacls `"$base\run.ps1`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null Write-Host "[OK] run.ps1 deployed" # 6. Scheduled Task (Wake-To-Run чтобы запускался даже если машина в standby) $taskName = 'WindowsHost-Backup-Daily' if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) { Unregister-ScheduledTask -TaskName $taskName -Confirm:$false } $action = New-ScheduledTaskAction -Execute 'powershell.exe' ` -Argument "-NoProfile -ExecutionPolicy Bypass -File $base\run.ps1" $trigger = New-ScheduledTaskTrigger -Daily -At $ScheduleAt $principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest $settings = New-ScheduledTaskSettingsSet ` -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries ` -StartWhenAvailable -MultipleInstances IgnoreNew ` -WakeToRun ` -ExecutionTimeLimit (New-TimeSpan -Hours 3) Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null Write-Host "[OK] ScheduledTask '$taskName' registered ($ScheduleAt daily, SYSTEM, Wake-To-Run, 3h max)" Write-Host "" Write-Host "=== Setup complete. Smoke: Start-ScheduledTask -TaskName '$taskName' ===" Write-Host "Log: $base\logs\.log"