Operations Lab.

Archive for 8月 2016

Windows 10 with Hyper-V で NAT を使用する

leave a comment »

※ このポストで取り扱っている NAT は、内部ネットワークから外部へ通信を行う際の Source NAT(SNAT)です。実際はポート変換も実行されるため、SNAPT (Source Network Address/Port Translation)になります。

Windows 10 Anniversary Update (Version 1607)で、Hyper-V 上の仮想マシンが外部へ通信する際に NAT を行うことが(標準の機能のみで)できるようになりました。

いままでは

Hyper-V の機能、または仮想スイッチの機能としては NAT を行うことができなかったため、仮想マシンが外部と通信を行う場合は、以下何れかの方法で外部通信を実現していました。

  • 外部(External)仮想スイッチで直接外部に接続する
  • Hyper-V ホストで、「インターネット接続共有」(クライアント OS の場合)または「ルーティングとリモートアクセス」の役割(サーバー OS の場合)を構成する
  • (NAT 用の VM を作成して、NAT を適用する)
    ※この方法は、結局他の方法で NAT VM を外部接続させる必要があります。

ノート PC などで、Hyper-V ホストの外部インターネットアクセスが無線 LAN アダプターとなっているような環境では、External スイッチへ VM を直接接続しても正しく通信が行えない(※)ため、ホスト側で「インターネット接続共有」を有効化して使用していた方も多いと思います。

※ External 仮想スイッチの場合、VM の MAC アドレスは変換されない(VM の仮想 NIC に付与された MAC アドレスで通信を行う)ため、無線アクセスポイントとの通信が正しく行えません。

今回の機能(Hyper-V 仮想スイッチでの NAT)により、無線 LAN 環境下であっても、インターネット接続共有や RRAS を使用せず外部へ通信できるようになります。

利用するには

Hyper-V の機能が有効化された Windows 10 Build 14295 が必要です。Windows 10 Anniversary Update は Build 10393 なので利用することができます。Windows Server 2016 でも(リリースされれば)利用できると思われます。(サーバー OS に無線 LAN アダプターを接続する人は少ないと思いますので、直接的な恩恵は少ないと思いますが。)

注:後述の通り、NAT スイッチには制限があるため、サーバー(システム)環境で多数のセグメントを収容し、NAT を行いたい場合は、今まで通り RRAS を使うか NAT 用の VM (仮想アプライアンス)を使用したほうが良いと思います。

設定方法

以下の流れで設定します。

  1. Hyper-V ホスト側に外部通信可能なインタフェースを用意し、IP アドレスを設定する(ホストが外部通信できる状態にする)
  2. 「内部」の(Internal タイプの)仮想スイッチを作成する
  3. ホスト OS 上に作成された(Internal 仮想スイッチに接続された)仮想 NIC (vEthernet)に IP アドレスを設定する
  4. (仮想 NIC に設定した IP アドレスをレンジに含む)NAT ネットワークを設定する

ホスト側の外部通信可能なインタフェース

これは、通常通りホスト上で IP アドレスをアサインし、ホストが外部通信可能であれば OK です。外部通信用の NIC を仮想スイッチにバインドする必要はありません。

Internel vSwitch の作成

Hyper-V マネージャーまたは PowerShell で仮想スイッチを作成します。このとき、SwitchType に Internal (内部)を指定します。

※ PowerShell で作業を行う場合は「管理者として実行」します。(以下同様)

下記の例では、「Internal」という名前の内部(Internal タイプの)仮想スイッチを作成しています。(名前は他の仮想スイッチと重複しなければ何でも OK 。)

PS C:\> New-VMSwitch -Name Internal -SwitchType Internal

Name      SwitchType NetAdapterInterfaceDescription
----      ---------- ------------------------------
Internal  Internal

PS C:\> Get-VMSwitch -Name Internal | fl *

Name                                : Internal
Id                                  : c51a5bd2-c53c-48f0-af9d-742f477fbfec
Notes                               :
Extensions                          : {Microsoft Windows フィルタリング プラットフォーム, Microsoft NDIS キャプチャ}
BandwidthReservationMode            : Absolute
PacketDirectEnabled                 : False
EmbeddedTeamingEnabled              : False
IovEnabled                          : False
SwitchType                          : Internal
AllowManagementOS                   : True
NetAdapterInterfaceDescription      :
NetAdapterInterfaceDescriptions     :
IovSupport                          : False
IovSupportReasons                   :
AvailableIPSecSA                    : 0
NumberIPSecSAAllocated              : 0
AvailableVMQueues                   : 0
NumberVmqAllocated                  : 0
IovQueuePairCount                   : 0
IovQueuePairsInUse                  : 0
IovVirtualFunctionCount             : 0
IovVirtualFunctionsInUse            : 0
PacketDirectInUse                   : False
DefaultQueueVrssEnabledRequested    : True
DefaultQueueVrssEnabled             : False
DefaultQueueVmmqEnabledRequested    : False
DefaultQueueVmmqEnabled             : False
DefaultQueueVmmqQueuePairsRequested : 16
DefaultQueueVmmqQueuePairs          : 0
BandwidthPercentage                 : 0
DefaultFlowMinimumBandwidthAbsolute : 0
DefaultFlowMinimumBandwidthWeight   : 0
CimSession                          : CimSession: .
ComputerName                        : TAKAI02
IsDeleted                           : False

