Windows 10をLinuxサーバからPXEでインストールする

Windows 10をインストールするにあたり、
USBメモリを作るのもCDドライブを用意するのも面倒だったので、PXEを使ってインストールしました。

# 利用する技術
はじめに今回利用する技術要素を簡単に紹介します。

## PXE

**PXE（Preboot eXecution Environment）**はクライアントがネットワークからソフトウェアを取得して実行する仕組みです。
よくあるNICにはこのPXEクライアント機能が実装されており、OSのインストールなどに利用することができます。[Wikipedia](https://en.wikipedia.org/wiki/Preboot_Execution_Environment)

## iPXE

iPXEはオープンソースのネットワークブートファームウェアです。
PXEクライアント機能に加え、いくつかの機能が追加されています。

詳しくは[こちらのページ](https://ipxe.org/)に記載があります。

今回利用するにあたって重要なのは、HTTPでソフトウェアを取得して起動できることです。

## WinPE

**WinPE（Windows PE）**はWindows OSのデプロイ等に利用できる、小さなOSです。
通常のWindowsと比べると機能が制限されています。


# インストール概要

今回は、これらの技術を組み合わせてWindows OSをインストールします。
最終的にはこんな感じにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547524/39167855-2cfc-5c60-bca7-660e5b962e2c.png)

インストールするために、Linuxサーバをひとつ用意します。
今回は、新しいパソコンとLinuxサーバをL2で接続することとし、他のホストは接続されていないこととします。

まずはLinuxサーバを作っていきます。

# Linuxサーバ構築
今回は**Fedora 33 WorkStation**のVMを用意しました。

```
[testing@localhost ~]$ cat /etc/fedora-release 
Fedora release 33 (Thirty Three)
```
インターフェイスには固定IPアドレス**192.168.0.1/24**を設定します。
（各種ソフトウェアインストールにはインターネット接続が必要ですので、環境によっては適宜DHCPに戻すなどする必要があるかもしれません。）

```
[testing@localhost ~]$ ip a sh ens3
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:28:c1:a9 brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    inet 192.168.0.1/24 brd 192.168.0.255 scope global noprefixroute ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::da34:251a:af7d:962d/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
```


## 1. DHCPサーバ、TFTPサーバ設定

はじめに、DHCPサーバとTFTPサーバを設定します。

今回利用するdnsmasqはTFTPサーバ機能を持っていますが利用しません。
別途tftpdをインストールします。

```bash:DHCP
# Fedoraインストールした時点でdnsmasq入っていました。
$ rpm -q dnsmasq
dnsmasq-2.82-3.fc33.x86_64

$ sudo vi /etc/dnsmasq.conf
<下に記載>

$ sudo firewall-cmd --add-service dhcp
$ sudo systemctl start dnsmasq
```

```bash:/etc/dnsmasq.conf（抜粋）
# interfaceを必要なインターフェイスに変更する。
interface=ens3

# 適当なdhcp-rangeをコメントアウトする。
dhcp-range=192.168.0.50,192.168.0.150,12h

# iPXEに関わる設定をコメントアウト。
# 3行目、iPXEに返すdhcp-bootをスクリプトのURLに変更する。スクリプトは後で作成。
dhcp-boot=undionly.kpxe
dhcp-match=set:ipxe,175 # iPXE sends a 175 option.
dhcp-boot=tag:ipxe,http://192.168.0.1/boot.ipxe
```

```bash:TFTP
# 今回はiPXEをFedora Repositoryから取得する。
$ sudo dnf install ipxe-bootimgs
$ rpm -ql ipxe-bootimgs
/usr/share/doc/ipxe-bootimgs
/usr/share/doc/ipxe-bootimgs/COPYING
/usr/share/doc/ipxe-bootimgs/COPYING.GPLv2
/usr/share/doc/ipxe-bootimgs/COPYING.UBDL
/usr/share/ipxe
/usr/share/ipxe/ipxe-i386.efi
/usr/share/ipxe/ipxe-x86_64.efi
/usr/share/ipxe/ipxe.dsk
/usr/share/ipxe/ipxe.iso
/usr/share/ipxe/ipxe.lkrn
/usr/share/ipxe/ipxe.usb
/usr/share/ipxe/undionly.kpxe # <- これを使う。 

$ sudo dnf install tftp-server

$ sudo firewall-cmd --add-service tftp
$ sudo systemctl start tftp

$ sudo cp /usr/share/ipxe/undionly.kpxe /var/lib/tftpboot/
```
ここまでやれば、iPXEの起動までは確認できるはずです。

## 2. WinPE準備
ここでWinPEを作成します。
Windowsで作成する方法もあるのですが、今回は**[wimlib](https://wimlib.net/)**を使ってLinuxで作成してみます。

この時点でWindowsのインストール用ディスクイメージ（ISOファイル）が必要ですので、Microsoft公式からダウンロードしてLinuxサーバに配置しておきます。

```
# 英語版の場合。
$ ls Win10_20H2_v2_English_x64.iso 
Win10_20H2_v2_English_x64.iso
```

```bash:WinPE
# wimlib-utilsとsyslinuxをインストール。
$ sudo dnf install wimlib-utils
$ sudo dnf install syslinux

# WinPE起動時のコマンドを指定。
$ vi boot.cmd
<下に記載>
# 適当にインストーラをマウント。
$ sudo mount Win10_20H2_v2_English_x64.iso /mnt

$ mkwinpeimg -W /mnt -a amd64 -s boot.cmd winpe.img

# アンマウントしておく。
$ sudo umount /mnt
```
上で作成する`boot.cmd`ファイルです。
**ユーザ名（例ではtesting）はLinuxのユーザ名に、
パスワード（例ではpassword）は後ほど指定するSambaのパスワードにします。**

```batch:boot.cmd
wpeinit
ping -n 10 localhost
net use n: \\192.168.0.1\testing password /user:testing
n:\installer\setup.exe
```

これでWinPEのイメージが作成できました。

### ちなみに
`ping -n 10 localhost`があるのは不可解かもしれませんが、`sleep 10`を目的としています。
`net use`を実行する際にネットワーク設定が終わっていないとエラーになるためで、苦肉の策なのです…

## 3. HTTPサーバ設定
今回はNginxを利用します。

```bash:HTTP
$ sudo dnf install nginx

$ sudo firewall-cmd --add-service http
$ sudo systemctl start nginx
```
WinPEの起動に必要なファイルを配置します。
唐突ですが、[wimboot](https://ipxe.org/wimboot)というソフトウェアを利用します。

```bash:wimboot
$ wget http://git.ipxe.org/releases/wimboot/wimboot-latest.zip
$ unzip wimboot-latest.zip
$ sudo cp <展開されたディレクトリ>/wimboot /usr/share/nginx/html/
```
WinPEイメージから必要なファイルを取り出して配置します。

```bash:WinPE
$ sudo mount winpe.img /mnt/
$ sudo cp -r /mnt/boot /mnt/sources /usr/share/nginx/html
```
最後に、iPXEのスクリプトファイルを作成します。
（[wimboot](https://ipxe.org/wimboot)ページを参考にしました。）

```bash:iPXEスクリプト
# DHCPで返すファイル名と一致させること。
$ sudo vi /usr/share/nginx/html/boot.ipxe
```

```bash:boot.ipxe
#!ipxe

kernel wimboot
initrd boot/bcd BCD
initrd boot/boot.sdi boot.sdi
initrd sources/boot.wim boot.wim

boot
```
うまくいけば、この時点でWinPEの起動が確認できます。

## 4. Samba設定
続いてSambaの設定を行います。

```bash:Samba
$ sudo dnf install samba

# パスワードを設定する。WinPE作成時に記載したユーザ名、パスワードと同じにする。
$ sudo smbpasswd -a -U testing
New SMB password:
Retype new SMB password:
Added user testing.

$ sudo firewall-cmd --add-service samba
$ sudo systemctl start smb
```

windowsのインストーラを参照できるようにします。
今回はhomeディレクトリを使います。

```bash:Samba
$ sudo setsebool -P samba_enable_home_dirs=on

$ cd
# WinPE作成時に記載したパスに合わせる。
$ mkdir installer
$ sudo mount -o context=user_u:object_r:user_home_t:s0 Win10_20H2_v2_English_x64.iso installer/

$ ls installer/
autorun.inf  boot  bootmgr  bootmgr.efi  efi  setup.exe  sources
```
これでLinuxサーバの設定はおわりです。

# インストール実行

インストールしてみます。
正しく接続できたら、新しいパソコンを起動して、インストーラが起動するのを待ちます。
インストーラが起動した後は通常と同様でだいたい大丈夫です。
（必要なら起動時にBoot Orderを変更します。）

例を示すため、試しに仮想マシンでやってみます。

- 初めにPXEからiPXEが起動します。
（今回の仮想マシンでは最初からiPXEが起動するため、ここはありません。）
- 次にiPXEがHTTPでスクリプトを取得し、実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547524/e7568ccf-c0bb-9c4b-5ce0-a6da48c79be7.png)
- WinPEが起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547524/233f89ea-629f-5db9-1a97-661e6834cfa3.png)
- WinPEがSambaに接続し、インストーラを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547524/73e5bc93-067c-5e16-6ee2-646701450f94.png)
- あとは通常と同様です。（一部選べないオプションなどあるようです。）

このようにインストールができました。

# 補足・追記

上記手順ではfirewalldやsystemdサービスの設定を永続化していないため、Linuxサーバを再起動すると設定が消えてしまいます。

# 感想

すごくつまったので安いUSBメモリ買ってきたほうが早かったと思います。
ただ、実際の機械をPXEで起動するのはやはり楽しいですね。

# バージョン情報など

今回利用した主なソフトウェアのバージョンを記載しておきます。

```bash:version
$ rpm -q dnsmasq
dnsmasq-2.82-3.fc33.x86_64

$ rpm -q tftp-server
tftp-server-5.2-31.fc33.x86_64

$ rpm -q nginx
nginx-1.18.0-3.fc33.x86_64

$ rpm -q samba
samba-4.13.3-0.fc33.x86_64

$ rpm -q ipxe-bootimgs
ipxe-bootimgs-20200823-1.git4bd064de.fc33.noarch

$ rpm -q wimlib-utils
wimlib-utils-1.13.3-1.fc33.x86_64
```