Windows EC2インスタンスのDNS設定をPowerShellから行う

しばたです。

本記事はWindows Server 2012以降が対象で、EC2インスタンスに限らずどの環境のWindows Serverでも適用可能な内容となります。
今回EC2インスタンスで検証したのでこの様なタイトルにしてあります。

DNS設定を行うPowerShellコマンドレット

Windows Server 2012からネットワーク接続のDNSサーバー設定を行うために以下のコマンドレットが追加されています。

他にもDnsClientモジュールとして様々なコマンドレットが提供されています。

WindowsのNIC名称(InterfaceAlias)

ここでSet-DnsClientServerAddressコマンドレットを使うには対象NICの指定が必要で、この指定はInterfaceIndexパラメーターで番号指定するのが原則なのですが、この番号はマシン毎で変わるため現実的にはNIC名称であるInterfaceAliasパラメーターを指定することが多いかと思います。

# NICのインデックスがあらかじめわかっている場合
Set-DnsClientServerAddress -InterfaceIndex 1 -ServerAddresses @("192.168.1.xxx", "192.168.1.yyy")

# NIC名称(InterfaceAlias)を指定する場合
Set-DnsClientServerAddress -InterfaceAlias "イーサネット" -ServerAddresses @("192.168.1.xxx", "192.168.1.yyy")

ただ、WindowsにおいてNIC名称はOSバージョンや言語によって変わってしまうため一意に決めるのが非常に難しく、Windowsの非常にイケていない部分の一つでもあります。

(英語版Windows)

(日本語版Windows)

とはいえ、このコマンドレットが使えるWindows Server 2012以降のEC2においてはEthernetおよびイーサーネットで始まるものと考えて差し支えないでしょう。
(複数ENIをアタッチし複数NICとなる場合は連番が付きます)

また、DNS設定可能なNICの一覧はGet-DnsClientコマンドレットで取得できます。

Get-DnsClient

Windows EC2インスタンスのDNS設定をPowerShellから行う

ここまでを踏まえてWindows EC2インスタンスのDNS設定を行うスクリプトを以下に記載します。

#
# このコマンドは Windows Server 2012以降で実行可能
#

# DNSサーバーの一覧を配列定義
$dnsServers = @("192.168.1.xxx", "192.168.1.yyy")

# NICの名称は言語ごとで異なるので注意
$nicAlias = switch ((Get-Culture).LCID) {
    1041    {"イーサネット*"; break}
    Default {"Ethernet*" ; break}
}

# DNS Clientオブジェクト取得
$client = Get-DnsClient -InterfaceAlias $nicAlias
# 変更前設定確認
$client | Get-DnsClientServerAddress -AddressFamily IPv4
# 設定変更
$client | Set-DnsClientServerAddress -ServerAddresses $dnsServers
$client | Get-DnsClientServerAddress -AddressFamily IPv4

そんなに難しいことはしてませんが、OSの言語設定を見てEthernetおよびイーサーネットで始まるNIC情報を取得、その情報をもとにSet-DnsClientServerAddressでDNSサーバー設定を実施しています。
最初の例ではInterfaceAliasパラメーターを直接指定していましたが、Get-DnsClientで取得されるCIMオブジェクト *1をパイプラインで引き渡すことも可能なためこのスクリプトではそうしています。

NICのプロパティ画面から見てもきちんと更新されています。

また、DNS設定を元に戻したい場合はSet-DnsClientServerAddressに-ResetServerAddressesパラメーターを指定します。

# 元に戻したい場合
$client | Set-DnsClientServerAddress -ResetServerAddresses
$client | Get-DnsClientServerAddress -AddressFamily IPv4

これで元に戻りました。

【2019.10.05 追記】もっとEC2に特化した処理を考えてみた

最初にブログを公開した直後に「これインスタンスメタデータを使えば厳密にNIC特定できないかなぁ?」と思い至り調べてみました。

インスタンスメタデータにはPrivateなIPv4アドレスを取得するlocal-ipv4データがあり、

インスタンスのプライベート IPv4 アドレス。複数のネットワークインターフェイスが存在する場合、これは eth0 デバイス (デバイス番号が 0 のデバイス) を示します。

と記載されています。
これを使えば複数NICある様な環境でも特定のNICだけ対象とすることができそうです。
インスタンスメタデータはInvoke-RestMethodで以下の様にして取得することができますので、

Invoke-RestMethod http://169.254.169.254/latest/meta-data/local-ipv4

最終的に次の様な感じで特定NIC(eth0)のDNS設定を更新することができます。

# DNSサーバーの一覧を配列定義
$dnsServers = @("192.168.1.xxx", "192.168.1.yyy")

# インスタンスメタデータの local-ipv4 合致するIP情報からDNS Clientオブジェクトを取得
$client = Get-NetIPAddress | 
    Where-Object { $_.IPAddress -eq (Invoke-RestMethod http://169.254.169.254/latest/meta-data/local-ipv4) } |
    Get-DnsClient

# 変更前設定確認
$client | Get-DnsClientServerAddress -AddressFamily IPv4

# 設定変更
$client | Set-DnsClientServerAddress -ServerAddresses $dnsServers
$client | Get-DnsClientServerAddress -AddressFamily IPv4

きちんと複数NICのうち特定のもの(イーサネット)のみ更新できています。

【2019.10.05 追記】NICが一つしかない場合

また、NICが一つしかないことがはっきりしているのであればGet-NetAdapterコマンドレットが単一の結果を返すので、

# NIC情報からDNS Clientオブジェクト取得
$client = Get-NetAdapter | Get-DnsClient
# 変更前設定確認
$client | Get-DnsClientServerAddress -AddressFamily IPv4
# 設定変更
$client | Set-DnsClientServerAddress -ServerAddresses $dnsServers
$client | Get-DnsClientServerAddress -AddressFamily IPv4

の様に記述することも可能です。
こちらの方がNIC名称の判定が無くより汎用的な処理になりますね。

最後に

以上となります。
このスクリプトをユーザーデータに仕込んだりSSM Run Commandから実行することで設定の自動化ができますので機会があればぜひお試しください。

脚注

  1. Microsoft.Management.Infrastructure.CimInstance#ROOT/StandardCimv2/MSFT_DNSClient