Blueforce-Linux/scripts/migration/windows/README.md
emirhanmamak-blueforce ef102b9657 feat(field-os): provisioning layer, real ISO build, offline contracts
Adds the Blueforce Field OS provisioning stack on top of the existing fleet
architecture, without rewriting the working systems (installer, diagnostics,
Ansible, WireGuard/SSH/xRDP/RustDesk, Docker policies, monitoring, Docusaurus).

ISO build (the previous builder was a skeleton that produced no medium):
- boot layout is read from the base ISO at build time via
  `xorriso -report_el_torito as_mkisofs` and rebuilt with `-as mkisofs`;
  `-boot_image any replay` cannot work on Ubuntu live ISOs because the GRUB2
  MBR lives in the source image's first 16 sectors and the EFI system partition
  is an appended partition, neither of which is a file in the ISO9660 tree
- preflight now asserts El Torito boot metadata positively: xorriso exits 0 on
  a non-ISO input, which previously let an arbitrary file pass --check
- artifact produced and verified: dist/Blueforce-Field-OS-1.0.0-amd64.iso,
  6 481 917 952 bytes, SHA256 cb0cc56f...d431296, BIOS+UEFI entries preserved,
  /autoinstall.yaml at the media root, casper/minimal.squashfs byte-identical
  to the base
- recorded baseline deviation: the verified medium on the build host is the
  Desktop flavour, so the manifest carries a baseline_deviation block instead
  of hiding it; Server remains one flag away

Offline and identity:
- offline APT contract with concrete package requirements, pin format and an
  audit table; builds fail closed while pins are UNPINNED
- autoinstall keeps interactive-sections: [storage]: the operator confirms the
  target disk, and no automatic wipe is ever performed
- firstboot terminal wizard for the 8-digit dealer id, three-state lifecycle
  (PROVISIONED_OFFLINE -> ENROLLED -> READY) with fail-closed gates, so a
  missing internet link or WireGuard handshake cannot fail provisioning but can
  never produce a false READY

Fleet and migration:
- five read-only Ansible playbooks for Field OS version, provisioning,
  enrollment, remote channels and offline readiness, all wave-scoped with a
  separate production override
- Windows pre-migration inventory and data export extended to the full brief
  scope, read-only, never collecting credentials

Tests: tests/run-all.sh -> 8/8 pass, exit 0. Boot acceptance (UEFI, Legacy
BIOS, Secure Boot), air-gapped install, real enrollment endpoint and Windows
rollback remain LAB work and are listed in IMPLEMENTATION-REPORT.md.
2026-09-16 11:14:42 +03:00

171 lines
9.8 KiB
Markdown