PS C:\>

Internal vSwitch のホスト側仮想インタフェース(vEthernet)に IP を設定

コントロールパネル(ネットワーク接続)または PowerShell で、ホスト側に作成された仮想 NIC に IP アドレスを設定します。ここで指定した IP アドレスが、VM から見た時の GW アドレスになります。

※ 上記ホスト側仮想 NIC にはデフォルトゲートウェイを設定する必要はありません。

下記の例では、192.168.137.1/24 をホスト側の仮想 NIC へアサインしています。(192.168.137.0/24 はインターネット接続共有で使用される IP アドレスレンジなので、競合する場合は他のレンジを使用したほうが良いでしょう。)

PS C:\> Get-NetAdapter | ft Name,ifIndex,InterfaceDescription,Status

Name                       ifIndex InterfaceDescription                     Status
----                       ------- --------------------                     ------
Bluetooth ネットワーク接続      20 Bluetooth Device (Personal Area Network) Disconnected
vEthernet (Internal)            12 Hyper-V Virtual Ethernet Adapter         Up
Wi-Fi                            3 Intel(R) Dual Band Wireless-AC 7260      Up

PS C:\> New-NetIPAddress -InterfaceIndex 12 -AddressFamily IPv4 -IPAddress 192.168.137.1 -PrefixLength 24

IPAddress         : 192.168.137.1
InterfaceIndex    : 12
InterfaceAlias    : vEthernet (Internal)
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 24
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Tentative
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore

IPAddress         : 192.168.137.1
InterfaceIndex    : 12
InterfaceAlias    : vEthernet (Internal)
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 24
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Invalid
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : PersistentStore

PS C:\> Get-NetIPAddress -InterfaceIndex 12

IPAddress         : fe80::103e:d542:892c:f48c%12
InterfaceIndex    : 12
InterfaceAlias    : vEthernet (Internal)
AddressFamily     : IPv6
Type              : Unicast
PrefixLength      : 64
PrefixOrigin      : WellKnown
SuffixOrigin      : Link
AddressState      : Preferred
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore

IPAddress         : 192.168.137.1
InterfaceIndex    : 12
InterfaceAlias    : vEthernet (Internal)
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 24
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Preferred
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore

PS C:\>

NAT ネットワークの設定

ホスト側で PowerShell を使用し、NAT ネットワークを作成します。New-NetNat コマンドに Name (ネットワーク識別名)と InternalIPInterfaceAddressPrefix (NAT 対象となるネットワークプレフィックス)を指定するだけです。

下記の例では、「VMNatNetwork」という名前の NAT ネットワーク(設定)を作成しています。InternalIPInterfaceAddressPrefix に指定した 192.168.137.0/24 からの通信が NAT 対象となります。

PS C:\> New-NetNat -Name VMNatNetwork -InternalIPInterfaceAddressPrefix 192.168.137.0/24

Name                             : VMNatNetwork
ExternalIPInterfaceAddressPrefix :
InternalIPInterfaceAddressPrefix : 192.168.137.0/24
IcmpQueryTimeout                 : 30
TcpEstablishedConnectionTimeout  : 1800
TcpTransientConnectionTimeout    : 120
TcpFilteringBehavior             : AddressDependentFiltering
UdpFilteringBehavior             : AddressDependentFiltering
UdpIdleSessionTimeout            : 120
UdpInboundRefresh                : False
Store                            : Local
Active                           : True

PS C:\> Get-NetNat | fl *

Store                            : Local
TcpFilteringBehavior             : AddressDependentFiltering
UdpFilteringBehavior             : AddressDependentFiltering
UdpInboundRefresh                : False
Active                           : True
Caption                          :
Description                      :
ElementName                      :
InstanceID                       : VMNatNetwork;0
ExternalIPInterfaceAddressPrefix :
IcmpQueryTimeout                 : 30
InternalIPInterfaceAddressPrefix : 192.168.137.0/24
InternalRoutingDomainId          : {00000000-0000-0000-0000-000000000000}
Name                             : VMNatNetwork
TcpEstablishedConnectionTimeout  : 1800
TcpTransientConnectionTimeout    : 120
UdpIdleSessionTimeout            : 120
PSComputerName                   :
CimClass                         : root/StandardCimv2:MSFT_NetNat
CimInstanceProperties            : {Caption, Description, ElementName, InstanceID...}
CimSystemProperties              : Microsoft.Management.Infrastructure.CimSystemProperties

