Identifying Website Visitor IP Addresses Using PowerShell

Today’s PowerShell Problem Solver focuses on a problem of my own. As you might imagine, running a blog is a time-consuming task, where I frequently run into performance or security problems that often leads me to downloading raw log files. Although processing this data can be tedious, PowerShell can help. One task I wanted is perform is to identify a website visitor’s IP address with the information I’ve obtained from my raw log files. This can easily done with PowerShell by accessing WhoIs information, which I’ll show you how to do in this article.

Although there are several websites that provide IP address information, I wasn’t about to manually copy and paste hundreds of IP addresses to get the information I needed. After a little research, I found a freely available web service that returns WhoIs information. These types of web services are intended for other websites and applications to consume, but you can just as easily use them in PowerShell. The arin.net website offers a number of free services. You have to take the time to read how to use them and figure out how to translate what you read into a PowerShell command. There’s no magic conversion, but hopefully with experience and examples like what I have for you today, this will become easier.
The service is exposed as a REST API. If you can find a service that uses the REST API, then you can use the Invoke-RestMethod cmdlet. That’s what we’re going to do here. The cmdlet needs address, which I’ll construct in PowerShell:

$baseURL = 'http://whois.arin.net/rest'
$ip = '208.67.222.222'
$url = "$baseUrl/ip/$ip"

Ready for how easy this is?

$r = Invoke-RestMethod  $url

Very often the results come back as an XML document.

Our results returned in an XML document. (Image Credit: Jeff Hicks)
Our results returned in an XML document. (Image Credit: Jeff Hicks)

This lets you walk through the document easily.
041115 1326 PowerShellP2
The name property looks like it will be useful, which we can grab like this:

$r.net.name

We can also obtain multiple properties.

Obtaining multiple properties. (Image Credit: Jeff Hicks)
Obtaining multiple properties. (Image Credit: Jeff Hicks)

I can also keep drilling down. When I see a property name and a value of the same, this is probably another nested layer.
041115 1326 PowerShellP4
This name looks like it belongs to a corporation, which is useful. Look at the #text property. That is another URL and since it has ‘rest’ in the path, I bet it is another service I can query.

I already knew about this from reading the API documentation on the site, but even without that knowledge, it doesn’t cost me anything to try.

$s = Invoke-Restmethod $r.net.orgRef.'#text'

Because the XML node has a # in the name, I need to quote it. There were no errors, so what did I get?

Using InvokeRestMethod in Windows PowerShell. (Image Credit: Jeff Hicks)
Using InvokeRestMethod in Windows PowerShell. (Image Credit: Jeff Hicks)

Here’s another XML document that I can navigate and get some useful information. Excellent. Now that I have some core commands that work, I can build a re-usable function.

Function Get-MyWhoIs {
<#
.SYNOPSIS
Get WhoIS data
.DESCRIPTION
Use this command to get public WhoIS domain information for a given IP v4 address.
.PARAMETER Ip
Enter an IPv4 Address. This command has aliases of: Address
.PARAMETER Full
Show complete whoIs information.
.EXAMPLE
PS C:\> Get-MyWhoIs 208.67.222.222
OpenDNS, LLC
.EXAMPLE
PS C:\> Get-MyWhoIs 208.67.222.222 -full
IP                     : 208.67.222.222
Name                   : OPENDNS-NET-1
RegisteredOrganization : OpenDNS, LLC
City                   : San Francisco
StartAddress           : 208.67.216.0
EndAddress             : 208.67.223.255
NetBlocks              : 208.67.216.0/21
Updated                : 3/2/2012 8:03:18 AM
.NOTES
NAME        :  Get-MyWhoIs
VERSION     :  1.0
LAST UPDATED:  4/3/2015
AUTHOR      :  Jeff Hicks (@JeffHicks)
Learn more about PowerShell:
Essential PowerShell Learning Resources
**************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .LINK Invoke-RestMethod .INPUTS [string] .OUTPUTS [string] or [pscustomobject] #> [cmdletbinding()] Param ( [parameter(Position=0,Mandatory,HelpMessage="Enter an IPv4 Address.", ValueFromPipeline,ValueFromPipelineByPropertyName)] [Alias("Address")] [ValidatePattern("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")] [string]$IP, [Parameter(Helpmessage="Show complete WhoIs information.")] [switch]$Full ) Begin { Write-Verbose "Starting $($MyInvocation.Mycommand)" $baseURL = 'http://whois.arin.net/rest' #default is XML anyway $header = @{"Accept"="application/xml"} } #begin Process { $url = "$baseUrl/ip/$ip" $r = Invoke-Restmethod $url -Headers $header Write-verbose ($r.net | out-string) if ($Full) { $propHash=[ordered]@{ IP = $ip Name = $r.net.name RegisteredOrganization = $r.net.orgRef.name City = (Invoke-RestMethod $r.net.orgRef.'#text').org.city StartAddress = $r.net.startAddress EndAddress = $r.net.endAddress NetBlocks = $r.net.netBlocks.netBlock | foreach {"$($_.startaddress)/$($_.cidrLength)"} Updated = $r.net.updateDate -as [datetime] } [pscustomobject]$propHash } else { #write just the name $r.net.orgRef.Name } } #Process End { Write-Verbose "Ending $($MyInvocation.Mycommand)" } #end } #end Get-WhoIs

The Get-MyWhoIs function takes an IPv4 address as a parameter. By default, it only returns the name.

The get-mywhois function in Windows PowerShell. (Image Credit: Jeff Hicks)
The get-mywhois function in Windows PowerShell. (Image Credit: Jeff Hicks)

I also wrote the function so that I could pipe in an array of IP addresses.
Piping in an array of IP addresses in Windows PowerShell. (Image Credit: Jeff Hicks)
Piping in an array of IP addresses in Windows PowerShell. (Image Credit: Jeff Hicks)

My function will also get detailed information and drill down to the second link.

if ($Full) {
$propHash=[ordered]@{
IP = $ip
Name = $r.net.name
RegisteredOrganization = $r.net.orgRef.name
City = (Invoke-RestMethod $r.net.orgRef.'#text').org.city #<-- DRILL DOWN TO NEXT LEVEL
StartAddress = $r.net.startAddress
EndAddress = $r.net.endAddress
NetBlocks = $r.net.netBlocks.netBlock | foreach {"$($_.startaddress)/$($_.cidrLength)"}
Updated = $r.net.updateDate -as [datetime]
}
[pscustomobject]$propHash
}
else {
#write just the name
$r.net.orgRef.Name
}

In PowerShell, this is a seamless experience and not much different than getting a service or process.
041115 1326 PowerShellP8
One last note on my function: I could have called it Get-WhoIs, but since “WhoIs” is practically an accepted standard term, I felt there might be a chance for a naming collision.

Instead, I simply added a My prefix to the noun. Some people and companies also use their initials. It doesn’t really matter. As long as some portion the noun is predictable, your command should be discoverable. I always tell people that when developing a PowerShell tool, you have to think about who will use it and what expectations they will bring to the table.
If you would like to learn more about scripting and toolmaking, consider getting a copy of Learn PowerShell Toolmaking in a Month of Lunches, 2nd. Ed.