PowerShell Problem Solver: Find IP Address Using PowerShell

Here’s another problem to solve with PowerShell: Find the IP address of a remote computer. Sounds simple enough. However, as with many tasks in the world of PowerShell, there are several ways to get the job done. I hope that even if you don’t need to solve this task that you’ll take a few minutes to read the article because you might learn a new technique or cmdlet that you could use elsewhere. Some of the techniques will require that you have administrator privileges on the remote computer. Some will be firewall friendly and some not. You can decide which approach works for you.

We’ll begin by defining a variable for computername so that we can use it in all of the techniques. Another advantage is that it makes it easier to turn your test code into a PowerShell function.

$computername = "chi-core01"

Let’s begin.

Test-Connection

The first technique is to use the Test-Connection cmdlet. This is a PowerShell version of ping.

Using the Test-Connection cmdlet in PowerShell. (Image Credit: Jeff Hicks)
Using the Test-Connection cmdlet in PowerShell. (Image Credit: Jeff Hicks)

By default, the cmdlet pings four times, and you can see there is an IPv4 address. To simplify, I will select only the information I care about and limit test to a single ping.

Test-Connection $computername -count 1 | select Address,Ipv4Address

Using the Test-Connection cmdlet in PowerShell to limit to a single ping. (Image Credit: Jeff Hicks)
Using the Test-Connection cmdlet in PowerShell to limit to a single ping. (Image Credit: Jeff Hicks)

You may be wondering why I used Address and not Destination. That’s because Destination is added by PowerShell for formatting purposes. The actual property name, which you can find with Get-Member is Address. If you don’t like the Address heading, rename it.

Test-Connection $computername -count 1 | select @{Name="Computername";Expression={$_.Address}},Ipv4Address

Renaming the Address heading. (Image Credit: Jeff Hicks)
Renaming the Address heading. (Image Credit: Jeff Hicks)

This is a pretty easy technique and doesn’t require any special permissions.

WMI and CIM

Another approach is to use WMI. You can either use the WMI or the CIM cmdlets which were introduced in PowerShell 3.0. The syntax is essentially the same.

Get-WmiObject win32_networkadapterconfiguration -filter "ipenabled = 'True'" -ComputerName $computername |
Select PSComputername,
@{Name = "IPAddress";Expression = {
[regex]$rx = "(\d{1,3}(\.?)){4}"
$rx.matches($_.IPAddress).Value}},MACAddress

Because the IPAddress property could be a collection of IPv4 and IPv6 addresses, I’m using a regular expression pattern to get only the IPv4 address. And since it was there, I decided to grab the MAC address as well.

Grabbing MAC addresses in Windows PowerShell. (Image Credit: Jeff Hicks)
Grabbing MAC addresses in Windows PowerShell. (Image Credit: Jeff Hicks)

You can get the same result using the CIM cmdlets.

Get-CimInstance win32_networkadapterconfiguration -filter "ipenabled = 'True'" -ComputerName $computername |
Select PSComputername,
@{Name = "IPAddress";Expression = {
[regex]$rx ="(\d{1,3}(\.?)){4}"
$rx.matches($_.IPAddress).Value}},MACAddress

The benefit here is that querying WMI is done over WSMAN instead of DCOM, which makes this much more firewall friendly.

NetAdapter Module

When Windows 8 and Windows Server 2012 shipped, they included some new cmdlets that wrapped up CIM classes. These cmdlets were packaged as modules and were much easier to use than trying to query WMI directly. For our purposes, there is a Get-NetIPAddress cmdlet that is part of the NetAdapter module. The cmdlet requires that the target computer be running at least PowerShell 3.0. Or you can use a CIM Session object you defined separately.

Get-NetIPAddress -CimSession $computername -AddressFamily IPv4 |
where { $_.InterfaceAlias -notmatch 'Loopback'} |
Select PSComputername,IPAddress		

I wanted to limit the query to IPv4 addresses and filter out the loopback adapter.

Filtering the loopback adapter. (Image Credit: Jeff Hicks)
Filtering the loopback adapter. (Image Credit: Jeff Hicks)

Since I’ve started to include the MAC address, I wanted to include it here but Get-NetIPAddress doesn’t include that property. Fortunately there is another cmdlet in the module called Get-NetAdapter which will provide the MAC and I can pipe an IP Address object to it.

Get-NetIPAddress -CimSession $Computername -AddressFamily IPv4 |
where { $_.InterfaceAlias -notmatch 'Loopback'} |
Select PSComputername,IPAddress,
@{Name = "MACAddress";Expression={
 ($_ | Get-NetAdapter).MACAddress
}}

Again, I’m using a custom property with Select-Object that pipes the IP Address object to Get-Net-Adapter and retrieves only the MAC Address.

