Compare commits
5 Commits
0ef8d52e19
...
7693400239
| Author | SHA1 | Date | |
|---|---|---|---|
| 7693400239 | |||
| fdefe96d6f | |||
| 98e582f5b5 | |||
| 5ac24f7096 | |||
| f0c392c880 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -11,3 +11,12 @@ Thumbs.db
|
|||||||
*~
|
*~
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Secret material — должны жить в `pass`, не в repo (даже untracked).
|
||||||
|
.secrets/
|
||||||
|
*.env
|
||||||
|
!*.env.example
|
||||||
|
|
||||||
|
# Transient operational drops — findings captured в .tasks/, не в repo root.
|
||||||
|
*-log.txt
|
||||||
|
*-size.txt
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Admin Task Board
|
# Admin Task Board
|
||||||
_Updated: 2026-05-22 (Сессия: MSSQL Express 2022 live (`mssql.kzntsv.site:1433`, 5 DBs, 8 prod hosts 200 OK). MinIO/imgproxy VDS stack live as standby (CMS остаётся на windows из-за hardcoded URL в closed-source DLL). board-viewer-vds-deploy ✓ (board.kzntsv.site + basicauth). **+ Portainer-migration sweep**: 12 ad-hoc stacks → Portainer-managed (canonical rule добавлен в CLAUDE.md + `portainer-stack-management-vds` concept). Skipped traefik+portainer (management plane). Decommission window-host MSSQL после 48ч soak 2026-05-24.)_
|
_Updated: 2026-05-24 (IIS migration to RUVDS — **partial cutover live**: snolla site (8.66 GB / 44725 files) transferred via scp (после ISP outbound-445 block обнаружен), IIS recreated, 25 HTTPS SNI bindings с LE certs (re-used from traefik acme.json), 7 prod hostnames live-smoked через VDS (третья сеть) → 200 OK + correct content. DNS A для kupimknigi.spb.ru flipped на 80.64.31.36 (после initial misroute на VDS). Soak window: 24h+. Source IIS:8089 + traefik routes ALIVE — rollback ready. Previous: temp admin start 2026-05-23 09:37-18:08, dropped на UNC-robocopy exit 16; secrets-leak в `.secrets/` fixed → `pass`. См. также 2026-05-22 chain: MSSQL Express 2022 live на `mssql.kzntsv.site:1433` (5 DBs, 8 prod hosts 200 OK), MinIO/imgproxy VDS standby.)_
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Status legend:
|
Status legend:
|
||||||
@@ -10,21 +10,29 @@ _Updated: 2026-05-22 (Сессия: MSSQL Express 2022 live (`mssql.kzntsv.site:
|
|||||||
🔵 Blocked — waiting on external input
|
🔵 Blocked — waiting on external input
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## ⚪ [board-viewer-expand-whitelist] — quick-win: расширить `board_viewer_repos` в auth.toml на VDS (~18 репо, fix `victor/books` опечатки)
|
## 🟢 [board-viewer-expand-whitelist] — Closed 2026-05-22 — whitelist расширен с 5 до 21 репо
|
||||||
|
|
||||||
**Status:** ready (no code-change, ship'ается сегодня; не блокирует round1)
|
**Closed:** 2026-05-22 — auth.toml обновлён: убраны `OpeItcLoc03/books` (опечатка) и `OpeItcLoc03/projects-meta-mcp` (субдир, не репо); добавлены 16 репо из 3 owner'ов (workshop/common/meeting-room/factory/projects-wiki, victor/books/stostayer.new/pilonuxt/pilorama98.ru/snolla/modules-db/crsc.web/npm-mcp/O_C-Phazerville, cancel_music/heart-and-mask/cancel-music-webstore/temps_utile-/modulair-rag). Build restart → 242 tasks from 21 repos (cancel_music: 9, OpeItcLoc03: 151, victor: 82). Owner-pill теперь полезен — 3 разных namespace.
|
||||||
**Where I stopped:** (not started — текущий whitelist 5 репо, user видит таски всего из 4 проектов (один `OpeItcLoc03/projects-meta-mcp` — субдиректория `.common`, не отдельный репо). Список ~18 репо для добавления — в `board-viewer-expand-whitelist.md`.)
|
|
||||||
**Next action:** SSH или Portainer file editor stack Id=3 → edit `/opt/stacks/board-viewer/auth.toml` `board_viewer_repos` (полный список в per-task file). Заменить `OpeItcLoc03/books`→`victor/books`, добавить `workshop`, `common`, `stostayer.new` и остальные. `docker compose restart build` (или ждать next-tick 5 мин). Smoke: external curl показывает карточки из всех namespace'ов.
|
**Atomic revert:** `ssh vds "cd /opt/stacks/board-viewer && sudo cp auth.toml.bak auth.toml && docker compose restart build"`
|
||||||
**Branch:** n/a
|
**Branch:** n/a
|
||||||
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: .workshop / 2026-05-22 / trigger: user-ux-feedback-round1 -->
|
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: .workshop / 2026-05-22 / trigger: user-ux-feedback-round1 -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔵 [board-viewer-redeploy-ux-round1] — rebuild image + push registry + compose pull on VDS, после dev-round1 6 UX-fixes
|
## 🟢 [board-viewer-redeploy-ux-round1] — Closed 2026-05-22 — 5 UX-fixes redeploy'ed (task-numbers paused)
|
||||||
|
|
||||||
**Status:** blocked
|
**Closed:** 2026-05-22 — image rebuilt (61 tests pass) + pushed to registry + VDS pull+redeploy. All 5 fix'ы verified:
|
||||||
**Blocker:** `board-viewer-drawer-bundle-md`, `board-viewer-ux-show-slug`, `board-viewer-ux-full-datetime`, `board-viewer-ux-owner-conditional`, `board-viewer-ux-task-numbers`, `board-viewer-ux-done-cutoff` — все 6 в `OpeItcLoc03/board-viewer/.tasks/`.
|
- ✅ Slug visible на карточке (`<code class="card-slug">`)
|
||||||
**Where I stopped:** (not started — открывать когда все 6 dev-таск 🟢 в `board-viewer/.tasks/`)
|
- ✅ Full datetime (YYYY-MM-DD HH:MM МСК + tooltip)
|
||||||
|
- ✅ Multi-owner working (3 namespace: cancel_music / OpeItcLoc03 / victor, owner-pill visible)
|
||||||
|
- ✅ Done cutoff (show 158 more, data-overflow attr)
|
||||||
|
- ✅ Drawer bundle (1.29 MB HTML vs 164 KB — md bundled, 0 runtime git requests)
|
||||||
|
|
||||||
|
`board-viewer-ux-task-numbers` remains paused (user choice, не regression).
|
||||||
|
|
||||||
|
**Branch:** master
|
||||||
|
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: .workshop / 2026-05-22 / per workshop CLAUDE.md §5 ops-handoff -->
|
||||||
**Next action:**
|
**Next action:**
|
||||||
1. Pull `OpeItcLoc03/board-viewer` master на dev-машине.
|
1. Pull `OpeItcLoc03/board-viewer` master на dev-машине.
|
||||||
2. `cd ~/projects/board-viewer && npm test` — все тесты green (включая новые от 6 UX-fixes).
|
2. `cd ~/projects/board-viewer && npm test` — все тесты green (включая новые от 6 UX-fixes).
|
||||||
@@ -681,17 +689,36 @@ Copy-Item C:\sites\snolla\Web.config.bak-pre-vds-cutover-20260522 C:\sites\snoll
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚪ [windows-hosting-vendor-research] — Design-таска (1 день). Выбрать вендора managed Windows hosting для будущего переезда IIS с windows-recovery-host (домашней машины). Output: 1-pager с recommended vendor + cost estimate + migration recipe outline.
|
## 🟢 [windows-hosting-vendor-research] — vendor selected: RUVDS (2026-05-22)
|
||||||
|
|
||||||
Это **переходный** трек до завершения snolla node-rewrite'а (после которого IIS не нужен вообще). Не файлим impl-таску миграции до этой research'и — vendor выбираем сначала.
|
**Status:** done
|
||||||
|
**Selected:** RUVDS Windows Server 2025 Core, 2GB RAM, 30GB HDD, 1IP (RDP ready). DC: Королёв. User selected outside research process.
|
||||||
**Status:** ready
|
**Next action:** impl-таска `iis-migration-to-ruvds` (ready).
|
||||||
**Where I stopped:** (not started)
|
|
||||||
**Next action:** См. `windows-hosting-vendor-research.md` — constraints + candidates + acceptance. Steps: (1) research 4 candidates (Rusonyx / Selectel / FirstVDS / Beget), (2) fill comparison table по criteria (RDP / .NET 4.8 / IIS 10 / public IP / cost / SLA / reviews), (3) write top-1 recommendation с trade-off justification, (4) cost estimate monthly + one-time, (5) document в `.wiki/concepts/windows-hosting-vendor-comparison-2026.md`. **Не file** follow-up impl-task `iis-migration-to-managed-windows-hosting` — пусть future-agent file'нёт когда vendor выбран. Spawn'd by [resilience-roadmap-design] workshop pass 1 2026-05-21.
|
|
||||||
**Branch:** n/a
|
**Branch:** n/a
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🟡 [iis-migration-to-ruvds] — RUVDS IIS live для 1 prod hostname (`kupimknigi.spb.ru`), 24 в очереди; источник held для rollback
|
||||||
|
|
||||||
|
**Status:** in_progress (DNS partial cutover — soak window 2026-05-24)
|
||||||
|
**Where I stopped:** 2026-05-24 ~00:00 — `kupimknigi.spb.ru` DNS A flipped на 80.64.31.36 в reg.ru, authoritative `ns1.reg.ru` корректный, public resolver caches (8.8.8.8=6h, 1.1.1.1=24h, Yandex=2h) держат старое значение → real users мигрируют постепенно по TTL expiry. RUVDS state: snolla site (8.66 GB / 44725 files) transferred + IIS recreated + 25 HTTPS SNI bindings с LE certs (R13, valid до 2026-07-22), 7 prod hostnames live-smoke через VDS (третья сеть) → 200 OK / correct content. Source IIS:8089 + traefik routes ALIVE — rollback ready.
|
||||||
|
**Findings зафиксированы в** [iis-migration-to-ruvds.md](iis-migration-to-ruvds.md) Decisions log: (1) outbound 445 блокирует home ISP, не RUVDS-FW → SSH/scp = canonical transfer-метод (deprecate SMB section в `windows-server-2025-core-bootstrap.md`); (2) home network HTTP-middlebox mangles Host header for direct external HTTP — real end-users не пострадают, тестировать через VDS; (3) traefik acme.json → IIS PFX recipe работает (extract + openssl pkcs12 -export + Import-PfxCertificate + AddSslCertificate by thumbprint); (4) IIS 10 HTTP/2 default; (5) `maljarka.tandemmebel.ru` + 3 rimiz hostnames вернули 502/404 — pre-existing CMS-tenant config gap, не migration defect.
|
||||||
|
**Next action:**
|
||||||
|
1. **Снизить DNS TTL** в reg.ru на остальные 24 hostnames до 300s — `pilorama98.ru`, `emspb.ru`, `tandemmebel.ru`, `labtools.{ru,pro}`, `snolla.com` + 11 snolla subdomains. Это сократит будущий cache-tail с 24h до 5 мин.
|
||||||
|
2. **24h soak kupimknigi** — проверить через cache-clean resolver (можно подождать пока 8.8.8.8 cache expire'нет — ~6h, потом curl без `--resolve`).
|
||||||
|
3. **Bulk DNS swap** оставшихся 24 hostnames на 80.64.31.36 (single sitting).
|
||||||
|
4. **1-week prod soak** с RUVDS как live source.
|
||||||
|
5. **Decommission source IIS:8089** (но не traefik сам — много non-CMS routes остаётся): stop/remove IIS site `snolla` на windows-recovery-host + remove CMS traefik yml files.
|
||||||
|
6. **LE renewal pipeline** — win-acme + HTTP-01 на RUVDS после full cutover (LE certs expire 2026-07-22, soak window до ~07-15).
|
||||||
|
7. **Cleanup migration-temp:** `Remove-NetFirewallRule -DisplayName 'smb-from-source','ssh-from-source'` на RUVDS, удалить `~/.ssh/ruvds-iis-migration*` на source, очистить `C:\ProgramData\ssh\administrators_authorized_keys` на RUVDS.
|
||||||
|
|
||||||
|
Полный план + Completed + Open questions + Remaining steps — в [iis-migration-to-ruvds.md](iis-migration-to-ruvds.md).
|
||||||
|
**Branch:** master
|
||||||
|
<!-- created-by: user-decision / 2026-05-22 / trigger: RUVDS purchased -->
|
||||||
|
<!-- partial-cutover-by: vitya / 2026-05-24 / kupimknigi DNS flipped, 24 hostnames pending -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Imported from MoreThenCms — 2026-05-21 subtree-split migration
|
# Imported from MoreThenCms — 2026-05-21 subtree-split migration
|
||||||
|
|
||||||
Полное содержимое `.tasks/STATUS.md` из MoreThenCms на момент 2026-05-21, **CMS-domain блоки исключены** (остались в MoreThenCms): `cms-admin-assets-root-folders-seed`, `cms-port-leak-fix`, `traefik-maljarka-502-bug`, `cms-maljarka-https-mode-bug-fix`. История per-file сохранена через subtree-split + temp-prefix merge.
|
Полное содержимое `.tasks/STATUS.md` из MoreThenCms на момент 2026-05-21, **CMS-domain блоки исключены** (остались в MoreThenCms): `cms-admin-assets-root-folders-seed`, `cms-port-leak-fix`, `traefik-maljarka-502-bug`, `cms-maljarka-https-mode-bug-fix`. История per-file сохранена через subtree-split + temp-prefix merge.
|
||||||
|
|||||||
107
.tasks/iis-migration-to-ruvds.md
Normal file
107
.tasks/iis-migration-to-ruvds.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# iis-migration-to-ruvds
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Migrate IIS hosting from [[../entities/windows-recovery-host]] (DESKTOP-NSEF0UK) to RUVDS Windows Server 2025 Core (`80.64.31.36`). Убрать SPOF домашней машины.
|
||||||
|
|
||||||
|
**Scope finalize 2026-05-24:** только `snolla` IIS site (catch-all для ~26 hostnames через CMS multi-tenant routing) — 8.66 GB. `stostayer` уже на external MSSQL (не наш scope). `stostayer.old` local-only (defer). `snolla-identity-manager` dead conn-string (defer). См. "Scope" ниже.
|
||||||
|
|
||||||
|
**Pre-requisites done:**
|
||||||
|
- MSSQL+MinIO уже на [[../entities/vds-kzntsv]] (см [[mssql-minio-migration-to-vds]])
|
||||||
|
- RUVDS purchased: Windows Server 2025 Core, 2GB RAM, 30GB HDD, 1IP, DC Королёв
|
||||||
|
- RDP ready (creds — `pass show ruvds-iis/full-env`, public IP `80.64.31.36`)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
**1 IIS site → 25 HTTPS hostnames bound via SNI:**
|
||||||
|
|
||||||
|
| Hostname | Note |
|
||||||
|
|---|---|
|
||||||
|
| kupimknigi.spb.ru | ⚡ pilot, DNS flipped 2026-05-24 |
|
||||||
|
| emspb.ru / www.emspb.ru | ✅ ready |
|
||||||
|
| pilorama98.ru / www.pilorama98.ru | ✅ ready |
|
||||||
|
| labtools.ru / www.labtools.ru | ✅ ready |
|
||||||
|
| labtools.pro / www.labtools.pro | ✅ ready |
|
||||||
|
| tandemmebel.ru / www.tandemmebel.ru | ✅ ready |
|
||||||
|
| snolla.com / on.snolla.com / ics-artmaterials.snolla.com / pilorama98.snolla.com | ✅ ready |
|
||||||
|
| labtools.snolla.com / emspb.snolla.com / tandemmebel.snolla.com | ✅ ready |
|
||||||
|
| sestech.snolla.com / labtoolspro.snolla.com / artmone.snolla.com | ✅ ready |
|
||||||
|
| rimiz.ru / www.rimiz.ru / rimiz.snolla.com | ⚠ CMS-side 502/404, не migration defect — degraded pre-cutover [[#degraded-tenants]] |
|
||||||
|
| maljarka.tandemmebel.ru | ⚠ CMS-side 502 (см. выше) |
|
||||||
|
| 1 catch-all `*:80:` HTTP binding | retained для legacy/diagnostics |
|
||||||
|
|
||||||
|
Все 25 HTTPS bindings связаны с LE certs (R13, выпущены 2026-04-23, valid до 2026-07-22).
|
||||||
|
|
||||||
|
## Key files
|
||||||
|
|
||||||
|
- `C:\sites\snolla\` — 8.66 GB / 44725 files (source on windows-recovery-host)
|
||||||
|
- `\\80.64.31.36\C$\sites\snolla\` — destination (transferred 2026-05-23 23:08-23:33 via scp)
|
||||||
|
- `C:\Users\vitya\iis-backup-pre-ruvds\scp-snolla.log` — scp transcript
|
||||||
|
- `C:\ProgramData\ssh\administrators_authorized_keys` (RUVDS) — pubkey deployed for transfer
|
||||||
|
- `~/.ssh/ruvds-iis-migration` / `.pub` (source) — temp key pair (clean up post-cutover)
|
||||||
|
- `pass show ruvds-iis/full-env` — RDP creds (canonical secret store)
|
||||||
|
- `scripts/iis-migration-to-ruvds/01-ruvds-bootstrap.ps1` — RUVDS bootstrap (idempotent)
|
||||||
|
- `[[../.wiki/concepts/windows-server-2025-core-bootstrap]]` — bootstrap recipe (SMB section deprecated, см. Decisions log 2026-05-23 23:00)
|
||||||
|
|
||||||
|
## Decisions log
|
||||||
|
|
||||||
|
- **2026-05-23 09:37 (admin attempt):** RDP creds сохранены в `.secrets/ruvds-iis.env` plaintext, в repo tree → нарушение etap-2 secrets-discipline. Ретроспективно вынесены в `pass show ruvds-iis/full-env`, `.secrets/` + `*.env` + `*-log.txt` + `*-size.txt` добавлены в `.gitignore`.
|
||||||
|
- **2026-05-23 18:08 (admin attempt):** robocopy через UNC `\\80.64.31.36\sites\snolla\` упал exit 16. Initial root cause: SMB-share не создан + FW 445 не открыт на fresh Win Server 2025 Core. Зафиксировано в `windows-server-2025-core-bootstrap.md`.
|
||||||
|
- **2026-05-23 22:30 (session-recovery transfer attempt):** После bootstrap'а (FW+share созданы) SMB всё равно не reachable — TCP/445 не выходит **с home-ISP**. **Real root cause: outbound 445 блокирует ISP (стандартная анти-worm политика residential провайдеров RU). FW scoping на RUVDS-стороне корректен.** SMB recommendation в bootstrap-концепте **deprecated** — SSH/scp = canonical transfer-метод.
|
||||||
|
- **2026-05-23 22:35 (transfer):** OpenSSH.Server на RUVDS уже был installed (admin'ом раньше), sshd running. Открыл FW 22 scoped к source IP, deployed ed25519 pubkey в `C:\ProgramData\ssh\administrators_authorized_keys` (с правильным ACL — SYSTEM + Administrators only). scp -r `C:\sites\snolla` → `C:/sites/` отработал за ~25 мин (8.66 GB / 5 MB/s home uplink), exit 0, count+size MATCH (44725 files / 9302398880 bytes).
|
||||||
|
- **2026-05-23 23:18 (IIS recreate):** Default Web Site удалён. AppPool `snolla` (.NET v4.0, Integrated, ApplicationPoolIdentity), Website `snolla` с physicalPath `C:\sites\snolla` + catch-all binding `*:80:`. ACL — `IIS APPPOOL\snolla` + `IIS_IUSRS` Read на 46150 file entries. Local smoke с loopback `localhost` + `Host: kupimknigi.spb.ru` → 200 OK + правильный title.
|
||||||
|
- **2026-05-23 23:25 (external smoke through home network):** все 8 hostnames вернули "SNOLLA | Cloud CMS" default (28406 bytes), **не** per-tenant content. Внутри RUVDS (loopback) — корректный per-tenant content. Difference: external requests **из home network** теряют Host header через HTTP-aware middlebox (DPI/transparent proxy в OpenWRT/ISP path). SSH tunnel localhost:8888→RUVDS:80 → correct content. Тест из VDS (другая сеть) → correct content. **Conclusion: home-side outbound HTTP mutation; real end-users из других сетей не пострадают.**
|
||||||
|
- **2026-05-23 23:30 (HTTPS bindings):** Экспортировал 14 LE certs из traefik `acme.json` (`C:\Users\vitya\projects\docker\diskstation\traefik\letsencrypt\acme.json`) → openssl pkcs12 -export → PFX → Import-PfxCertificate на RUVDS → New-WebBinding `*:443:<hostname>` с SslFlags=1 (SNI) → AddSslCertificate by thumbprint. 25 HTTPS bindings live. Cert chain valid (LE R13), HTTP/2 auto-negotiated.
|
||||||
|
- **2026-05-23 23:35 (live smoke from VDS):** 7 prod hostnames → 200 OK + correct content; canonical bare→www 301s (CMS-side); 4 hostnames (`maljarka.tandemmebel.ru`, `rimiz.ru`, `www.rimiz.ru`, `rimiz.snolla.com`) → 502/404 — CMS-internal tenant mismatch (на source IIS:8089 они возвращают 200 default = тоже degraded). Не блокирует cutover.
|
||||||
|
- **2026-05-24 ~00:00 (DNS partial-cutover):** user flipped A `kupimknigi.spb.ru` в reg.ru — сначала на `89.253.255.94` (VDS Linux, ошибка), потом на `80.64.31.36` (RUVDS). Authoritative `ns1.reg.ru` отдаёт правильное; public resolver-cache (8.8.8.8=6h, 1.1.1.1=24h, Yandex=2h) держат старое до TTL expiry. Real end-users мигрируют postepenno over cache TTL. **TTL=86400 — slишком много. Recommendation: снизить до 300s в reg.ru для всех hostnames в scope ДО полного DNS swap'а.**
|
||||||
|
- **MinIO pipeline caveat (carry-over от `[iis-cutover-to-vds-services]`):** RUVDS IIS coupled к windows-host imgproxy через DNS `imgproxy.kzntsv.site`. SPOF home machine остаётся для image-rendering. Verified post-migration: `Test-NetConnection imgproxy.kzntsv.site -Port 443` с RUVDS = OK. Image pipeline working but не resilient.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
- [ ] **DNS TTL = 86400 на kupimknigi.spb.ru** — снизить до 300s в reg.ru перед swap'ом остальных 24 hostnames. Это сократит cache-tail с 24h до 5 мин.
|
||||||
|
- [ ] **Source IIS state backup НЕ снят** (skip'нули — appcmd требует elevated source-shell которого не было). Acceptable risk — source IIS всё ещё running как rollback. Если RUVDS proves stable за 1 неделю → можно decommission source без forensic snapshot'а.
|
||||||
|
- [ ] **CMS-side 502/404 на 4 hostnames** — `maljarka.tandemmebel.ru` / `rimiz.ru` / `www.rimiz.ru` / `rimiz.snolla.com`. На source тоже не работают (return default page). Pre-existing dead-routes от `[iis-traefik-dead-routes-cleanup]` 2026-05-21. Изоляция не блокер для migration; защититься от cutover-blame — отдельная investigation если нужно.
|
||||||
|
- [ ] **Backup strategy для RUVDS** — расширить `vds-backup-rsync-kreknin` cron как третий source? Решение отложено до после full cutover.
|
||||||
|
- [ ] **LE renewal на RUVDS** — текущие certs valid до 2026-07-22 (~60 дней). До expiry нужен permanent renewal pipeline:
|
||||||
|
- Option A: win-acme (wacs.exe) standalone-на-RUVDS с DNS-01 (manual TXT на reg.ru — нет reg.ru-plugin) или HTTP-01 (но только после DNS swap'а — иначе LE challenge bounce'нется на home).
|
||||||
|
- Option B: cron-job который re-extract'ит из traefik acme.json + scp upload + Import-PfxCertificate на RUVDS. Pollute source's traefik lifecycle.
|
||||||
|
- Recommendation: Option A win-acme + HTTP-01 после full cutover (~95 days margin до cert expiry — 60 days minus DNS-stabilization window).
|
||||||
|
- [ ] **Image-pipeline SPOF (post-cutover):** windows-host imgproxy остаётся single-point. Option B (local nginx-relay) / Option D (decompile DLL) — defer.
|
||||||
|
|
||||||
|
## Completed steps
|
||||||
|
|
||||||
|
- [x] **2026-05-22:** vendor research (`windows-hosting-vendor-research` 🟢) — RUVDS selected.
|
||||||
|
- [x] **2026-05-23 09:37:** RUVDS purchased + RDP creds saved (плюс post-hoc cleanup → `pass`).
|
||||||
|
- [x] **2026-05-23 18:08:** failed-robocopy attempt → finding закреплён.
|
||||||
|
- [x] **2026-05-23 22:00:** RUVDS bootstrap (IIS + sub-features + .NET 4.8 verify + URL Rewrite + FW 80/443 + SMB share + 445 rule). Idempotent script `scripts/iis-migration-to-ruvds/01-ruvds-bootstrap.ps1`.
|
||||||
|
- [x] **2026-05-23 22:35:** OpenSSH.Server + FW 22 + key-auth setup.
|
||||||
|
- [x] **2026-05-23 22:47-23:13:** scp transfer (8.66 GB / 44725 files, exit 0, count+size MATCH).
|
||||||
|
- [x] **2026-05-23 23:18:** IIS site `snolla` recreated на RUVDS + ACL grant.
|
||||||
|
- [x] **2026-05-23 23:25:** HTTP catch-all smoke — local (loopback) green, external (home) garbled by middlebox, external (VDS) green.
|
||||||
|
- [x] **2026-05-23 23:30:** 14 LE certs extracted from traefik acme.json → 14 PFX → 25 HTTPS bindings + SNI live.
|
||||||
|
- [x] **2026-05-23 23:35:** 7 prod hostnames live-smoked through VDS → 200 OK / correct content; 4 hostnames degraded pre-migration (acceptable).
|
||||||
|
- [x] **2026-05-24 ~00:00:** DNS swap kupimknigi.spb.ru → 80.64.31.36 (initial misroute to VDS corrected).
|
||||||
|
|
||||||
|
## Remaining steps (post-soak)
|
||||||
|
|
||||||
|
- [ ] Verify kupimknigi.spb.ru через cache-clean resolver (после TTL expiry на 8.8.8.8 / 1.1.1.1) — должно показывать RUVDS content в браузере без `--resolve` override.
|
||||||
|
- [ ] **Lower DNS TTL** в reg.ru на остальные 24 hostnames до 300s. Можно сделать сразу — обновит cache в ближайшие 24h, после этого swap пойдёт быстро.
|
||||||
|
- [ ] DNS swap (через reg.ru) остальных 24 hostnames на `80.64.31.36`.
|
||||||
|
- [ ] 1-неделя soak с RUVDS как live prod.
|
||||||
|
- [ ] Decommission source IIS:8089 + traefik routes для CMS-hostnames (но не traefik сам — много других routes остаётся).
|
||||||
|
- [ ] Cleanup: `Remove-NetFirewallRule -DisplayName 'smb-from-source'` (already-unused) + `ssh-from-source` (после disable temp key) + удалить `~/.ssh/ruvds-iis-migration*` keys + удалить `C:\ProgramData\ssh\administrators_authorized_keys` на RUVDS.
|
||||||
|
- [ ] LE renewal pipeline (см. Open questions).
|
||||||
|
- [ ] Concept update — `windows-server-2025-core-bootstrap.md` SMB-section deprecate в пользу SSH/scp, добавить host-header-middlebox warning, добавить HTTP/2 note.
|
||||||
|
- [ ] New concept — `traefik-acme-json-to-iis-cert-import.md` (recipe экспорта-импорта).
|
||||||
|
- [ ] Source-info commit — записать что image-pipeline SPOF остался → отдельная task.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **Win Server 2025 Core** — GUI-less, RDP-only, drag-n-drop через `mstsc /admin` Local Drives redirect; scp/PSSession предпочтительнее RDP-copy.
|
||||||
|
- **2GB RAM** — w3wp ~330 MB при cold start, monitor under load. Возможно нужен `RecyclingPeriodicRestartMemory 200MB`.
|
||||||
|
- **HTTP middlebox в home network** mangles Host header for direct external HTTP — affected smoke testing, не end-users.
|
||||||
|
- **Migration transitional** — long-term snolla-on-node makes IIS obsolete. Не over-engineer.
|
||||||
|
|
||||||
|
<!-- created-by: user-decision / 2026-05-22 / trigger: RUVDS purchased -->
|
||||||
|
<!-- attempted-by: admin-session / 2026-05-23 09:37-18:08 / dropped after failed-robocopy -->
|
||||||
|
<!-- session-recovery: vitya / 2026-05-23 evening / SMB-ISP-block detected → SSH/scp pivot → 8.66 GB transferred → IIS recreated → 25 HTTPS SNI bindings → kupimknigi DNS flipped 2026-05-24 / soak window 24h+ -->
|
||||||
104
.wiki/concepts/windows-server-2025-core-bootstrap.md
Normal file
104
.wiki/concepts/windows-server-2025-core-bootstrap.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
---
|
||||||
|
title: Windows Server 2025 Core — bootstrap для IIS-хоста (RUVDS-сценарий)
|
||||||
|
status: live
|
||||||
|
tags: [windows, iis, ruvds, bootstrap, smb, ops]
|
||||||
|
related: [[iis-migration-2026-05-19-postmortem]], [[recovery-architecture-snapshot]], [[future-resilient-architecture-goals]]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Windows Server 2025 Core — bootstrap для IIS-хоста
|
||||||
|
|
||||||
|
Из коробки на RUVDS (2GB RAM, 30GB HDD, Win Server 2025 Core, RDP-only) машина **не готова** ни принимать файлы по SMB, ни хостить IIS — стандартный сценарий «купил, RDP'нулся, robocopy'нул» не работает. Документ фиксирует дефолтное состояние, default-blockers, и матрицу transfer-методов для переноса content'а с windows-recovery-host.
|
||||||
|
|
||||||
|
## Дефолтное состояние fresh Win Server 2025 Core
|
||||||
|
|
||||||
|
| Компонент | Дефолт | Нужно для IIS-host'инга |
|
||||||
|
|---|---|---|
|
||||||
|
| GUI / Server Manager desktop | нет (Core SKU) | PowerShell-only management |
|
||||||
|
| IIS (Web-Server feature) | не установлен | `Install-WindowsFeature Web-Server -IncludeAllSubFeature -IncludeManagementTools` |
|
||||||
|
| .NET Framework 4.8 | varies (бывает pre-bundled, бывает нет) | `Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\'` — Release ≥ 528040 |
|
||||||
|
| URL Rewrite Module 2.1 | нет | download + `msiexec /i rewrite_amd64_en-US.msi /quiet` |
|
||||||
|
| Defender Firewall inbound 80/443 | closed | `New-NetFirewallRule -DisplayName "HTTP" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow` (+443) |
|
||||||
|
| **SMB inbound 445** | **closed** | для UNC robocopy — открывать profile-specific rule |
|
||||||
|
| **File and Printer Sharing service / share** | **off, no share** | для UNC robocopy — `Set-NetFirewallRule -DisplayGroup "File and Printer Sharing" -Enabled True -Profile Any` + `New-SmbShare` |
|
||||||
|
| WinRM / PSRemoting | enabled (домен / private) | для PSSession-based transfer — verify `Enable-PSRemoting -SkipNetworkProfileCheck` |
|
||||||
|
| RDP 3389 | open | работает из коробки |
|
||||||
|
|
||||||
|
## Гочa: UNC robocopy на fresh Core → exit 16
|
||||||
|
|
||||||
|
**Симптом:** `robocopy C:\sites\snolla\ \\<ruvds-ip>\sites\snolla\ *.* /S /E /DCOPY:DA /COPY:DAT /Z /MT:8 /R:2 /W:5` → `2026/05/23 18:08:29 ERROR 53 (0x00000035) Создание папки назначения … Не найден сетевой путь.`
|
||||||
|
|
||||||
|
**Root cause:** на RUVDS:
|
||||||
|
1. TCP/445 закрыт в Defender Firewall (public profile по дефолту).
|
||||||
|
2. SMB share `sites$` не создан (`Get-SmbShare` показывает только админскую `C$`, доступную только Administrator).
|
||||||
|
3. Authentication через UNC требует Windows credentials — `net use \\<ip>\C$ /user:Administrator <pass>` сначала.
|
||||||
|
|
||||||
|
Проверка с source (windows-recovery-host):
|
||||||
|
```powershell
|
||||||
|
Test-NetConnection -ComputerName <ruvds-ip> -Port 445
|
||||||
|
# TcpTestSucceeded : False ← вот корень
|
||||||
|
```
|
||||||
|
|
||||||
|
## Матрица transfer-методов (source → RUVDS)
|
||||||
|
|
||||||
|
| Метод | Setup на RUVDS | Pros | Cons |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **RDP local-drive redirection** | nothing — mstsc `/admin` → Local Resources → Drives → C: | zero-config, через 3389 (уже открыт) | drag-n-drop вручную, не batch; explorer-через-RDP slow для >1 GB; не подходит для 8 GB+ snolla |
|
||||||
|
| **SMB inbound (recommended для batch)** | open FW 445 + `New-SmbShare -Name sites -Path C:\sites -FullAccess Administrator` + `net use` с source | robocopy native, /MT:8 параллелизм, resume через `/Z` | exposed SMB — должен быть либо в private network, либо VPN, либо source-IP firewall whitelist |
|
||||||
|
| **WinRM PSSession + Copy-Item** | `Enable-PSRemoting`, добавить source IP в `TrustedHosts`, FW 5985/5986 | encrypted, не нужен SMB, скрипт-friendly | медленнее SMB на больших файлах; HTTP-default; HTTPS требует cert setup |
|
||||||
|
| **SFTP** (OpenSSH server feature) | `Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0` + start sshd + FW 22 | works с любого Linux/Mac source; ключи безопасно | extra service surface; sshd default config иногда permissive |
|
||||||
|
| **HTTP PUT через временный traefik+webdav** | overkill для bootstrap | reusable для последующих deploy'ев | сначала нужен IIS/nginx running — но IIS ещё не deployed → chicken-and-egg |
|
||||||
|
|
||||||
|
**Recommendation для IIS migration:** SMB inbound с source-IP whitelist. Самый быстрый для 11 sites × ~1 GB; native robocopy /Z поддерживает interrupt-resume; FW rule убирается после migration cutover.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# На RUVDS (RDP-сессия, PowerShell):
|
||||||
|
Set-NetFirewallRule -DisplayGroup "File and Printer Sharing" -Enabled True
|
||||||
|
New-NetFirewallRule -DisplayName "SMB-from-source" -Direction Inbound `
|
||||||
|
-Protocol TCP -LocalPort 445 -RemoteAddress <source-public-ip> -Action Allow
|
||||||
|
New-Item -ItemType Directory -Path C:\sites -Force
|
||||||
|
New-SmbShare -Name sites -Path C:\sites -FullAccess Administrator
|
||||||
|
```
|
||||||
|
|
||||||
|
Source-side (windows-recovery-host):
|
||||||
|
```powershell
|
||||||
|
net use \\<ruvds-ip>\sites /user:Administrator <pass>
|
||||||
|
robocopy C:\sites\snolla \\<ruvds-ip>\sites\snolla *.* /S /E /DCOPY:DA /COPY:DAT /Z /MT:8 /R:2 /W:5
|
||||||
|
net use \\<ruvds-ip>\sites /delete
|
||||||
|
```
|
||||||
|
|
||||||
|
После cutover — `Remove-SmbShare -Name sites` + удалить FW rule.
|
||||||
|
|
||||||
|
## Bootstrap-чеклист (порядок)
|
||||||
|
|
||||||
|
1. RDP в RUVDS (3389 уже открыт, creds в `pass show ruvds-iis/full-env`).
|
||||||
|
2. `Install-WindowsFeature Web-Server -IncludeAllSubFeature -IncludeManagementTools` — установить IIS.
|
||||||
|
3. Verify .NET 4.8: `(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\').Release` ≥ 528040. Если нет — установить через RUVDS Server Manager (web) или offline-installer + RDP-drop.
|
||||||
|
4. Install URL Rewrite 2.1: download `https://download.microsoft.com/.../rewrite_amd64_en-US.msi`, `msiexec /i ... /quiet`.
|
||||||
|
5. Open FW 80/443: `New-NetFirewallRule -DisplayName "HTTP" -Direction Inbound -Protocol TCP -LocalPort 80,443 -Action Allow`.
|
||||||
|
6. Open SMB rule (см. блок выше) — **только на время migration**.
|
||||||
|
7. Verify external connectivity:
|
||||||
|
- MSSQL: `Test-NetConnection mssql.kzntsv.site -Port 1433`
|
||||||
|
- MinIO: `Test-NetConnection minio.kzntsv.site -Port 443` (если CMS pipeline остаётся на windows-host — не нужно; см. [[iis-cutover-to-vds-services]] §MinIO phase)
|
||||||
|
8. Backup source IIS state с windows-recovery-host:
|
||||||
|
```powershell
|
||||||
|
& "$env:SystemRoot\system32\inetsrv\appcmd.exe" list site -config > C:\Users\vitya\sites-backup.txt
|
||||||
|
Copy-Item C:\Windows\System32\inetsrv\config\applicationHost.config C:\Users\vitya\applicationHost.config.bak
|
||||||
|
```
|
||||||
|
9. Transfer 11 sites через robocopy/SMB (см. метод выше).
|
||||||
|
10. Recreate sites через `appcmd add site` или `New-IISSite`, копировать bindings.
|
||||||
|
11. Update Web.config conn-strings → `mssql.kzntsv.site,1433;TrustServerCertificate=True` (как в `[mssql-vds-migration]`).
|
||||||
|
12. Pilot на kupimknigi.spb.ru через hosts-file DNS override, 24h soak, потом DNS A swap.
|
||||||
|
|
||||||
|
## Gotcha-fineprint
|
||||||
|
|
||||||
|
- **2GB RAM tight:** IIS + 11 worker pools = легко уйдёт в paging. Рекомендую установить `IIS:\AppPools\<name> -RecyclingPeriodicRestartMemory 200MB` per-pool + monitor через `Get-Counter '\Process(w3wp*)\Working Set'`.
|
||||||
|
- **Windows Server 2025 Core нет explorer.exe:** drag-n-drop не работает в RDP без redirect drives — закладывай SMB или SFTP заранее.
|
||||||
|
- **LE certs:** если IIS будет terminate'ить TLS напрямую — `win-acme` (wacs.exe) standalone-friendly на Core. Альтернатива — traefik-on-RUVDS перед IIS, но это лишняя layer для 11 sites.
|
||||||
|
- **Backup strategy для RUVDS:** unsolved — RUVDS native snapshots = VDS-snapshot-level, не file-level. Daily rsync sites+IIS config → kreknin (расширить `vds-backup-rsync-kreknin` на источник RUVDS) — отдельная chore-task после успешного cutover.
|
||||||
|
|
||||||
|
## Cross-refs
|
||||||
|
|
||||||
|
- Driver для миграции: [[future-resilient-architecture-goals]] §Workshop pass 1 → IIS-track «вынести IIS с windows-recovery-host чтобы убрать SPOF домашней машины».
|
||||||
|
- Vendor selection: `.tasks/windows-hosting-vendor-research.md` (🟢 closed 2026-05-22 — RUVDS выбран).
|
||||||
|
- Mid-impl task: `.tasks/iis-migration-to-ruvds.md` (🟡 paused 2026-05-23 — этот концепт зафиксирован после failed-robocopy инцидента).
|
||||||
|
- Source-host postmortem: [[iis-migration-2026-05-19-postmortem]] — что было при первой миграции снолла-recovery → нативный IIS.
|
||||||
@@ -39,6 +39,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
|
|||||||
- [vbox-windows-stability-tuning](concepts/vbox-windows-stability-tuning.md) — VirtualBox + Windows-гость — нюансы стабильности cross-hypervisor миграции
|
- [vbox-windows-stability-tuning](concepts/vbox-windows-stability-tuning.md) — VirtualBox + Windows-гость — нюансы стабильности cross-hypervisor миграции
|
||||||
- [verdaccio-prune-semantics](concepts/verdaccio-prune-semantics.md) — Verdaccio prune semantics — proxied vs locally-published
|
- [verdaccio-prune-semantics](concepts/verdaccio-prune-semantics.md) — Verdaccio prune semantics — proxied vs locally-published
|
||||||
- [wd40efax-smr-cascade](concepts/wd40efax-smr-cascade.md) — WD40EFAX SMR Cascade — root cause NAS failure 2026-05-18
|
- [wd40efax-smr-cascade](concepts/wd40efax-smr-cascade.md) — WD40EFAX SMR Cascade — root cause NAS failure 2026-05-18
|
||||||
|
- [windows-server-2025-core-bootstrap](concepts/windows-server-2025-core-bootstrap.md) — Win Server 2025 Core (RUVDS) — bootstrap для IIS-хоста: default-blockers + transfer-методов матрица
|
||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
|
|
||||||
|
|||||||
@@ -17,3 +17,5 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations).
|
|||||||
## [2026-05-21] ingest | concepts/ocis-on-vds-deploy-recipe — oCIS deploy recipe + gotchas (UID mismatch, PROXY_TLS, PROXY_ENABLE_BASIC_AUTH, LibreGraph user-create); updated entities/vds-kzntsv (added owncloud row to stack/hostnames/file-layout); source: .tasks/owncloud-vds-deploy.md
|
## [2026-05-21] ingest | concepts/ocis-on-vds-deploy-recipe — oCIS deploy recipe + gotchas (UID mismatch, PROXY_TLS, PROXY_ENABLE_BASIC_AUTH, LibreGraph user-create); updated entities/vds-kzntsv (added owncloud row to stack/hostnames/file-layout); source: .tasks/owncloud-vds-deploy.md
|
||||||
|
|
||||||
## [2026-05-22] update | concepts/ocis-on-vds-deploy-recipe — Gotcha 5: 60s HTTP timeout caps slow-uplink uploads at ~200MB on 3 MiB/s link; traefik buffering NOT the fix (500 from oxy buffer); workaround = VDS-side curl PUT loopback via throwaway sftp key. Source: .tasks/owncloud-vds-deploy.md close-note.
|
## [2026-05-22] update | concepts/ocis-on-vds-deploy-recipe — Gotcha 5: 60s HTTP timeout caps slow-uplink uploads at ~200MB on 3 MiB/s link; traefik buffering NOT the fix (500 from oxy buffer); workaround = VDS-side curl PUT loopback via throwaway sftp key. Source: .tasks/owncloud-vds-deploy.md close-note.
|
||||||
|
|
||||||
|
## [2026-05-23] ingest | concepts/windows-server-2025-core-bootstrap — default-blockers (SMB closed, IIS/URL-Rewrite/.NET unverified) + transfer-методов матрица (RDP-redirect / SMB / WinRM / SFTP); recommendation = SMB inbound с source-IP whitelist на время migration. Source: failed-robocopy инцидент 2026-05-23 18:08 (.tasks/iis-migration-to-ruvds.md Decisions log).
|
||||||
|
|||||||
153
scripts/iis-migration-to-ruvds/01-ruvds-bootstrap.ps1
Normal file
153
scripts/iis-migration-to-ruvds/01-ruvds-bootstrap.ps1
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#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
|
||||||
|
}
|
||||||
171
scripts/iis-migration-to-ruvds/02-source-transfer.ps1
Normal file
171
scripts/iis-migration-to-ruvds/02-source-transfer.ps1
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#requires -Version 5.1
|
||||||
|
#requires -RunAsAdministrator
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Source-side transfer: backup IIS state + robocopy snolla site to RUVDS.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Run on source (DESKTOP-NSEF0UK / windows-recovery-host) in PowerShell as Administrator.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Probe RUVDS SMB 445 reachability (fail fast).
|
||||||
|
2. Pull RUVDS Administrator password from pass-store.
|
||||||
|
3. Backup IIS state to C:\Users\vitya\iis-backup-pre-ruvds\:
|
||||||
|
- applicationHost.config
|
||||||
|
- appcmd dump of all sites (for reference)
|
||||||
|
- appcmd dump of snolla site config + apppool (for recreate on RUVDS)
|
||||||
|
4. net-use mount \\80.64.31.36\sites with creds.
|
||||||
|
5. Copy IIS backup files into the share (so RUVDS can grab them for recreate).
|
||||||
|
6. robocopy C:\sites\snolla -> \\80.64.31.36\sites\snolla
|
||||||
|
(/Z resume-on-disconnect, /MT:8 parallel, /R:2 /W:5 retries, /XJ skip junctions)
|
||||||
|
7. Verify count + size match.
|
||||||
|
8. Disconnect SMB.
|
||||||
|
|
||||||
|
Transcript: C:\Users\vitya\iis-backup-pre-ruvds\transfer-log.txt
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$RuvdsIp = '80.64.31.36',
|
||||||
|
[string]$ShareName = 'sites',
|
||||||
|
[string]$SiteName = 'snolla',
|
||||||
|
[string]$SourceSite = 'C:\sites\snolla',
|
||||||
|
[string]$BackupDir = 'C:\Users\vitya\iis-backup-pre-ruvds'
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$ProgressPreference = 'SilentlyContinue'
|
||||||
|
|
||||||
|
if (-not (Test-Path $BackupDir)) { New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null }
|
||||||
|
Start-Transcript -Path (Join-Path $BackupDir 'transfer-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 }
|
||||||
|
|
||||||
|
$shareUnc = "\\$RuvdsIp\$ShareName"
|
||||||
|
$mountedShare = $false
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
Step '1. Probe RUVDS SMB 445'
|
||||||
|
$smb = Test-NetConnection -ComputerName $RuvdsIp -Port 445 -InformationLevel Quiet -WarningAction SilentlyContinue
|
||||||
|
if (-not $smb) { throw "RUVDS:445 not reachable. Re-run 01-ruvds-bootstrap.ps1 on RUVDS." }
|
||||||
|
Ok "RUVDS:445 reachable"
|
||||||
|
|
||||||
|
Step '2. Pull RUVDS password from pass-store'
|
||||||
|
$bash = 'C:\Program Files\Git\bin\bash.exe'
|
||||||
|
if (-not (Test-Path $bash)) { throw "bash.exe not found at $bash; pass-store unreachable" }
|
||||||
|
$passOut = & $bash -lc 'pass show ruvds-iis/full-env'
|
||||||
|
$passLine = $passOut | Where-Object { $_ -match '^RUVDS_IIS_PASS=' } | Select-Object -First 1
|
||||||
|
if (-not $passLine) { throw "RUVDS_IIS_PASS not found in pass show ruvds-iis/full-env" }
|
||||||
|
$ruvdsPass = $passLine -replace '^RUVDS_IIS_PASS=',''
|
||||||
|
Ok "credentials loaded from pass-store"
|
||||||
|
|
||||||
|
Step '3. Backup source IIS state'
|
||||||
|
$appcmd = "$env:SystemRoot\system32\inetsrv\appcmd.exe"
|
||||||
|
if (-not (Test-Path $appcmd)) { throw "appcmd.exe not found -- IIS Management Tools not installed on source?" }
|
||||||
|
|
||||||
|
Copy-Item 'C:\Windows\System32\inetsrv\config\applicationHost.config' (Join-Path $BackupDir 'applicationHost.config') -Force
|
||||||
|
Ok "applicationHost.config backed up"
|
||||||
|
|
||||||
|
& $appcmd list site /config:* /xml | Out-File -FilePath (Join-Path $BackupDir 'all-sites.xml') -Encoding UTF8
|
||||||
|
Ok "appcmd list site (all) dumped"
|
||||||
|
|
||||||
|
& $appcmd list site /name:$SiteName /config:* /xml | Out-File -FilePath (Join-Path $BackupDir 'snolla-site.xml') -Encoding UTF8
|
||||||
|
Ok "snolla-site.xml dumped"
|
||||||
|
|
||||||
|
& $appcmd list apppool /xml | Out-File -FilePath (Join-Path $BackupDir 'all-apppools.xml') -Encoding UTF8
|
||||||
|
Ok "all-apppools.xml dumped"
|
||||||
|
|
||||||
|
Step '4. Mount RUVDS share'
|
||||||
|
cmd.exe /c "net use $shareUnc /user:Administrator $ruvdsPass" 2>&1 | Out-Null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
cmd.exe /c "net use $shareUnc /delete /y" 2>&1 | Out-Null
|
||||||
|
cmd.exe /c "net use $shareUnc /user:Administrator $ruvdsPass" 2>&1 | Tee-Object -Variable mountOut | Out-Null
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "net use failed: $mountOut" }
|
||||||
|
}
|
||||||
|
$mountedShare = $true
|
||||||
|
Ok "mounted $shareUnc"
|
||||||
|
|
||||||
|
Step '5. Copy IIS backup files to share (for RUVDS recreate-script access)'
|
||||||
|
$remoteBackupDir = Join-Path $shareUnc '_iis-backup'
|
||||||
|
if (-not (Test-Path $remoteBackupDir)) { New-Item -ItemType Directory -Path $remoteBackupDir -Force | Out-Null }
|
||||||
|
Copy-Item (Join-Path $BackupDir '*.xml') $remoteBackupDir -Force
|
||||||
|
Copy-Item (Join-Path $BackupDir 'applicationHost.config') $remoteBackupDir -Force
|
||||||
|
Ok "IIS backup files copied to $remoteBackupDir"
|
||||||
|
|
||||||
|
Step '6. robocopy snolla site'
|
||||||
|
$srcDir = $SourceSite
|
||||||
|
$dstDir = Join-Path $shareUnc $SiteName
|
||||||
|
if (-not (Test-Path $srcDir)) { throw "source $srcDir not found" }
|
||||||
|
|
||||||
|
$srcSizeGB = [math]::Round(((Get-ChildItem $srcDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1GB), 2)
|
||||||
|
Write-Host " Source size: $srcSizeGB GB"
|
||||||
|
Write-Host " Destination: $dstDir"
|
||||||
|
Write-Host " This may take 30-90 min on home uplink. Progress goes to log."
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$robocopyLog = Join-Path $BackupDir 'robocopy-snolla.log'
|
||||||
|
$robocopyArgs = @(
|
||||||
|
$srcDir, $dstDir,
|
||||||
|
'*.*',
|
||||||
|
'/S', '/E',
|
||||||
|
'/DCOPY:DA', '/COPY:DAT',
|
||||||
|
'/Z',
|
||||||
|
'/MT:8',
|
||||||
|
'/R:2', '/W:5',
|
||||||
|
'/XJ',
|
||||||
|
'/TEE',
|
||||||
|
"/LOG+:$robocopyLog",
|
||||||
|
'/NP', '/NDL'
|
||||||
|
)
|
||||||
|
$rcStart = Get-Date
|
||||||
|
& robocopy.exe @robocopyArgs
|
||||||
|
$rcExit = $LASTEXITCODE
|
||||||
|
$rcDuration = (Get-Date) - $rcStart
|
||||||
|
|
||||||
|
# robocopy exit codes: 0-7 = success/partial, 8+ = error
|
||||||
|
if ($rcExit -ge 8) {
|
||||||
|
throw "robocopy failed with exit $rcExit. See $robocopyLog tail."
|
||||||
|
}
|
||||||
|
Ok ("robocopy done in {0:hh\:mm\:ss} (exit={1})" -f $rcDuration, $rcExit)
|
||||||
|
|
||||||
|
Step '7. Verify count + size'
|
||||||
|
$srcCount = (Get-ChildItem $srcDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count
|
||||||
|
$dstCount = (Get-ChildItem $dstDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count
|
||||||
|
$srcBytes = (Get-ChildItem $srcDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
|
||||||
|
$dstBytes = (Get-ChildItem $dstDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
|
||||||
|
Write-Host " Source: $srcCount files, $([math]::Round($srcBytes / 1MB, 0)) MB"
|
||||||
|
Write-Host " Dest: $dstCount files, $([math]::Round($dstBytes / 1MB, 0)) MB"
|
||||||
|
if ($srcCount -ne $dstCount) { Fail "FILE COUNT MISMATCH ($srcCount vs $dstCount)"; throw "count mismatch" }
|
||||||
|
if ($srcBytes -ne $dstBytes) { Fail "SIZE MISMATCH ($srcBytes vs $dstBytes bytes)"; throw "size mismatch" }
|
||||||
|
Ok "count + size match"
|
||||||
|
|
||||||
|
Step '8. Summary'
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Source site: $srcDir"
|
||||||
|
Write-Host " RUVDS dest: $dstDir"
|
||||||
|
Write-Host " Files transferred: $dstCount"
|
||||||
|
Write-Host " Bytes transferred: $([math]::Round($dstBytes / 1GB, 2)) GB"
|
||||||
|
Write-Host " Duration: $('{0:hh\:mm\:ss}' -f $rcDuration)"
|
||||||
|
Write-Host " IIS backup local: $BackupDir"
|
||||||
|
Write-Host " IIS backup remote: $remoteBackupDir (on RUVDS as C:\sites\_iis-backup\)"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Transcript: $(Join-Path $BackupDir 'transfer-log.txt')" -ForegroundColor DarkGray
|
||||||
|
Write-Host " Robocopy log: $robocopyLog" -ForegroundColor DarkGray
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Next: run 03-ruvds-recreate-sites.ps1 on RUVDS." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
Fail $_.Exception.Message
|
||||||
|
Write-Host $_.ScriptStackTrace -ForegroundColor DarkRed
|
||||||
|
exit 1
|
||||||
|
} finally {
|
||||||
|
if ($mountedShare) {
|
||||||
|
cmd.exe /c "net use $shareUnc /delete /y" 2>&1 | Out-Null
|
||||||
|
Write-Host "`n [cleanup] SMB share dismounted" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
Stop-Transcript | Out-Null
|
||||||
|
}
|
||||||
42
scripts/iis-migration-to-ruvds/README.md
Normal file
42
scripts/iis-migration-to-ruvds/README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# IIS migration to RUVDS — execution scripts
|
||||||
|
|
||||||
|
Скрипты для миграции IIS-хостинга `windows-recovery-host` (DESKTOP-NSEF0UK) → RUVDS Win Server 2025 Core (`80.64.31.36`).
|
||||||
|
|
||||||
|
Контекст и план целиком — `.tasks/iis-migration-to-ruvds.md` + `.wiki/concepts/windows-server-2025-core-bootstrap.md`.
|
||||||
|
|
||||||
|
## Execution order
|
||||||
|
|
||||||
|
| # | Script | Where to run | Purpose |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | `01-ruvds-bootstrap.ps1` | **на RUVDS** под RDP (PowerShell admin) | Install IIS + URL Rewrite + open FW 80/443 + open SMB scoped to source IP + create C:\sites share. Idempotent. |
|
||||||
|
| 2 | `02-source-transfer.ps1` *(coming next)* | **на source** (DESKTOP-NSEF0UK) | net-use → robocopy snolla → verify. |
|
||||||
|
| 3 | `03-ruvds-recreate-sites.ps1` *(coming next)* | **на RUVDS** | appcmd add site / bindings / web.config conn-string swap → `mssql.kzntsv.site,1433`. |
|
||||||
|
| 4 | `04-pilot-and-swap.md` *(playbook, not script)* | mixed | Pilot kupimknigi.spb.ru через hosts-file → 24h soak → DNS A swap. |
|
||||||
|
| 5 | `99-ruvds-cleanup-smb.ps1` *(after cutover)* | **на RUVDS** | Remove SMB share + FW rule (закрываем 445 после migration). |
|
||||||
|
|
||||||
|
## Pre-reqs
|
||||||
|
|
||||||
|
- RUVDS up + RDP ready (`pass show ruvds-iis/full-env`)
|
||||||
|
- Source public IP **46.151.25.64** уже whitelist'нут в скрипте `#1` (если source IP меняется — `-SourceIp <new>` параметр)
|
||||||
|
- На RUVDS — Administrator-shell PowerShell
|
||||||
|
|
||||||
|
## How to copy scripts to RUVDS
|
||||||
|
|
||||||
|
Два варианта:
|
||||||
|
|
||||||
|
**A. RDP drive-redirect (рекомендуемо):**
|
||||||
|
1. `mstsc /v:80.64.31.36` → Show Options → Local Resources → More → Drives → отметить `C:` → ОК.
|
||||||
|
2. После logon в RUVDS — `\\tsclient\C\` mount'нут. Скопировать `C:\Users\vitya\projects\.admin\scripts\iis-migration-to-ruvds\01-ruvds-bootstrap.ps1` → `C:\bootstrap.ps1`.
|
||||||
|
3. PowerShell admin → `C:\bootstrap.ps1`.
|
||||||
|
|
||||||
|
**B. Paste-friendly:** открыть скрипт в нашей репе локально → copy всё содержимое → в RUVDS `notepad C:\bootstrap.ps1` → paste → save → run.
|
||||||
|
|
||||||
|
## Verification после bootstrap'а
|
||||||
|
|
||||||
|
На source:
|
||||||
|
```powershell
|
||||||
|
Test-NetConnection 80.64.31.36 -Port 445 # должно стать True
|
||||||
|
Test-NetConnection 80.64.31.36 -Port 443 # должно стать True
|
||||||
|
```
|
||||||
|
|
||||||
|
Если оба True — переходим к script #2.
|
||||||
Reference in New Issue
Block a user