Files
admin/scripts/iis-migration-to-ruvds/01-ruvds-bootstrap.ps1
vitya 7693400239 tasks(iis-migration-to-ruvds): partial cutover live — kupimknigi DNS flipped
Session 2026-05-23 evening → 2026-05-24: full snolla site migration
windows-recovery-host → RUVDS Win Server 2025 Core, partial DNS cutover для
1 prod hostname (kupimknigi.spb.ru), оставшиеся 24 hostnames pending.

What's live on RUVDS:
- snolla IIS site catch-all *:80: + 25 HTTPS SNI bindings (1 per hostname,
  LE certs от 2026-04-23 / valid до 2026-07-22)
- 8.66 GB / 44725 files transferred via scp после ISP-block discovery
- ApplicationPoolIdentity + ACL grant verified
- HTTP/2 auto-negotiated, MSSQL/imgproxy/MinIO connectivity OK from RUVDS

Findings (Decisions log в task file для деталей):
1. Outbound 445 блокирует home ISP (не RUVDS FW) — `windows-server-2025-core-bootstrap.md`
   SMB-section deprecated, SSH/scp = canonical transfer-метод.
2. Home network HTTP-middlebox mangles Host header в outbound external HTTP —
   тест с source даёт garbled response; тест с VDS (третья сеть) даёт корректный.
3. traefik acme.json → IIS PFX recipe: extract base64 cert+key per cert →
   openssl pkcs12 -export → Import-PfxCertificate + AddSslCertificate by
   thumbprint with SslFlags=1 (SNI). Reusable, потенциально новый concept.
4. IIS 10 на Win Server 2025 говорит HTTP/2 by default через TLS.
5. 4 hostnames (maljarka.tandemmebel.ru + 3 rimiz) → 502/404 на RUVDS;
   на source IIS:8089 возвращают 200 default-page. Pre-existing CMS-tenant
   config gap, не migration defect.

Scripts added:
- scripts/iis-migration-to-ruvds/01-ruvds-bootstrap.ps1 (idempotent)
- scripts/iis-migration-to-ruvds/02-source-transfer.ps1 (не использовался —
  SMB не работает; оставлен как reference)
- scripts/iis-migration-to-ruvds/README.md

Cleanup done:
- Plaintext PFX/PEM keys в C:\Users\vitya\iis-backup-pre-ruvds\certs\ удалены
  (PFX import на RUVDS уже сделан, source-of-truth = traefik acme.json).
- `.secrets/` уже удалён ранее в этой session (см. предыдущий commit).

Source state: IIS:8089 + traefik routes ALIVE — rollback ready. Decommission
после 1-week soak с RUVDS как live prod.

Не push'нуто — ждёт user grant per project-discipline Rule 4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:01:20 +03:00

154 lines
6.4 KiB
PowerShell