PS C:\>

InternalIPInterfaceAddressPrefix は、Internal vSwitch に接続されたホスト側の仮想 NIC にアサインした IPアドレスを含んでいる必要があります。例えば、ホスト側の仮想 NIC に 192.168.0.1/24 を設定した場合、InternalIPInterfaceAddressPrefix には 192.168.0.0/24 などを指定します。

※ NAT 先の IP アドレス(所謂、External IP Address)は指定しません。動的に決定されます。

制限事項

何れも現時点においての制限事項です。(将来どうなるかは不明です。)

  • NAT ネットワークは複数作成できません。複数作成すると意図しない動作をする可能性があります。
  • Windows Container (docker)を使用すると、NAT タイプのネットワークが自動的に作成されます。コンテナと併用する場合は、自動的に作成された NetNat を流用するか、一度 NAT 定義を削除した後両方をカバー可能な広いレンジの NAT ネットワークを定義してください。
  • NAT を行うインタフェースは指定できません。IP アドレスから動的に解決されます。

参考

WMI 経由でレジストリの値を取得する

leave a comment »

Windows Server を管理していると、複数のコンピュータから特定のレジストリキー配下の値を一括で取得したい、と思うことがあります。ただ、Windows Server 2008 などの若干古い OS を対象とした場合、管理対象で PowerShell Remoting が有効になっていなかったり、WMF (Windows Management Framework)のバージョンが古かったりなどで、PowerShell だけでは一括処理が難しい場合があります。

今回は、PowerShell から WMI 経由で複数のリモートコンピューターに接続し、レジストリのサブキー配下の値を再帰的に取得してみます。

前提条件

  • ターゲットコンピューターに WMI で接続できること
  • クライアント(管理側・スクリプト実行側)コンピューター上で PowerShell 3.0 以降が実行できること(2.0 でも動く気がしますが、未検証)

※認証や委任周りで大変面倒なことになるので、今回試している環境はドメイン環境かつ Domain Admins に種族しているアカウントでスクリプトを実行しています。

リモートから複数コンピューターのレジストリ値を取得する

以下のコードは GitHub 上でも参照できます

Param(
    [parameter(Mandatory=$true)]
    [string[]]$ComputerName
)

$HKEY_LOCAL_MACHINE = 2147483650
$ns = "root\default"
$cls = "StdRegProv"
$regkey = "SOFTWARE\Policies\Microsoft"

$result = New-Object System.Collections.ArrayList

$ComputerName | % {
    $c = $_
    $wmi = Get-WmiObject -List $cls -Namespace $ns -ComputerName $c
    $r = $wmi.EnumKey($HKEY_LOCAL_MACHINE, $regkey)
    $r.sNames | % {
        $obj = New-Object -TypeName PSObject
        Add-Member -InputObject $obj -MemberType NoteProperty -Name ComputerName -Value $c
        Add-Member -InputObject $obj -MemberType NoteProperty -Name Name -Value $_
        Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "SubKey"
        Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $null
        $result.Add($obj) | Out-Null
    }
    $r = $wmi.EnumValues($HKEY_LOCAL_MACHINE, $regkey)
    if($r.ReturnValue -eq 0) {
        for($i = 0; $i -lt $r.sNames.Count; $i++) {
            $obj = New-Object -TypeName PSObject
            Add-Member -InputObject $obj -MemberType NoteProperty -Name ComputerName -Value $c
            Add-Member -InputObject $obj -MemberType NoteProperty -Name Name -Value $r.sNames[$i]
            switch ($r.Types[$i]) {
                1 { # REG_SZ
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_SZ"
                    $val = $wmi.GetStringValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.sValue
                }
                2 { # REG_EXPAND_SZ
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_EXPAND_SZ"
                    $val = $wmi.GetExpandedStringValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.sValue
                }
                3 { # REG_BINARY
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_BINARY"
                    $val = $wmi.GetBinaryValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.uValue
                }
                4 { # REG_DWORD
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_DWORD"
                    $val = $wmi.GetDWORDValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.uValue
                }
                7 { # REG_MULTI_SZ
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_MULTI_SZ"
                    $val = $wmi.GetMultiStringValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.sValue
                }
                11 { # REG_QWORD
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_QWORD"
                    $val = $wmi.GetQWORDValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.uValue
                }
                default { # Invalid Object
                }
            }
            $result.Add($obj) | Out-Null
        }
    }
}

