---
title: PowerShellでHyper-V環境を構築したときの奮闘記
tags:  #phpperkaigi 2026 #phperkaigi #hyper-v #powershell #wsl2  
author: [masnmt](https://image.docswell.com/user/masnmt)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/YE9PLDXYJ3.jpg?width=480
description: ※PHPerKaigi 2026のLTで発表したときに利用したスライドです。  オンプレ構成をローカル再現で再現したいということで、Hyper-Vを利用してみました。 PowerShellは正直扱いにくいのでWSL2でやろうとしたら、とんでもなく苦労したのでそれの備忘録的なものです。 最初からPowerShellでやればこんなに苦労しなかったかもしれません。
published: May 23, 26
canonical: https://image.docswell.com/s/masnmt/KPRWM6-2026-05-23-063556
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/YE9PLDXYJ3.jpg)

PowerShellでHyper-V環境を
構築したときの奮闘記
PHPerKaigi LT大会 2026/03/22
masnmt


# Page. 2

![Page Image](https://bcdn.docswell.com/page/GE8DX32KED.jpg)

自己紹介
@masnmt (読み方はますんと)
合同会社リトルハーミットの代表やってる野本昌嗣といいます。
花火を観るのが好きで、花火鑑賞士試験に合格してたりもしてます。
あと、ボドゲとお酒も好きです。
主な戦場
好きなディストリビューション
Debian GNU/Linux
好きなエディタ
GNU Emacs
好きなDBMS
PostgreSQL
PHP
Laravel
メインブラウザ
Vivaldi
フロントエンド
Vue
好きなポインティングデバイス
トラックボール
クラウド
AWS
IMEの日本語入力方式
かな入力


# Page. 3

![Page Image](https://bcdn.docswell.com/page/LELM8V2P7R.jpg)

自己紹介
こんなボドゲ作ってたりします。
迫りくる納期と戦う
ITエンジニアの
苦悩がここに！
大惨事を隠蔽しつつ
登録ユーザ数を
伸ばせ！


# Page. 4

![Page Image](https://bcdn.docswell.com/page/4JMY6G82JW.jpg)

なんでHyper-Vを使おうと？
複数VMでオンプレ構成をローカル再現したい
ネットワーク（VLAN / セグメント）も含めて検証したい
クラウドではL2レベルの構成再現が難しい
DockerではOS/カーネル分離やネットワーク再現に限界
再現性・メンテナンスのためコード管理したい


# Page. 5

![Page Image](https://bcdn.docswell.com/page/PJR9PZ5579.jpg)

そもそもHyper-Vとは
Windowsに標準搭載のType-1ハイパーバイザー
ハードウェア直上で動作し、高速・安定
仮想マシン（Windows / Linux）を柔軟に実行可能
vSwitchでネットワーク分離・VLAN対応
※WSL2も内部的にHyper-Vを利用


# Page. 6

![Page Image](https://bcdn.docswell.com/page/PEXQ39KXJX.jpg)

PowerShellは正直扱いにくいのでWSL2で
Hyper-V関連のコマンドはWindows上で実行されます。
なんとか.exeをWSL2上からも実行できることは知っていました。
なのでデフォルトで入っているpowershell.exeも実行できるはず。
$ powershell.exe


# Page. 7

![Page Image](https://bcdn.docswell.com/page/3EK9YP59ED.jpg)

PowerShellは正直扱いにくいのでWSL2で
Hyper-V関連のコマンドはWindows上で実行されます。
なんとか.exeをWSL2上からも実行できることは知っていました。
なのでデフォルトで入っているpowershell.exeも実行できるはず。
$ powershell.exe
zsh: command not found: powershell.exe


# Page. 8

![Page Image](https://bcdn.docswell.com/page/L73W9XK975.jpg)

PowerShellは正直扱いにくいのでWSL2で
パスを通しましょう。
C:\Windows\System32\WindowsPowerShell\v1.0
なので
/mnt/c/Windows/System32/WindowsPowerShell/v1.0
これでPowerShellをたたけるようになり、以外と便利になるので、
Hyper-Vを使わなくてもおすすめです。


# Page. 9

![Page Image](https://bcdn.docswell.com/page/87DKG238JG.jpg)

Hyper-Vコマンドをたたくための確認
取得系のコマンドを実行できるか試してみる。
$ powershell.exe -c Get-VMHost


# Page. 10

![Page Image](https://bcdn.docswell.com/page/VJPK314WE8.jpg)

Hyper-Vコマンドをたたくための確認
取得系のコマンドを実行できるか試してみる。
$ powershell.exe -c Get-VMHost
Get-VMHost :
このタスクを完了するために必要な
アクセス許可がありません。
このコンピューター &#039;Hoge-PC&#039; の承認ポリシーの
管理者に問い合わせてください。


# Page. 11

![Page Image](https://bcdn.docswell.com/page/2EVV4YX9EQ.jpg)

Hyper-Vコマンドをたたくための確認
Hyper-Vのたいていのコマンドは管理者権限が必要です。
管理者権限でPowerShellを動かせばいいのです。
$ powershell.exe -c Get-VMHost
Get-VMHost :
このタスクを完了するために必要な
アクセス許可がありません。
このコンピューター &#039;Hoge-PC&#039; の承認ポリシーの
管理者に問い合わせてください。


# Page. 12

![Page Image](https://bcdn.docswell.com/page/57GL19V4EL.jpg)

Hyper-Vコマンドをたたくための確認
右クリックで「管理者として実行」はWSL2上にはない。
スクリプトで自動化を狙っているので、コマンドラインから行ける
方法として、管理者権限（昇格）で別プロセスを起動方法がある。
まずはPowerShellのターミナルで確認してみる。
PS&gt; Start-Process powershell -Verb RunAs
Get-VMHost


# Page. 13

![Page Image](https://bcdn.docswell.com/page/4EQYDW6RJP.jpg)

Hyper-Vコマンドをたたくための確認


# Page. 14

![Page Image](https://bcdn.docswell.com/page/KJ4WZK4Q71.jpg)

Hyper-Vコマンドをたたくための確認
別窓で開いて、一瞬で閉じる。
何が起きているのかわからない。
PS&gt; Start-Process powershell -Verb RunAs
Get-VMHost


# Page. 15

![Page Image](https://bcdn.docswell.com/page/LE1YRX4L7G.jpg)

Hyper-Vコマンドをたたくための確認
-NoExitを指定することで、閉じることは回避できます。
右Hyper-Vでやりたいことは1コマンド実行で終わるものではなく、
複数実行して行うものなので、ps1ファイルにまとめて記述して実行
したいので、-Fileもつけます。
PS&gt; Start-Process powershell -Verb RunAs
-ArgumentList &#039;-NoExit&#039;,&#039;-File&#039;,&#039;hoge.ps1&#039;


# Page. 16

![Page Image](https://bcdn.docswell.com/page/GEWG1NXQJ2.jpg)

Hyper-Vコマンドをたたくための確認
すぐ終了して何が起きてるのかわからん。なぜだ。
PS&gt; Start-Process powershell -Verb RunAs
-ArgumentList &#039;-NoExit&#039;,&#039;-File&#039;,&#039;hoge.ps1&#039;


# Page. 17

![Page Image](https://bcdn.docswell.com/page/47ZLP363J3.jpg)

Hyper-Vコマンドをたたくための確認
フルパスが必要。
あと、引数はちゃんと配列で渡したほうがきれい。
PS&gt; Start-Process powershell -Verb RunAs
-ArgumentList @(
&#039;-NoExit&#039;,&#039;-File&#039;,&#039;C:\hoge.ps1&#039;
)


# Page. 18

![Page Image](https://bcdn.docswell.com/page/YJ6WMX2ZJV.jpg)

Hyper-Vコマンドをたたくための確認
マジか。
PS&gt; Start-Process powershell -Verb RunAs
-ArgumentList @(
&#039;-NoExit&#039;,&#039;-File&#039;,&#039;C:\hoge.ps1&#039;
)
このシステムではスクリプトの実行が無効になっているため、ファ
イル C:\hoge.ps1 を読み込むことができません。


# Page. 19

![Page Image](https://bcdn.docswell.com/page/GJ5MZX29J4.jpg)

Hyper-Vコマンドをたたくための確認
ArgumentListでも管理者昇格が必要。
大元のpowershellとは扱いが別。さらに標準出力も消えるので、ps1
ファイルで何か画面に出したかったらWrite-Hostが必要になる。
PS&gt; Start-Process powershell -Verb RunAs
-ArgumentList @(
&#039;-NoExit&#039;,&#039;-ExecutionPolicy&#039;,&#039;Bypass&#039;,
&#039;-File&#039;,&#039;C:\hoge.ps1&#039;
)


# Page. 20

![Page Image](https://bcdn.docswell.com/page/9E29RX457R.jpg)

Hyper-Vコマンドをたたくための確認


# Page. 21

![Page Image](https://bcdn.docswell.com/page/D7Y4D8M6EM.jpg)

Hyper-Vコマンドをたたくための確認
OK
PS&gt; Start-Process powershell -Verb RunAs
-ArgumentList @(
&#039;-NoExit&#039;,&#039;-ExecutionPolicy&#039;,&#039;Bypass&#039;,
&#039;-File&#039;,&#039;C:\hoge.ps1&#039;
)


# Page. 22

![Page Image](https://bcdn.docswell.com/page/VENY6Q31J8.jpg)

さぁWSL2でやってみよう
-NoProfileを付けて、ユーザ環境によるものを一切読まずクリーンな
環境で実行することにする。
$ powershell.exe -NoProfile -Command &quot;
Start-Process powershell -Verb RunAs
-ArgumentList @(&#039;-NoExit&#039;,&#039;-NoProfile&#039;,
&#039;-ExecutionPolicy&#039;,&#039;Bypass&#039;,
&#039;-File&#039;,&#039;~/hoge.ps1&#039;)&quot;


# Page. 23

![Page Image](https://bcdn.docswell.com/page/Y79PLD9YE3.jpg)

さぁWSL2でやってみよう
すぐ終了して何が起きてるのかわからん。
あぁ、そうかWSL2でもフルパスか。
標準出力だけでなく、標準エラーも握りつぶされているのは正直キツイ。
$ powershell.exe -NoProfile -Command &quot;
Start-Process powershell -Verb RunAs
-ArgumentList @(&#039;-NoExit&#039;,&#039;-NoProfile&#039;,
&#039;-ExecutionPolicy&#039;,&#039;Bypass&#039;,
&#039;-File&#039;,&#039;~/hoge.ps1&#039;)&quot;


# Page. 24

![Page Image](https://bcdn.docswell.com/page/G78DX39K7D.jpg)

さぁWSL2でやってみよう
WSL2ではwslpathというコマンドが利用できるので、Linuxのパス
をWindows用のパスに変換して渡してあげる。
ちなみに hoge.ps1 の一行目は、とりあえず何か表示させるために、
Write-Host &quot;ほげ&quot; を記述してある。
$ powershell.exe -NoProfile -Command &quot;
Start-Process powershell -Verb RunAs
-ArgumentList @(&#039;-NoExit&#039;,&#039;-NoProfile&#039;,
&#039;-ExecutionPolicy&#039;,&#039;Bypass&#039;,
&#039;-File&#039;,&#039;$(wslpath -w ~/hoge.ps1)&#039;)&quot;


# Page. 25

![Page Image](https://bcdn.docswell.com/page/L7LM8VWPJR.jpg)

さぁWSL2でやってみよう
WSL2ではwslpathというコマンドが利用できるので、Linuxのパス
をWindows用のパスに変換して渡してあげる。
ちなみに hoge.ps1 の一行目は、とりあえず何か表示させるために、
Write-Host &quot;ほげ&quot; を記述してある。
発生場所
\\wsl.localhost\Debian\home\masnmt\phperkaigi2026\hoge.ps1:1 文
字:12
+ Write-Host &quot;縺ｻ縺・
+
~~~~~
文字列に終端記号 &quot; がありません。


# Page. 26

![Page Image](https://bcdn.docswell.com/page/4EMY6G92EW.jpg)

さぁWSL2でやってみよう
ps1ファイルの中の日本語はshift-jisでないとパースエラーになる。
ちなみにコメントでもダメ。
発生場所
\\wsl.localhost\Debian\home\masnmt\phperkaigi2026\hoge.ps1:1 文
字:12
+ Write-Host &quot;縺ｻ縺・
+
~~~~~
文字列に終端記号 &quot; がありません。


# Page. 27

![Page Image](https://bcdn.docswell.com/page/PER9PZG5J9.jpg)

これで落とし穴は全て埋まった(落ちた)
.shでシェルスクリプトにして使いやすくする。
#!/usr/bin/env bash
WPATH=$(wslpath -w $1)
powershell.exe -NoProfile -Command &quot;
Start-Process powershell -Verb RunAs -ArgumentList @(
&#039;-NoExit&#039;, &#039;-NoProfile&#039;, &#039;-ExecutionPolicy&#039;, &#039;Bypass&#039;,
&#039;-File&#039;, &#039;$WPATH&#039;
)&quot;


# Page. 28

![Page Image](https://bcdn.docswell.com/page/P7XQ39XXEX.jpg)

これで落とし穴は全て埋まった(落ちた)
実行が終わるとウインドウが残ったままになるので、-NoExitをやめ
て何かキーを押したら閉じるようにする。
try {
. \\wsl.localhost\Debian\home\masnmt\phperkaigi2026\hoge.ps1
} catch {
Write-Error $_
Write-Host &#039;
&#039;
} finally {
Write-Host
Write-Host &#039;Press any key to exit... &#039; -NoNewline
[void]($Host.UI.RawUI.ReadKey(&#039;NoEcho,IncludeKeyDown&#039;))
}
エラーが発生したので終了します


# Page. 29

![Page Image](https://bcdn.docswell.com/page/37K9YPW97D.jpg)

これで落とし穴は全て埋まった(落ちた)
ファイルパスを渡して前後にtry-catchを挟むスクリプトをコマンド
して渡す方法として、-EncodedCommand というオプションが
PowerShellにある。
こいつに渡す文字列はUTF-16LEでエンコードした文字列をBase64
にしたもの(日本語OK)。
ただし、-EncodedCommandで実行するときのカレントディレクト
リは C:\Windows\System32 になるので注意。
この方法でps1ファイルを渡しても、ps1ファイルそのものはやはり
shift-jisでないとパースエラーになる。


# Page. 30

![Page Image](https://bcdn.docswell.com/page/LJ3W9X19J5.jpg)

これで落とし穴は全て埋まった(落ちた)
#!/usr/bin/env bash
PS1_PATH=$(wslpath -w $1)
PS1_ARGS=&quot;${@:2}&quot;
WPWD=$(wslpath -w &quot;$PWD&quot;)
ENCODED=$(iconv -f UTF-8 -t UTF-16LE &lt;&lt;-__END_OF_PS1__ | base64 | tr -d &#039;\r\n&#039;
try {
Set-Location -LiteralPath &#039;$WPWD&#039;
. &#039;$PS1_PATH&#039; $PS1_ARGS
} catch {
Write-Error \$_
Write-Host &#039;
&#039;
} finally {
Write-Host
Write-Host &#039;Press any key to exit... &#039; -NoNewline
[void](\$Host.UI.RawUI.ReadKey(&#039;NoEcho,IncludeKeyDown&#039;))
}
__END_OF_PS1__
)
エラーが発生したので終了します
POWERSHELL=$(command -v powershell.exe || echo /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe)
$POWERSHELL -NoProfile -Command &quot;
\$p = Start-Process powershell -Verb RunAs -PassThru -ArgumentList @(
&#039;-NoProfile&#039;, &#039;-ExecutionPolicy&#039;, &#039;Bypass&#039;, &#039;-EncodedCommand&#039;, &#039;$ENCODED&#039;
);
\$p.WaitForExit()
&quot;


# Page. 31

![Page Image](https://bcdn.docswell.com/page/8JDKG2X8EG.jpg)

まとめ
posershell-admin.shを作成して、それにps1ファイルと引数を
渡せば、WSL2上でも管理者権限でいろいろ実行できる。
ps1ファイルはSJIS。何度でもいうよ、それはSJIS。
画面上で何か確認したかったら Write-Host する。
はもうしょうがない。


# Page. 32

![Page Image](https://bcdn.docswell.com/page/VEPK31PW78.jpg)

to be continued...?
さーて、次回があってもきっと話さないまだまだあった修羅の道は？
複数のVMをHyper-Vで構築する
各VMにLinuxのisoファイルを使ってインストール(grubも扱うよ)
そしてansible
の三本です！
ご清聴ありがとうございました！