#requires -Version 5.1
#requires -RunAsAdministrator
<#
.SYNOPSIS
RUVDS bootstrap for IIS migration from windows-recovery-host.
.DESCRIPTION
Run on RUVDS in PowerShell as Administrator (RDP session).
Steps:
1. Install IIS (Web-Server + sub-features + Management Tools).
2. Verify .NET Framework 4.8 (Release >= 528040).
3. Install URL Rewrite Module 2.1.
4. Open Defender Firewall: HTTP/80, HTTPS/443.
5. Create C:\sites + SMB share, scoped to source IP 46.151.25.64.
6. Print summary.
Idempotent. Transcript: C:\bootstrap-log.txt
#>
[CmdletBinding()]
param(
[string]$SourceIp = '46.151.25.64',
[string]$SiteRoot = 'C:\sites',
[string]$ShareName = 'sites'
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
Start-Transcript -Path C:\bootstrap-log.txt -Append -Force | Out-Null
function Step($name) { Write-Host "`n=== $name ===" -ForegroundColor Cyan }
function Ok($msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
function Skip($msg) { Write-Host " [SKIP] $msg" -ForegroundColor Yellow }
function Fail($msg) { Write-Host " [FAIL] $msg" -ForegroundColor Red }
try {
Step '1. Install IIS'
$iis = Get-WindowsFeature Web-Server
if ($iis.Installed) {
Skip "Web-Server already installed"
} else {
Install-WindowsFeature Web-Server -IncludeAllSubFeature -IncludeManagementTools -Restart:$false | Out-Null
Ok "Web-Server installed"
}
$extras = @('Web-Asp-Net45','Web-Net-Ext45','Web-ISAPI-Ext','Web-ISAPI-Filter','Web-Windows-Auth','Web-Mgmt-Console')
foreach ($f in $extras) {
$st = Get-WindowsFeature $f
if ($st.Installed) {
Skip "$f already installed"
} else {
Install-WindowsFeature $f -Restart:$false | Out-Null
Ok "$f installed"
}
}
Step '2. Verify .NET Framework 4.8'
$ndp = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\' -ErrorAction SilentlyContinue
if ($ndp -and $ndp.Release -ge 528040) {
Ok ".NET 4.8 detected (Release=$($ndp.Release))"
} else {
Fail ".NET 4.8 NOT found (Release=$($ndp.Release)). Win Server 2025 normally pre-bundles 4.8 -- check Optional Features."
Write-Host " Hint: DISM /Online /Add-Capability /CapabilityName:NetFx4.8~~~~" -ForegroundColor Yellow
throw "Bootstrap aborted: .NET 4.8 missing"
}
Step '3. Install URL Rewrite Module 2.1'
$rewriteInstalled = Test-Path 'HKLM:\SOFTWARE\Microsoft\IIS Extensions\URL Rewrite'
if ($rewriteInstalled) {
Skip "URL Rewrite already installed"
} else {
$msiUrl = 'https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi'
$msiPath = "$env:TEMP\rewrite_amd64_en-US.msi"
if (-not (Test-Path $msiPath)) {
Write-Host " Downloading $msiUrl ..."
Invoke-WebRequest -Uri $msiUrl -OutFile $msiPath -UseBasicParsing
Ok "downloaded ($([math]::Round((Get-Item $msiPath).Length / 1MB, 2)) MB)"
}
Write-Host " Installing..."
$p = Start-Process msiexec.exe -ArgumentList "/i `"$msiPath`" /quiet /norestart" -Wait -PassThru
if ($p.ExitCode -ne 0) { throw "msiexec exit $($p.ExitCode)" }
Ok "URL Rewrite installed"
}
Step '4. Open Defender Firewall (HTTP/80, HTTPS/443)'
foreach ($port in @(80,443)) {
$name = "iis-http-$port"
$existing = Get-NetFirewallRule -DisplayName $name -ErrorAction SilentlyContinue
if ($existing) {
Skip "FW rule $name already exists"
} else {
New-NetFirewallRule -DisplayName $name -Direction Inbound -Protocol TCP -LocalPort $port -Action Allow -Profile Any | Out-Null
Ok "FW rule $name added"
}
}
Step '5. Open SMB inbound + create share'
if (-not (Test-Path $SiteRoot)) {
New-Item -ItemType Directory -Path $SiteRoot -Force | Out-Null
Ok "Created $SiteRoot"
} else {
Skip "$SiteRoot already exists"
}
$fileSharingRules = Get-NetFirewallRule -DisplayGroup 'File and Printer Sharing' -ErrorAction SilentlyContinue | Where-Object Enabled -eq 'False'
if ($fileSharingRules) {
$cnt = $fileSharingRules.Count
$fileSharingRules | Set-NetFirewallRule -Enabled True
Ok "Enabled built-in 'File and Printer Sharing' group ($cnt rules)"
} else {
Skip "'File and Printer Sharing' rules already enabled"
}
$smbRule = Get-NetFirewallRule -DisplayName 'smb-from-source' -ErrorAction SilentlyContinue
if ($smbRule) {
Skip "smb-from-source FW rule already exists"
} else {
New-NetFirewallRule -DisplayName 'smb-from-source' `
-Direction Inbound -Protocol TCP -LocalPort 445 `
-RemoteAddress $SourceIp `
-Action Allow -Profile Any | Out-Null
Ok "smb-from-source FW rule added (source=$SourceIp)"
}
$share = Get-SmbShare -Name $ShareName -ErrorAction SilentlyContinue
if ($share) {
Skip "SMB share '$ShareName' already exists (path=$($share.Path))"
} else {
New-SmbShare -Name $ShareName -Path $SiteRoot -FullAccess Administrator | Out-Null
Ok "SMB share '$ShareName' created -> $SiteRoot"
}
Step '6. Summary'
Write-Host ""
Write-Host " IIS (Web-Server): $((Get-WindowsFeature Web-Server).Installed)"
Write-Host " ASP.NET 4.5/4.8: $((Get-WindowsFeature Web-Asp-Net45).Installed)"
Write-Host " URL Rewrite 2.1: $(Test-Path 'HKLM:\SOFTWARE\Microsoft\IIS Extensions\URL Rewrite')"
Write-Host " FW HTTP/80: $([bool](Get-NetFirewallRule -DisplayName 'iis-http-80' -ErrorAction SilentlyContinue))"
Write-Host " FW HTTPS/443: $([bool](Get-NetFirewallRule -DisplayName 'iis-http-443' -ErrorAction SilentlyContinue))"
Write-Host " FW smb-from-source: $([bool](Get-NetFirewallRule -DisplayName 'smb-from-source' -ErrorAction SilentlyContinue))"
Write-Host " SMB share '$ShareName': $([bool](Get-SmbShare -Name $ShareName -ErrorAction SilentlyContinue)) -> $SiteRoot"
Write-Host " Disk C: free: $([math]::Round((Get-PSDrive C).Free / 1GB, 2)) GB"
Write-Host ""
Write-Host "Transcript: C:\bootstrap-log.txt" -ForegroundColor DarkGray
} catch {
Fail $_.Exception.Message
Write-Host $_.ScriptStackTrace -ForegroundColor DarkRed
exit 1
} finally {
Stop-Transcript | Out-Null
}