Using a custom property with the Select-Object cmdlet. (Image Credit: Jeff Hicks)
Using a custom property with the Select-Object cmdlet. (Image Credit: Jeff Hicks)

But I’m picky. I don’t like the format of the MAC address. Instead of dashes I want colons so that MAC address looks like it did in the WMI queries. All I have to do is tell PowerShell to replace them.

Get-NetIPAddress -CimSession $computername -AddressFamily IPv4 |
where { $_.InterfaceAlias -notmatch 'Loopback'} |
Select PSComputername,IPAddress,
@{Name = "MACAddress";Expression={
 ($_ | Get-NetAdapter).MACAddress.Replace("-",":")
}}	

The MAC Address is a string so all I am doing is invoking the Replace() method and specifying what characters I want to replace.

Using the Replace() method to specify characters that will be replaced. (Image Credit: Jeff Hicks)
Using the Replace() method to specify characters that will be replaced. (Image Credit: Jeff Hicks)

DNS

In addition to the NetAdapter module, you might also find a module for DNS. This module includes cmdlets for resolving host names from DNS. Whereas the previous techniques all required connecting with the target computer, this approach doesn’t require that. I’ll let you find time to read cmdlet help and examples.

Resolve-DNSName -Name $computername -type A | Select Name,IPAddress

I wanted to limit my query and select specific properties.

Selecting specific properties in Windows PowerShell. (Image Credit: Jeff Hicks)
Selecting specific properties in Windows PowerShell. (Image Credit: Jeff Hicks)

I like this technique because you don’t need permission to query the destination computer, only resolve something in DNS, which everyone should be able to do. In fact, let’s combine this with my first technique.

Resolve-DNSName -Name $computername -type A |
Select Name,IPAddress,
@{Name="Responding";Expression={ Test-Connection $_.Name -quiet}}

This code will now include a new property indicating if the computer is responding to pings.

Checking if the computer is responding to pings in Windows PowerShell. (Image Credit: Jeff Hicks)
Checking if the computer is responding to pings in Windows PowerShell. (Image Credit: Jeff Hicks)

You could speed this up a bit by limiting the ping count in Test-Connection.

Old School Approach

Finally, let’s look at an “old school” approach. You can use the NBTStat utility to identify a computer’s IP and MAC addresses.

nbtstat -a $computername

The parameters are case-sensitive so be sure to use a lower-case ‘a’.

Using the NBTStat utility to determine computer's IP and MAC address. (Image Credit: Jeff Hicks)
Using the NBTStat utility to determine computer’s IP and MAC address. (Image Credit: Jeff Hicks)

All I need to do is extract the relevant information and for that I’ll turn again to regular expressions. First, let me save the output as one long string.

$in = nbtstat -a $computername | out-string

Next I have a regular expression pattern that uses named captures to get text that looks like IP and MAC addresses.

[regex]$rx = "(?<IPAddress>(\d{1,3}\.){3}\d{1,3})[\s\S]*(?<MAC>(\w{2}-){5}\w{2})"

Here’s the tricky part. I need to go through the collection of matches and pull out the information from the named captures. I also know I want to write an object to the pipeline.

$rx.Matches($in) | foreach-object -begin {
 $hash = [ordered]@{Computername = $computername}
} -process {
 $ip = $_.groups["IPAddress"].Value | Where {$_ -ne '0.0.0.0'}
 $hash.Add("IPAddress",$ip)
 $mac = $_.groups["MAC"].Value
  $hash.add("MACAddress",$mac)
} -end {
[pscustomobject]$hash
}

In the Begin. scriptblock of ForEach-Object, I’m defining an ordered hashtable, which is something new in v3. The first entry is the computername. Then for each match, I’m extracting the values from my named captures. Although for IP, I need to filter out any 0.0.0.0 addresses that will also match my regular expression pattern. It is possible to write a regular expression pattern to exclude this address but I didn’t want to make my pattern any more complicated than it already is. Finally, the End scriptblock turns the hashtable into a custom object.

Turning the hashtable into a custom object. (Image Credit: Jeff Hicks)
Turning the hashtable into a custom object. (Image Credit: Jeff Hicks)

Oh. I want colons for the MAC. No problem. I’ll simply tack on the Replace method.

$mac = $_.groups["MAC"].Value.replace("-",":")

Customizing the MAC address with the Replace() method. (Image Credit: Jeff Hicks)
Customizing the MAC address with the Replace() method. (Image Credit: Jeff Hicks)

And there you have it. Several different techniques and commands for finding a computer’s IP address with PowerShell. It wouldn’t take too much effort to turn any of these into more feature-rich tools including credentials, multiple computer names and error handling.
I hope PowerShell is solving some problems for you. But if not, please post something in the PowerShell forum here at Petri.