Search code examples
powershellnetwork-programmingwindows-server-2016

Find IP and MAC address for several servers where multiple NICs are present


I've looked around several stack posts and while most come close to achieving what I'm looking for, I need to make the gathering of IP and MAC addresses a bit more dynamic.

Goal

I'm using PowerShell to do some modifications in the registry of several servers in a network. I will send an array of several server names to a function that for each server name creates a string with the following structure:

ComputerName@IPAddress:MACaddress

One of the servers in that string is going to be the same as the server I'm running the script from, but the script will not know which one it is. I need to be able to, regardless of if the server is the localhost or if it's a remote server, create the above string.

Work so far

If I run this locally I can get it to work with the following:

$ServerName = $env:COMPUTERNAME
$IPAddress = "192.168.0.1"
$MACAddress = ((Get-WmiObject win32_networkadapterconfiguration | where{$_.IPAddress -like "*$IPaddress*"} | select macaddress).macaddress).Replace(':','-')

$RegistryString = "$ServerName@$IPaddress"+":$MacAddress"

This works without issue. If I want to do it remotely the following works great:

$ServerName = "MyRemoteServer"
$IPAddress = ([System.Net.Dns]::GetHostAddresses($ServerName)).IPAddressToString
$MACAddress = ((Get-WmiObject win32_networkadapterconfiguration -ComputerName $ServerName | where{$_.IPAddress -like "*$IPaddress*"} | select macaddress).macaddress).Replace(':','-')

What I want to do is to send an array of computer names to a function that for each of these servers creates the ServerName@IPAddress:MACAddress string and enters that into the registry.

Issue

The problem is that if I try the second part of the remoting it works for remote computers but for the local computer it returns the IP addresses for all network interface cards. I tried to do the following workaround from a different Stack post:

$ServerName = "MyArbitraryServerName"
$NIC = Get-WmiObject Win32_NetworkAdapter -ComputerName $ServerName | select -First 1
$IPAddress = $NIC.GetRelated("Win32_NetworkAdapterConfiguration") | select -expand IPAddress -first 1
$MACAddress = ((Get-WmiObject win32_networkadapterconfiguration -ComputerName $ServerName | where{$_.IPAddress -like "*$IPaddress*"} | select macaddress).macaddress).Replace(':','-')

This works, but it feels a bit dirty to just do the select -first 1. I have verified that this works for most of my local machines but I get the feeling that this will not always be the case.

Question

If the select -first 1 always guarantees that I will get the correct IP (namely the IP from the local intranet) I'm fine with this, but I know that some environments have several NICs for different network segments but within the same domain so I suspect this will not work.

If the solution is to just have an if(localhost) then do something else do the remote stuff then that would work too, but I'm not sure how I would go about verifying if the server name in the array that the function is taking is a local host or not.

Any assistance is appreciated!

EDIT: Here's most of the sources I used to come up with the above:

https://social.technet.microsoft.com/Forums/windowsserver/en-US/a4512ce9-8ca2-413d-80d2-88da1e48dcd8/remote-registry-change-via-powershell?forum=winserverManagement

How can I use PowerShell to make remote registry changes?

https://blogs.technet.microsoft.com/heyscriptingguy/2014/11/07/powertip-use-powershell-to-find-mac-address/

https://www.petri.com/powershell-problem-solver-find-ip-address-using-powershell


Solution

  • I found a dirty solution where I force the user of the script to enter the local IP address. When I do the IP address lookup I also do a string-matching to verify if I'm running the script locally or not. If I am, I use the manually entered IP address. Here's the full solution:

    function global:SetServerMacMap{
    
    param(
        [Parameter(mandatory=$true)][String]$ServerName,
        [Parameter(mandatory=$true)][String]$LocalIPAddress
    )
    
    $IPAddress = ([System.Net.Dns]::GetHostAddresses($ServerName)).IPAddressToString
    
    if($IPAddress -match $LocalIPAddress){
        $IPAddress = $LocalIPAddress
    }
    
    $MACAddress = ((Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ServerName | where{$_.IPAddress -like "*$IPaddress*"} | select macaddress).macaddress).Replace(':','-')
    
    $ServerMacMap = "$ServerName@$IPaddress"+":$MacAddress"
    
    $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $NSSServerName ) 
            $regKey= $reg.OpenSubKey("SOFTWARE\\MySoftware\\Settings",$true) 
            $regKey.SetValue('ServerMacMap',$ServerMacMap,[Microsoft.Win32.RegistryValueKind]::String)
    }
    

    If there is a more elegant solution that is more dynamic (i.e., a solution that doesn't need a manually entered IP address) I'm all ears. For now this does what I aimed to do but I'm not totally happy with it.