I'm trying to ge the DNS servers of network interfaces via WMI that are static (placed by the user). I have this script that works, except for the static part of course:
Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DNSServerSearchOrder -ne $null} | Select DnsServerSearchOrder,Index,InterfaceIndex
This leads to an output like so:
DnsServerSearchOrder Index InterfaceIndex
-------------------- ----- --------------
{192.168.122.1} 1 6
{1.1.1.1} 2 10
The interface with 192.168.122.1
has it's DNS setup to DHCP so that value is not good for me. How do I filter out interfaces where dns is not static? Any ideas?
netsh interface ip show config
:
Configuration for interface "Ethernet"
DHCP enabled: Yes
IP Address: 192.168.122.130
Subnet Prefix: 192.168.122.0/24 (mask 255.255.255.0)
Default Gateway: 192.168.122.1
Gateway Metric: 0
InterfaceMetric: 35
DNS servers configured through DHCP: 192.168.122.1
Register with which suffix: Primary only
WINS servers configured through DHCP: None
Configuration for interface "Ethernet 2"
DHCP enabled: Yes
IP Address: 10.0.0.17
Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0)
Default Gateway: 10.0.0.1
Gateway Metric: 0
InterfaceMetric: 35
Statically Configured DNS Servers: 1.1.1.1
Register with which suffix: Primary only
WINS servers configured through DHCP: None
Notice the difference in terms between Statically Configured DNS Servers
and DNS servers configured through DHCP
. I figured I might parse this output but I'm not sure if I can rely on this text if windows language/locale is changed and I'd rather use the WMI interface if possible.
I thought this might be available in the System.Net.NetworkInformation
namespace, but evidently not. I looked through a few lower-level Windows networking APIs thinking certainly it must exist somewhere in there, but no such luck. After running dumpbin
on netsh.exe
to see what kinds of libraries/functions it's consuming, I did get an idea of one other place to look: the registry.
As it happens, if you look in the registry under the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\
key you will see a key for each network interface with the name in GUID format. Within an interface's key you will find two values of interest: DhcpNameServer
and NameServer
. On my client system where the DNS server is set by DHCP, DhcpNameServer
contains that DNS server's IP address and NameServer
contains an empty [String]
. If I manually set a DNS server while keeping the automatically-assigned address for the interface itself, NameServer
then contains that manually-set DNS server address while DhcpNameServer
still contains the same DNS server specified by DHCP.
Based on these observations, it would seem that...
DhcpNameServer
value always contains the DNS server(s) specified by DHCP.NameServer
value always contains the DNS server(s) specified manually.Win32_NetworkAdapterConfiguration.DNSServerSearchOrder
property) the result will contain the value of NameServer
, if provided, otherwise the value of DhcpNameServer
, if provided. In other words, the system tells you which DNS servers are currently being used, but not how their addresses were specified.NameServer
has a non-empty value.Thus, given an interface with ID $interfaceID
, you can build a path to its registry key like this...
$interfaceKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\$interfaceID"
...and then retrieve the dynamically-assigned and manually-assigned nameserver values like this...
Get-ItemProperty -Path $interfaceKeyPath -Name 'DhcpNameServer', 'NameServer'
That just leaves the matter of from where you get the value for $interfaceID
, and there are numerous sources although the trick will be excluding undesirable interfaces (for example, on my Windows 10 system I have a packet capture loopback adapter and a hypervisor adapter that I'd want to be excluded from such a query). The most compatible way (dating back to .NET 2.0) would be the Id
property of the NetworkInterface
class...
[System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() `
| Select-Object -ExpandProperty 'Id'
...although the only useful properties on which to filter are Name
and NetworkInterfaceType
.
On Windows Vista and above the Win32_NetworkAdapter
class provides a GUID
property...
Get-WmiObject -Class 'Win32_NetworkAdapter' -Property 'GUID' -Filter 'PhysicalAdapter = true'
...although even when filtering on PhysicalAdapter
it still returns the loopback and hypervisor adapters and I'm not seeing any definitive property or class relation that can be used to select only hardware adapters.
The Win32_NetworkAdapterConfiguration
class is much the same...
Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration' -Property 'SettingID'
...with no properties to filter out non-hardware or even non-physical adapters.
On (I think) Windows 8 and above there's the Get-NetConnectionProfile
cmdlet...
Get-NetConnectionProfile | Select-Object -ExpandProperty 'InstanceID'
which is documented to get "a connection profile associated with one or more physical network adapters" and, on my system, it does only return my physical adapter.
There is also the Get-NetAdapter
cmdlet...
Get-NetAdapter -Physical `
| Where-Object -Property 'EndPointInterface' -NE -Value $true `
| Select-Object -ExpandProperty 'InterfaceGuid'
I found that passing the -Physical
parameter excluded the hypervisor adapter but not the loopback adapter, so filtering out where EndPointInterface
is $true
was necessary to eliminate that. The HardwareInterface
and Virtual
properties might also be of interest.
Another option would be to invoke the Get-NetAdapterHardwareInfo
cmdlet, which seems to know how to distinguish true hardware adapters, and let that determine which adapters are retrieved by Get-NetAdapter
...
Get-NetAdapterHardwareInfo `
| Get-NetAdapter `
| Select-Object -ExpandProperty 'InterfaceGuid'
The Get-Net*
cmdlets above return CIM instances so, for example, instead of Get-NetAdapter -Physical
you could use something like...
Get-WmiObject -Namespace 'Root\StandardCimv2' -Class 'MSFT_NetAdapter' `
-Property 'InterfaceGuid' -Filter 'HardwareInterface = true AND EndPointInterface = false'
to retrieve the MSFT_NetAdapter
instances just the same. I'm not really sure what the guidance is on using one versus the other. It would seem like one should prefer the cmdlets, and yet, unlike WMI/CIM, they offer limited/no parameters for efficiently filtering the output or specifying which properties are desired so you have to do that in the pipeline. I think it's noteworthy, though, that I wasn't able to find any current documentation for these MSFT_*
class; they all say they're no longer updated, except for the MSFT_NetConnectionProfile
class for which I couldn't find any documentation page at all. That says to me Microsoft doesn't want you relying on any definite structure of these classes, and yet if the cmdlets just pass along those class instances...I'm not sure how can you meaningfully and reliably interact with them if nothing is documented.
Also, keep in mind that you'll want to prefer Get-CimInstance
and its ilk over Get-WmiObject
, where possible. I don't think I've yet encountered an instance where it was any more complicated than changing Get-WmiObject
to Get-CimInstance
, although there are more differences (not necessarily bad) than the name.