So the PowerShell.org Scripting Games Puzzle for August is about to expire, so rather later than never I decided to give it a go and meet the challenges set by the puzzle, in short challenge was as follows:
At http://www.telize.com/geoip, you’ll find a JavaScript Object Notation endpoint. It’s public.
Being able to query information from the Web – often in XML or JavaScript Object Notation – is an important integration skill. PowerShell can actually make it pretty easy. Although this challenge can be solved using a one-liner, you could also go further and write a complete “Get-GeoInformation” function around it. However, keep in mind that a function would not normally (a) limit the data that’s output or (b) pre-format the data. Why not?
Challenges:
- Try to do this in a one-liner, but spell out all command and parameter names.
- Write an advanced function that provides a complete Get-GeoInformation “wrapper” around this endpoint.
- Along with your entry, include the endpoint for another XML or JavaScript Object Notation web service that you think is cool, along with a brief notation of what it does
As a one-liner, this can be achieved by invoking the Invoke-WebRequest cmdlet to retrieve content from the GeoIP rest API (http://www.telize.com/geoip) and then convert the JSON output using ConvertFrom-Json cmdlet.
Invoke-WebRequest -Uri "http://www.telize.com/geoip" | ConvertFrom-Json
So that is part one of the challenge over, now the challenge is to create an advanced function. For my function I want to have the ability to specify a single or multiple IP addresses to retrieve IP geolcation data and also to specify the protocol used for the web reqeuest, by default this will use https.
So, the first step to creating an advanced function is to add the CmdletBinding attribute, this will allow for additional parameter checking such as validation of the IP address using the IPAddress Class method and a set of valid protocols for the web request.
[CmdletBinding()] Param ( # Specify IP addresses to return IP geolocation data. [Parameter(Position=0)] [String[]] [ValidateScript({$_ -match [IPAddress]$_ })] $IPAddress = "", # Specify protocol for web request. [Parameter(Position=1)] [ValidateSet('https','http')] [String] $Protocol = "https" )
In order to perform the web request we will need to invoke the ‘Invoke-WebRequest’ cmdlet to connect to a Uniform Resource Identifier (Uri) for the web request, in this scenario http://www.telize.com/geoip. So based on the protocol specified as an attribute we will generate the Uri value as follows:
$Uri = ($Protocol + "://www.telize.com/geoip")
The function provides the ability to specify a single or multiple IP addresses or if the parameter is not provided will retrieve IP geolocation from the local host. The content returned from the cmdlet is a JSON object and therefore can be converted to a custom object using the ConvertFrom-Json cmdlet. Finally, we will select the objects from the properties returned and create a calculated property.
Try { # Conditional logic to determine if a IP address has been specified. If ($IPAddress) { # Performs an action on each item in the collection. ForEach ($IP in $IPAddress) { Write-Verbose ("Retrieving IP geolocation data for " + $IPAddress + ".") # Retrieves content from the URI specified and converts JSON-formatted string. $WebRequest = Invoke-WebRequest -Uri ($Uri + "/" + $IP) | ConvertFrom-Json } } Else { # Retrieves content from the URI specified and converts JSON-formatted string. Write-Verbose "No IP address sepcified, retrieving IP geolocation data from host" $WebRequest = Invoke-WebRequest -Uri $Uri | ConvertFrom-Json } # Selects objects from the speficied property and creates calculated properties. $WebRequest | Select-Object -Property @{N="Longitude";E={$_.longitude}},@{N="Latitude";E={$_.latitude}},@{N="ASN";E={$_.asn}},@{N="Offset";E={$_.Offset}},@{N="IP";E={$_.ip}}, @{N="Area Code";E={$_.area_code}},@{N="Continent Code";E={$_.continent_code}},@{N="DMA Code";E={$_.dma_code}},@{N="City";E={$_.City}},@{N="Timezone";E={$_.Timezone}},@{N="ISP";E={$_.isp}}, @{N="Country";E={$_.country}},@{N="Country Code";E={$_.country_code}} } Catch { Write-Host ("ERROR: Failed to retrieve IP geolocation data with the error: " + $Error.Exception.Message[0] + ".") -ForegroundColor Red }
The function can be downloaded from here.