A domain controller is being retired and every statically configured Windows machine still points at it. To update DNS servers across a list of hosts you do not need a management platform — PsExec takes a text file of targets and runs netsh on all of them.

Before you do, read the last section: for most fleets this is the wrong way to update DNS servers, and it is worth knowing why before you spend an afternoon on it.

The host list

Download PsTools from Sysinternals and create a text file with one IP address or hostname per line — D:\hosts.txt here:

192.168.20.101
192.168.20.102
192.168.20.103
192.168.20.104

Update DNS servers: set the primary

PsExec @D:\hosts.txt -n 5 -accepteula -u <username> netsh interface ip set dns name="Interface Name" static 192.168.10.10

-n 5 caps the connect wait so one powered-off machine does not stall the whole list, and -accepteula stops the licence dialog on the first run.

@D:\hosts.txt is the PsExec syntax for "run this on every host in that file". <username> must be an administrator on the targets.

Add a secondary DNS server

PsExec @D:\hosts.txt -u <username> netsh interface ip add dns name="Interface Name" 192.168.10.11 index=2

Note what is missing from the second command: the word static. set dns … static replaces the whole list with one server; add dns … index=2 inserts one at position 2 and pushes anything already there down the list. Use set first, then add — in the other order the set wipes what you just added.

The trap: "Interface Name" is not the same everywhere

This is what turns a five-minute job into an afternoon. The interface is called Ethernet on one machine, Ethernet0 on the VM template from three years ago, Ethernet 2 where a NIC was replaced, and Local Area Connection on anything old enough. netsh returns an error on every host where the name does not match, and in a run of a couple of hundred you will not read all of it.

Find out first:

PsExec @D:\hosts.txt -u <username> netsh interface show interface

If the names are not uniform, do not force them to be. Use PowerShell instead, which can address the adapter by state rather than by name — bearing in mind it needs WinRM enabled on the targets and Windows 8 / Server 2012 or later for the NetAdapter and DnsClient modules. PsExec only needs SMB and admin rights, which is why the oldest machines usually stay on the netsh path.

Invoke-Command -ComputerName (Get-Content D:\hosts.txt) -Credential (Get-Credential) -ScriptBlock {
    Get-NetAdapter -Physical | Where-Object Status -eq "Up" |
        Set-DnsClientServerAddress -ServerAddresses 192.168.10.10, 192.168.10.11
}

Two details in there that matter. -Physical keeps it off Hyper-V's vEthernet adapters, VPN and Bluetooth interfaces, which all report Up too. And PowerShell remoting cannot authenticate to a bare IP address with Kerberos — so either put hostnames in hosts.txt, or add those addresses to TrustedHosts on the machine you are running from.

That sets both servers in one call, on whatever the working adapter happens to be called, and it reports per machine instead of per line of netsh output.

Verify

Invoke-Command -ComputerName (Get-Content D:\hosts.txt) -ScriptBlock {
    Get-DnsClientServerAddress -AddressFamily IPv4 |
        Where-Object ServerAddresses |
        Select-Object InterfaceAlias, ServerAddresses
}

netsh interface ip show dnsservers is the one that answers the question this post ends on: it labels the list either Statically Configured DNS Servers or DNS servers configured through DHCP. Get-DnsClientServerAddress shows the effective servers either way and cannot tell you which.

Or ipconfig /all through PsExec, if you would rather stay with one tool. Either way, confirm on a sample rather than assuming the run did what it printed. Check a machine that was not in the list too — that is how you notice you pointed the file at the wrong OU export.

Undo it

PsExec @D:\hosts.txt -u <username> netsh interface ip set dns name="Interface Name" source=dhcp
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ResetServerAddresses

That clears the static list and hands the interface back to the scope at the next renewal. It is the command you will want the day you find a machine somebody pinned years ago.

When you should not do this at all

If those machines get their addressing from DHCP, changing DNS with netsh writes a static entry that overrides the scope and never changes again. The next time DNS moves, this list of machines is the one nobody remembers. Change DHCP option 006 on the scope instead and let the clients pick it up on renewal: one edit, no host list, and no machine left behind.

So: update DNS servers by script for machines with fixed addressing, for machines that must keep working while DHCP is being rebuilt, and for the handful of devices that were configured by hand years ago. For everything else, fix it at the source.

The same PsExec mechanics — the @file list, the -u account, the requirements on the target — are covered in more detail in creating a user on a remote workstation.

Leave a Reply