Hyper-VのネットワークをPowerShellで監視する

#はじめに
この記事ではPowerShellを使ってHyper-Vのネットワークを監視する方法についてまとめます。

[Micosoftの公式サイト](https://docs.microsoft.com/en-us/windows-server/administration/performance-tuning/role/hyper-v-server/detecting-virtualized-environment-bottlenecks)にHyper-Vのネットワークのボトルネック検出のために、パフォーマンスカウンタを使用できると書かれています。
パフォーマンスカウンタの値を取得する方法はいくつかあるのですが、今回はPoweShellで取得します。
ただ、コマンドレットをそのまま実行しただけでは結果が少し見づらいので、見やすくするスクリプトを書いてみました。

#実行環境
【ホストの環境】
・Windows 10 pro 21H1
・PowerShell 5.1
・仮想マシンの構成バージョン 9.0

すでにHyper-Vのインストール、仮想マシンや仮想スイッチの設定は完了していることを前提にしています。
またPowerShellはデフォルトでスクリプト実行が制限されているので、事前に実行ポリシーを変更しておくことをおすすめします。

参考: [Windows Hyper-Vによる仮想マシンの作成と仮想スイッチの作成](https://qiita.com/Shinya_IDENO/items/aecccaa0cc1e58190aff)
参考: [PowerShell のスクリプトが実行できない場合の対処方法](https://qiita.com/Targityen/items/3d2e0b5b0b7b04963750)

#仮想マシンごとの通信量
PowerShellのコマンドレット[Get-Counter](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.diagnostics/get-counter?view=powershell-7.2)を使用します。
パフォーマンスモニタのGUIを使用して取得もできますが、PowerShellであればコマンドレット実行で取得できるので楽です。
ただ、実行結果が分かりづらい……

```powershell:PowerShell
Get-Counter -Counter "\Hyper-V Virtual Network Adapter(*)\Bytes/sec"
```

```:実行結果
Timestamp                  CounterSamples                                                                                      
---------                  --------------                                                                                      
2021/11/17 1:40:33         \\xxxxx\hyper-v virtual network adapter(windows10-1_ネットワーク アダプター_xxxx-aaaa)\bytes/sec :                                                
                           346.09089824452                                                                                     
                                                                                                                               
                           \\xxxxx\hyper-v virtual network adapter(windows10-1_ネットワーク アダプター_xxxx-bbbb)\bytes/sec :                                                
                           0                                                                                                   
                                                                                                                               
                           \\xxxxx\hyper-v virtual network adapter(windows10-2_ネットワーク アダプター_yyyy-aaaa)\bytes/sec :                                                
                           0                                                                                                   
                                                                                                                               
                           \\xxxxx\hyper-v virtual network adapter(windows10-2_ネットワーク アダプター_yyyy-aaaa)\bytes/sec :                                                
                           346.09089824452                                                                                     
                                                      
```

CounterSamplesのカラムを見ると分かりますが、パフォーマンスカウンタのインスタンスにネットワークアダプタのIDが含まれています。
そこでネットワークアダプタのIDから仮想スイッチ名を取得して、もう少しわかりやすくします。
そこで完成したのが以下のスクリプトです。(__管理者権限__で実行する必要があります)

```powershell:PowerShell
$VNATraffic = Get-Counter -Counter "\Hyper-V Virtual Network Adapter(*)\Bytes/sec"

Get-VMNetworkAdapter * | % {
    $ID = $_."ID" -replace "Microsoft:", "" -replace "\\", "--"
    $CounterSample = $VNATraffic.CounterSamples | ? {$_.InstanceName -match $ID}
    
    [pscustomobject]@{ `
        VMName = $_.VMName; `
        SwitchName = $_.SwitchName; `
        "Bytes/sec" = $CounterSample.CookedValue `
    }
}
```

```:実行結果
VMName      SwitchName      Bytes/sec
------      ----------      ---------
Windows10-2 Internal Switch         0
Windows10-2 Private Switch          0
Windows10-1 Internal Switch      4231
Windows10-1 Private Switch          0
```
これでどの仮想マシンが、どのネットワークで、どのくらい通信しているのか見やすくなったと思います。

ちょっと気持ち悪いのはネットワークアダプタのIDの変換が必要なことです。
なぜかGet-VMNetworkAdapterで取得するIDと、パフォーマンスカウンタでのIDが微妙に表記が異なります……


#ネットワークアダプタごとの通信量
ネットワークアダプタの通信量も、仮想マシンごとの通信量と同様の方法で見やすくできます。

```powershell:PowerShell
$NITraffic = Get-Counter -Counter "\Network Interface(*)\Bytes Total/sec"

Get-NetAdapter | % {
    $Name = $_."InterfaceDescription" -replace "\(", "["  -replace "\)", "]" -replace "\#", "_"
    $CounterSample = $NITraffic.CounterSamples | ? {$_.InstanceName -contains $Name}

    [pscustomobject]@{ `
        AdapterName = $_.Name; `
        InterfaceDescription = $_.InterfaceDescription; `
        "Bytes/sec" = [int]$CounterSample.CookedValue;
    }
}
```

```:実行結果
AdapterName                 InterfaceDescription                             Bytes/sec
-----------                 --------------------                             ---------
Wi-Fi                       Marvell AVASTAR 350N Wireless Network Controller     11675
vEthernet (Default Switch)  Hyper-V Virtual Ethernet Adapter                         0
vEthernet (Internal Switch) Hyper-V Virtual Ethernet Adapter #2                      0
```

このパフォーマンスカウンタ( \Network Interface(*)\Bytes Total/sec )のインスタンスには機器名が含まれています。
その機器名からアダプタ名を取得しているのですが、ここでちょっとした罠があります。
Hyper-Vで仮想スイッチを作成すると機器名に"#"がつくんですが、パフォーマンスカウンタでは"#"は使用できないので"_"に変換されます。
パフォーマンスモニタのGUIとコントロールパネルの画面を比べると何を言っているのかわかると思います。
![無題001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/930094/001b26c8-cbb9-488f-02db-ce5fa0d8d48e.png)

この現象は"#"以外の特定の文字でも起きます。
一部の文字がパフォーマンスカウンタでは特別な意味を持っているためです。( [Specifying a Counter Path ](https://docs.microsoft.com/en-us/windows/win32/perfctrs/specifying-a-counter-path))
どの文字がどの文字に変換されるのかは記載が見当たらなかったので、自分が遭遇した文字だけメモしておきます。

|デフォルト名に含まれている文字|パフォーマンスカウンタでの文字|
|--|--|
|#|_|
|(|[|
|)|]|
|\|--|
|/|不明|
|*|不明|



#終わりに
アプリケーションごとにIDの表記が微妙に違うので変換が面倒です……
同じMS製品なので統一してほしいところです。
ほかにスマートな方法をご存知の方は教えてくださるとうれしいです。