return $result

$regkey に設定したレジストリ サブキー配下を取得しますので、適当に書き換えてください。(本来はパラメーター化すべきですが、今回は面倒だったのでそのままです。)接続先コンピューターは、スクリプトのパラメーター –ComputerName で指定できます。配列(PowerShell Console 上だとカンマ区切り)で渡せば、複数コンピューターを指定できます。

Written by kazu

2016/08/21 at 16:09

Windows 10 1607 Hyper-V で Windows Server 2016 TP5 を起動できない

leave a comment »

Windows 10 Anniversary Update (Version. 1607)が配信され、アップデート(アップグレード?)している方も多いかと思います。しかし、Windows Server 2016 Technical Preview 5 の検証を Hyper-V 仮想マシン上で実施していた方は要注意です。

Windows Server 2016 TP5 を起動できない

Windows 10 (Hyper-V ホスト側)を Version. 1607 (Build 10393)へアップデートすると、一部の環境で Windows Server 2016 TP5 をブートできなくなります。条件は以下の2つです。

  1. Gen 2 仮想マシン(第2世代仮想マシン)を使用している。
  2. ゲスト OS 側(Windows Server 2016 TP5)に KB3172729 がインストールされていない。

原因

セキュアブートの脆弱性(と言って良いか分かりませんが、セキュリティ上の問題)により、Windows Server 2016 TP5 (初期の)ブートコードがブラックリストに登録されています。Windows 10 Anniversary Update は、この新しい(Update された)ブラックリスト情報を持っているため、Windows Server 2016 TP5 の起動ができません。

回避策と対応策

回避策および恒久対応としては、以下の何れかを実施することにより起動が可能です。

  1. Gen 1 仮想マシンを使用する。
  2. セキュアブートを無効化する。
    • 単純に無効化するだけでなく、セキュアブートテンプレートを Linux 用のテンプレートに変更した後で無効化する必要があります。
  3. ゲスト OS 側に KB3172729 をインストールする。

KB3172729 はゲスト側のブートコードを更新し、ホスト側で正しく認証されるものに置き換えます。これにより、KB3172729 のインストール後は、正しくセキュアブートが機能するようになります。

KB3172729 をインストールするためには、原則として仮想マシンを起動する必要があります。(厳密には他の方法もありますが。)既に仮想マシンが起動できなくなっている場合は、上記 2 の方法で仮想マシンを一度起動し、Update を適用した後に、再度セキュアブートの設定を変更します。

具体的には、仮想マシンの設定で「セキュリティ」を選択し、テンプレートで「Microsoft UEFI 証明機関」を選択した後、「セキュアブートを有効にする」のチェックを外します。テンプレートが「Microsoft Windows」に設定されているとセキュアブートを無効化しても OS をブートできません。

変更前

win10-ws2016tp5-vm-002

変更後

win10-ws2016tp5-vm-003

仮想マシンが起動できたら、早々に Update を適用し、せえきゅあブートの設定を元に戻しておきましょう。

 

Written by kazu

2016/08/20 at 02:54

UEFI システム上で bootsect.exe が使えない?

leave a comment »

結論

bootsect.exe が古いです。

いきさつ

久しぶりに(?)Windows Server 2008 R2 を物理サーバーにインストールする必要が生じたため、Windows Server 2008 R2 のメディアから、インストール用の USB を作成しようとしたところ、bootsect.exe が動きませんでした。

bootsect-efi

E:\boot>bootsect.exe /nt60 D:
This tool can only be run on systems booted using a PC/AT BIOS.  This system was booted using EFI or some other firmware type.

使用したのは Windows Server 2008 R2 のメディアに入っている bootsect.exe です。パーティションを作成し、メディアの中身を xcopy でコピーした後、bootsect でブートローダーを書き込もうとしたところ、エラーになりました。

原因

Windows Server 2008 R2 の(おそらく Windows 7 も)bootsect.exe が古く、UEFI ブートしたシステム上では動作しないようになっているためです。最近の(少なくとも Windows 10 の)bootsect.exe は、UEFI ブートした OS 環境からも利用できるように(BIOS ブート向けのブート情報を書き込めるように)なっています。

対処法

Windows 10 などの bootsect.exe をそのまま使用します。

bootsect-efi2

UEFI ブートしているシステムでも C:\Windows\System32 配下に bootsect.exe などがインストールされているはずです。(デフォルトでパスが通っているので、インストールメディアの boot フォルダーなど別の bootsect.exe が存在するパスに移動していなければ、そのまま使えると思います。)

まとめ

いざという時のために、最新の Windows ADK から作成した WinPE 環境を USB ブートできるようにしておく(常に持ち歩く)と安心かも。

Written by kazu

2016/08/13 at 19:45