# Windows → Field OS migration tooling (`scripts/migration/windows/`)
Read-only inventory and export-manifest tooling that runs **on the Windows
terminal before it is migrated** to Blueforce Field OS (docs/28). It never
installs, never writes to the machine, never touches a service, the registry or
the network configuration, and never collects credentials or secrets.
## Files
| File | Report type | Purpose |
|---|---|---|
| `BF-WindowsPreMigrationInventory.ps1` | `windows-pre-migration-inventory` | Hardware, Windows version, disks/volumes, network (IP/DNS/gateway/routes/profiles/proxy), serial-COM ports, USB devices + hardware IDs, drivers, installed software, services, scheduled tasks, Docker presence, VPN clients, RustDesk/AnyDesk IDs, MEG paths, current hostname. |
| `BF-WindowsDataExport.ps1` | `windows-pre-migration-data-export-manifest` | Metadata-only review manifest for the manual data transfer: candidate user folders, removable media, mapped network drives, printers, MEG data directories, installed-software review. Copies nothing. |
Both scripts emit `BF-<no>-windows-inventory*.json` (machine) and
`BF-<no>-windows-inventory*.md` (human).
## Why this directory and not a root `migration/windows/`
The requested layout was `migration/windows/`; this repository keeps every
operational script under `scripts/` (`scripts/{install,maintenance,diagnostics,recovery,migration}`),
so the tooling lives in `scripts/migration/windows/`. The deviation is
deliberate and documented in:
- `migration/README.md` (repo root) — pointer file kept at the expected location
- `docs/32-REPOSITORY-AUDIT-AND-CONFLICTS.md` §B-6 — audit entry for the name and location deviation
Filename deviation from the original request (kept, not "fixed", to preserve
repository consistency). No wrapper scripts are added on purpose: the static
test `tests/check-migration-static.sh` requires **every** `*.ps1` in this
directory to carry the dealer-id/JSON/Markdown/`-NoSoftware` contract, so thin
renamed wrappers would only duplicate the same contract in four files.
| Requested filename | Repo artifact | Status |
|---|---|---|
| `bf-win-inventory.ps1` | `BF-WindowsPreMigrationInventory.ps1` — identity/hardware/OS/disk blocks | Implemented under the repo name |
| `bf-win-network-export.ps1` | same script, `network` + `serial_ports` + `usb` sections | Merged, no separate file |
| `bf-win-software-export.ps1` | same script, `installed_software` / `services` / `scheduled_tasks` / `drivers` / `docker` / `vpn` / `remote_access` / `meg` sections (skippable with `-NoSoftware`) | Merged, no separate file |
| `bf-win-device-export.ps1` | `BF-WindowsDataExport.ps1` (`BF-<no>-windows-inventory-export.json/.md`) | Implemented under the repo name |
This mapping table is the authoritative name/deviation record: one inventory
report and one export manifest, not four fragmented scripts.
## How to run
PowerShell 5.1 (Windows PowerShell) and PowerShell 7+ are both supported. The
scripts are UTF-8 **with BOM** so Windows PowerShell 5.1 reads the Turkish
report headers correctly.
```powershell
# 1) Dry run first: prints the plan and the output paths, writes nothing.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\BF-WindowsPreMigrationInventory.ps1 `
-DealerId 12010193 -OutputDirectory C:\Blueforce\migration -Check
# 2) Real read-only collection (dealer number known).
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\BF-WindowsPreMigrationInventory.ps1 `
-DealerId 12010193 -OutputDirectory C:\Blueforce\migration
# 3) Data-export review manifest (metadata only).
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\BF-WindowsDataExport.ps1 `
-DealerId 12010193 -OutputDirectory C:\Blueforce\migration
# 4) Privacy-reduced run: skip the software surface.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\BF-WindowsPreMigrationInventory.ps1 `
-DealerId 12010193 -OutputDirectory C:\Blueforce\migration -NoSoftware
```
### Execution policy
`-ExecutionPolicy Bypass` applies to **that one process only**; it does not
change the machine or user execution policy. If the operator cannot use it,
either unblock the files (`Unblock-File .\BF-*.ps1`) or run from an already
permitted location/policy. Elevation is not required: nothing is changed, and
an unelevated run only reports which CIM sections were incomplete (the `notes`
array).
### Dealer number unknown
The report name comes from the **8-digit dealer number** (`^[0-9]{8}$`), never
from the machine hostname. If the dealer number is genuinely not known yet, use
the explicit opt-in:
```powershell
.\BF-WindowsPreMigrationInventory.ps1 -DealerIdUnknown -OutputDirectory C:\Blueforce\migration
```
That writes `BF-unknown-windows-inventory.*`, sets `dealer_id_known: false` and
adds a prominent warning in `notes` and at the top of the Markdown report: the
file **must not** be uploaded to the central inventory until the dealer number
is confirmed and the script is re-run with `-DealerId`. Do not guess a dealer
number to "fix" the filename.
## Output contract
| Item | Value |
|---|---|
| JSON | `<OutputDirectory>/BF-<no>-windows-inventory.json` |
| Markdown | `<OutputDirectory>/BF-<no>-windows-inventory.md` |
| Export JSON/MD | `<OutputDirectory>/BF-<no>-windows-inventory-export.{json,md}` |
| Device ID | `BF-<no>` (from the dealer number) |
| `report_type` | `windows-pre-migration-inventory` (export manifest: `windows-pre-migration-data-export-manifest`) |
| `read_only` | `true` — always; the run performs no state change |
| `dealer_id_known` | `true`, or `false` on an explicit `-DealerIdUnknown` run |
| `current_hostname` | the existing Windows hostname (unchanged, recorded) |
| `target_hostname` | `bf-<no>` — the hostname expected after migration (docs/02) |
| `schema_version` | `1` |
Field names are aligned with `scripts/diagnostics/bf-hardware-inventory.sh`
(docs/02 §9) so central tooling can consume both:
| `bf-hardware-inventory.sh` (Linux) | `BF-WindowsPreMigrationInventory.ps1` (Windows) |
|---|---|
| `schema_version`, `device_id`, `hostname`, `dealer_id`, `collected_at`, `notes` | same names (`collected_at` + `collected_at_utc`, `notes` is an array) |
| `manufacturer`, `model`, `serial` | same |
| `bios{vendor,version,date}` | same (+ `smbios_version`) |
| `cpu{model,cores,arch}` | same (+ `logical_processors`, `address_width`) |
| `memory_mb` | same |
| `disks` | per-disk objects `{name,size_gb,interface_type,media_type,serial}` (doc 02 §9 shape) |
| `network{interfaces,macs}` | `network.interfaces[]` (per-adapter objects) + `network.macs[]`, plus `ipv4_addresses`, `default_gateways`, `dns_servers`, `routes`, `network_profiles`, `proxy` |
| `gpu{model}` | same (+ `driver_version`, `video_processor`, `adapter_ram_mb`) |
| `usb` | `usb{devices[],controllers[]}` (includes USB hardware IDs `USB\VID_xxxx&PID_xxxx`) |
| `serial_ports` | `serial_ports{cim_serial_ports[],registry_devicemap,pnp_com_devices[]}` (COM ports) |
| `os{distro,kernel}` | `os{distro,version,build}` + a `windows{}` block (edition, DisplayVersion, build/UBR, hotfixes) |
| `smart`, `tpm`, `virt`, `temps` | not applicable on Windows; not emitted |
| — | Windows-only blocks: `windows`, `volumes`, `drivers`, `installed_software`, `services`, `scheduled_tasks`, `docker`, `vpn`, `remote_access`, `meg`, `user_profiles`, `collection_scope` |
## Read-only guarantee
Asserted mechanically by `tests/check-migration-static.sh` on every run:
- **Forbidden primitives** — no `Set-*`/`New-*`/`Remove-*` item or property
cmdlet, no service control cmdlet, no network-configuration command of any
kind, no registry write, no download cmdlet, no external process execution
(the exact forbidden families are enumerated in
`tests/check-migration-static.sh`). The only write is the report pair under
`-OutputDirectory` (and `-Check` writes nothing at all).
- **Never collected** — credentials, passwords, tokens, private keys, DPAPI
blobs, file contents, browser profiles, event logs, `Windows ProductId`.
- **Device IDs only** — RustDesk/AnyDesk: exactly one ID value is extracted
(registry value named `id` / `ad.anynet.id`, else the matching one-line value
in `RustDesk.toml` / `system.conf`). The RustDesk `id_ed25519` private key,
AnyDesk password/hash settings and `*.ovpn`/`*.conf.dpapi` contents are never
read — VPN tunnel/profile files are listed **by name only**.
- **Registry is read-only** — `Get-ItemProperty`/`Get-ChildItem` only, and
path-like values are selected by an exact name allow-list so unrelated
(possibly sensitive) values in the same key are ignored.
- **Software surface is opt-out** — `-NoSoftware` skips installed software,
services, scheduled tasks and drivers; hardware, network, device and MEG-path
sections are still collected.
- **Dry run** — `-Check` prints the plan and output paths, writes nothing,
exits `0`.
## Verification status (honest note)
There is no PowerShell interpreter on the Linux build host, so these scripts are
statically verified only: syntax/structure review, the `tests/check-migration-static.sh`
contract (tag names, dealer-id regex, JSON/Markdown output, forbidden-primitive
greps) and brace/quote balance checking. **They have not been executed on a real
Windows terminal yet** — first execution belongs to the LAB/pilot step in
docs/28 §10 (`bf-hardware-inventory.sh` runs after migration and its JSON is
diffed against this report). Treat the first run output as unvalidated until
that LAB step is recorded.
## Related documentation
- `docs/28-WINDOWS-TO-LINUX-MIGRATION.md` — migration gate, test plan, rollback
- `docs/02-DEVICE-NAMING-AND-INVENTORY.md` — `BF-<no>` identity + JSON contract
- `docs/17-BACKUP-RECOVERY-AND-REINSTALL.md` — L8 reimage / recovery ladder
- `scripts/diagnostics/bf-live-hw-check` — read-only hardware gate used **after**
migration on the Linux side
- `migration/README.md` (repo root) — location pointer