-- Views
May 23, 26
スライド概要
※PHPerKaigi 2026のLTで発表したときに利用したスライドです。
オンプレ構成をローカル再現で再現したいということで、Hyper-Vを利用してみました。
PowerShellは正直扱いにくいのでWSL2でやろうとしたら、とんでもなく苦労したのでそれの備忘録的なものです。
最初からPowerShellでやればこんなに苦労しなかったかもしれません。
PowerShellでHyper-V環境を 構築したときの奮闘記 PHPerKaigi LT大会 2026/03/22 masnmt
自己紹介 @masnmt (読み方はますんと) 合同会社リトルハーミットの代表やってる野本昌嗣といいます。 花火を観るのが好きで、花火鑑賞士試験に合格してたりもしてます。 あと、ボドゲとお酒も好きです。 主な戦場 好きなディストリビューション Debian GNU/Linux 好きなエディタ GNU Emacs 好きなDBMS PostgreSQL PHP Laravel メインブラウザ Vivaldi フロントエンド Vue 好きなポインティングデバイス トラックボール クラウド AWS IMEの日本語入力方式 かな入力
自己紹介 こんなボドゲ作ってたりします。 迫りくる納期と戦う ITエンジニアの 苦悩がここに! 大惨事を隠蔽しつつ 登録ユーザ数を 伸ばせ!
なんでHyper-Vを使おうと? 複数VMでオンプレ構成をローカル再現したい ネットワーク(VLAN / セグメント)も含めて検証したい クラウドではL2レベルの構成再現が難しい DockerではOS/カーネル分離やネットワーク再現に限界 再現性・メンテナンスのためコード管理したい
そもそもHyper-Vとは Windowsに標準搭載のType-1ハイパーバイザー ハードウェア直上で動作し、高速・安定 仮想マシン(Windows / Linux)を柔軟に実行可能 vSwitchでネットワーク分離・VLAN対応 ※WSL2も内部的にHyper-Vを利用
PowerShellは正直扱いにくいのでWSL2で Hyper-V関連のコマンドはWindows上で実行されます。 なんとか.exeをWSL2上からも実行できることは知っていました。 なのでデフォルトで入っているpowershell.exeも実行できるはず。 $ powershell.exe
PowerShellは正直扱いにくいのでWSL2で Hyper-V関連のコマンドはWindows上で実行されます。 なんとか.exeをWSL2上からも実行できることは知っていました。 なのでデフォルトで入っているpowershell.exeも実行できるはず。 $ powershell.exe zsh: command not found: powershell.exe
PowerShellは正直扱いにくいのでWSL2で パスを通しましょう。 C:\Windows\System32\WindowsPowerShell\v1.0 なので /mnt/c/Windows/System32/WindowsPowerShell/v1.0 これでPowerShellをたたけるようになり、以外と便利になるので、 Hyper-Vを使わなくてもおすすめです。
Hyper-Vコマンドをたたくための確認 取得系のコマンドを実行できるか試してみる。 $ powershell.exe -c Get-VMHost
Hyper-Vコマンドをたたくための確認 取得系のコマンドを実行できるか試してみる。 $ powershell.exe -c Get-VMHost Get-VMHost : このタスクを完了するために必要な アクセス許可がありません。 このコンピューター 'Hoge-PC' の承認ポリシーの 管理者に問い合わせてください。
Hyper-Vコマンドをたたくための確認 Hyper-Vのたいていのコマンドは管理者権限が必要です。 管理者権限でPowerShellを動かせばいいのです。 $ powershell.exe -c Get-VMHost Get-VMHost : このタスクを完了するために必要な アクセス許可がありません。 このコンピューター 'Hoge-PC' の承認ポリシーの 管理者に問い合わせてください。
Hyper-Vコマンドをたたくための確認 右クリックで「管理者として実行」はWSL2上にはない。 スクリプトで自動化を狙っているので、コマンドラインから行ける 方法として、管理者権限(昇格)で別プロセスを起動方法がある。 まずはPowerShellのターミナルで確認してみる。 PS> Start-Process powershell -Verb RunAs Get-VMHost
Hyper-Vコマンドをたたくための確認
Hyper-Vコマンドをたたくための確認 別窓で開いて、一瞬で閉じる。 何が起きているのかわからない。 PS> Start-Process powershell -Verb RunAs Get-VMHost
Hyper-Vコマンドをたたくための確認 -NoExitを指定することで、閉じることは回避できます。 右Hyper-Vでやりたいことは1コマンド実行で終わるものではなく、 複数実行して行うものなので、ps1ファイルにまとめて記述して実行 したいので、-Fileもつけます。 PS> Start-Process powershell -Verb RunAs -ArgumentList '-NoExit','-File','hoge.ps1'
Hyper-Vコマンドをたたくための確認 すぐ終了して何が起きてるのかわからん。なぜだ。 PS> Start-Process powershell -Verb RunAs -ArgumentList '-NoExit','-File','hoge.ps1'
Hyper-Vコマンドをたたくための確認 フルパスが必要。 あと、引数はちゃんと配列で渡したほうがきれい。 PS> Start-Process powershell -Verb RunAs -ArgumentList @( '-NoExit','-File','C:\hoge.ps1' )
Hyper-Vコマンドをたたくための確認 マジか。 PS> Start-Process powershell -Verb RunAs -ArgumentList @( '-NoExit','-File','C:\hoge.ps1' ) このシステムではスクリプトの実行が無効になっているため、ファ イル C:\hoge.ps1 を読み込むことができません。
Hyper-Vコマンドをたたくための確認 ArgumentListでも管理者昇格が必要。 大元のpowershellとは扱いが別。さらに標準出力も消えるので、ps1 ファイルで何か画面に出したかったらWrite-Hostが必要になる。 PS> Start-Process powershell -Verb RunAs -ArgumentList @( '-NoExit','-ExecutionPolicy','Bypass', '-File','C:\hoge.ps1' )
Hyper-Vコマンドをたたくための確認
Hyper-Vコマンドをたたくための確認 OK PS> Start-Process powershell -Verb RunAs -ArgumentList @( '-NoExit','-ExecutionPolicy','Bypass', '-File','C:\hoge.ps1' )
さぁWSL2でやってみよう -NoProfileを付けて、ユーザ環境によるものを一切読まずクリーンな 環境で実行することにする。 $ powershell.exe -NoProfile -Command " Start-Process powershell -Verb RunAs -ArgumentList @('-NoExit','-NoProfile', '-ExecutionPolicy','Bypass', '-File','~/hoge.ps1')"
さぁWSL2でやってみよう すぐ終了して何が起きてるのかわからん。 あぁ、そうかWSL2でもフルパスか。 標準出力だけでなく、標準エラーも握りつぶされているのは正直キツイ。 $ powershell.exe -NoProfile -Command " Start-Process powershell -Verb RunAs -ArgumentList @('-NoExit','-NoProfile', '-ExecutionPolicy','Bypass', '-File','~/hoge.ps1')"
さぁWSL2でやってみよう WSL2ではwslpathというコマンドが利用できるので、Linuxのパス をWindows用のパスに変換して渡してあげる。 ちなみに hoge.ps1 の一行目は、とりあえず何か表示させるために、 Write-Host "ほげ" を記述してある。 $ powershell.exe -NoProfile -Command " Start-Process powershell -Verb RunAs -ArgumentList @('-NoExit','-NoProfile', '-ExecutionPolicy','Bypass', '-File','$(wslpath -w ~/hoge.ps1)')"
さぁWSL2でやってみよう WSL2ではwslpathというコマンドが利用できるので、Linuxのパス をWindows用のパスに変換して渡してあげる。 ちなみに hoge.ps1 の一行目は、とりあえず何か表示させるために、 Write-Host "ほげ" を記述してある。 発生場所 \\wsl.localhost\Debian\home\masnmt\phperkaigi2026\hoge.ps1:1 文 字:12 + Write-Host "縺サ縺・ + ~~~~~ 文字列に終端記号 " がありません。
さぁWSL2でやってみよう ps1ファイルの中の日本語はshift-jisでないとパースエラーになる。 ちなみにコメントでもダメ。 発生場所 \\wsl.localhost\Debian\home\masnmt\phperkaigi2026\hoge.ps1:1 文 字:12 + Write-Host "縺サ縺・ + ~~~~~ 文字列に終端記号 " がありません。
これで落とし穴は全て埋まった(落ちた) .shでシェルスクリプトにして使いやすくする。 #!/usr/bin/env bash WPATH=$(wslpath -w $1) powershell.exe -NoProfile -Command " Start-Process powershell -Verb RunAs -ArgumentList @( '-NoExit', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', '$WPATH' )"
これで落とし穴は全て埋まった(落ちた) 実行が終わるとウインドウが残ったままになるので、-NoExitをやめ て何かキーを押したら閉じるようにする。 try { . \\wsl.localhost\Debian\home\masnmt\phperkaigi2026\hoge.ps1 } catch { Write-Error $_ Write-Host ' ' } finally { Write-Host Write-Host 'Press any key to exit... ' -NoNewline [void]($Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')) } エラーが発生したので終了します
これで落とし穴は全て埋まった(落ちた) ファイルパスを渡して前後にtry-catchを挟むスクリプトをコマンド して渡す方法として、-EncodedCommand というオプションが PowerShellにある。 こいつに渡す文字列はUTF-16LEでエンコードした文字列をBase64 にしたもの(日本語OK)。 ただし、-EncodedCommandで実行するときのカレントディレクト リは C:\Windows\System32 になるので注意。 この方法でps1ファイルを渡しても、ps1ファイルそのものはやはり shift-jisでないとパースエラーになる。
これで落とし穴は全て埋まった(落ちた)
#!/usr/bin/env bash
PS1_PATH=$(wslpath -w $1)
PS1_ARGS="${@:2}"
WPWD=$(wslpath -w "$PWD")
ENCODED=$(iconv -f UTF-8 -t UTF-16LE <<-__END_OF_PS1__ | base64 | tr -d '\r\n'
try {
Set-Location -LiteralPath '$WPWD'
. '$PS1_PATH' $PS1_ARGS
} catch {
Write-Error \$_
Write-Host '
'
} finally {
Write-Host
Write-Host 'Press any key to exit... ' -NoNewline
[void](\$Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'))
}
__END_OF_PS1__
)
エラーが発生したので終了します
POWERSHELL=$(command -v powershell.exe || echo /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe)
$POWERSHELL -NoProfile -Command "
\$p = Start-Process powershell -Verb RunAs -PassThru -ArgumentList @(
'-NoProfile', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', '$ENCODED'
);
\$p.WaitForExit()
"
まとめ posershell-admin.shを作成して、それにps1ファイルと引数を 渡せば、WSL2上でも管理者権限でいろいろ実行できる。 ps1ファイルはSJIS。何度でもいうよ、それはSJIS。 画面上で何か確認したかったら Write-Host する。 はもうしょうがない。
to be continued...? さーて、次回があってもきっと話さないまだまだあった修羅の道は? 複数のVMをHyper-Vで構築する 各VMにLinuxのisoファイルを使ってインストール(grubも扱うよ) そしてansible の三本です! ご清聴ありがとうございました!