Yesterday I posted a popular article about using Invoke-WebRequest to get weather conditions. That function used the Yahoo web site but really only worked for US cities. So I also cleaned up and revised another set of advanced PowerShell functions (required PowerShell 3) that can retrieve weather information for probably any location on Earth. The first piece of information you need is your WOEID, or "Where On Earth ID". Here is a function to do just that.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Function Get-Woeid {
<#
.Synopsis
Retrieve WOEID information
.Description
Search Yahoo's Geo Places for a WOEID (Where On Earth ID). You can search based on a postal code, or place name. Optionally, you can save the XML results to the pipeline.
.Parameter Search
A search parameter such as a postal or zip code. You can also use a place name.
.Parameter AsXML
Write the XML document to the pipeline.
.Example
PS C:\> get-woeid 89101
Country : US
Region : Nevada
Postal : 89101
Locale : Las Vegas
WOEID : 12795439
.Example
PS C:\> get-woeid -search "H1A0A1"
Region : Quebec
Postal : H1A 0A1
WOEID : 55963829
Locale : Montreal
Country : CA
.Example
PS C:\> get-woeid "Helsinki, Finland"
Country : FI
Region : Uudenmaan maakunta
Postal :
Locale : Helsinki
WOEID : 565346
.Example
PS C:\> get-woeid "Helsinki, Finland" | get-weather -unit C
Date : Mon, 16 Feb 2015 4:49 pm EET
Location : Helsinki, Finland
Temperature : -2 C
Condition : Fair
ForecastCondition : Clear
ForecastLow : -6 C
ForecastHigh : -2 C
.Example
PS C:\> [xml]$doc = get-woeid "omaha, ne" -AsXML
PS C:\> $doc.Save("c:\work\omahaid.xml")
The first command gets the raw XML document and the second command writes it to disk. Or you can explore the document interactively.
PS C:\> $doc.query.results.place.timezone
type woeid #text
---- ----- -----
Time Zone 56043661 America/Chicago
.Inputs
strings
.Outputs
custom object
.Link
Invoke-RestMethod
.Notes
NAME: Get-Woeid
VERSION: 3.0
AUTHOR: Jeffery Hicks
LASTEDIT: February 16, 2015
Learn more about PowerShell:
Essential PowerShell Learning Resources
#>
Param(
[parameter(Position=0,Mandatory,ValueFromPipeline,
HelpMessage="Enter a place name or postal (zip) code.")]
[string[]]$Search,
[switch]$AsXML
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
}
Process {
foreach ($item in $search) {
Write-Verbose "Querying for $search"
$uri = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D'$ITEM'%20limit%201"
Write-Verbose $uri
[xml]$xml= Invoke-RestMethod -Uri $uri
if ($AsXML) {
Write-Verbose "Writing XML document"
$xml
Write-Verbose "Ending function"
#bail out since we're done
Return
}
if ($xml.query.results.place.woeid) {
Write-Verbose "Parsing XML into an ordered hashtable"
$hash = [ordered]@{
WOEID = $xml.query.results.place.woeid
Locale = $xml.query.results.place.locality1.'#text'
Region = $xml.query.results.place.admin1.'#text'
Postal = $xml.query.results.place.postal.'#text'
Country = $xml.query.results.place.country.code
}
Write-Verbose "Writing custom object"
New-Object -TypeName PSObject -Property $hash
} #if $xml
else {
Write-Warning "Failed to find anything for $item"
}
} #foreach
} #process
End {
Write-Verbose "Ending $($myinvocation.mycommand)"
}
} #end function
To use the function you specify some sort of search criteria such as a postal code or city name.

I saved my WOEID as a variable in my PowerShell profile. This function also uses Invoke-RestMethod. I included a parameter to write an XML document to the pipeline instead in case you want to modify the function and need some help discovering the data. The second function, Get-Weather, uses the WOIED to get the current weather conditions. It too uses Invoke-RestMethod to retrieve the data. The resulting XML document is then parsed out using Select-XML to build an output object.
Function Get-Weather {
<#
.Synopsis
Retrieve weather information from Yahoo.com.
.Description
Using the Yahoo weather APIs, this function will retrieve weather information for the specified WOEID or "Where on Earth ID". If you don't know your WOED, open a web browser and go to http://weather.yahoo.com/. Enter in your zip or postal code. Your WOEID will be the number at the end of the url.
Or you can use the Get-Woeid function.
A custom object is written to the pipeline and you can control how much information is displayed by specifying Basic, Extended or All for the Detail parameter.
Basic properties:
Date,Location,Temperature,Condition,ForecastCondition,ForecastLow, and ForecastHigh. This is the default.
Extended properties:
Basic properties plus WindChill,WindSpeed,Humidity,Barometer,Visibility,and Tomorrow.
All properties:
Extended properties plus Sunrise,Sunset,City,Region,Zip,Latitude,Longitude,URL
.Parameter Woeid
Your WOEID, or "Where on Earth ID".
.Parameter Unit
Show temperature in Farenheit or Celsius
.Parameter Online
Open the web page in your default browser.
.Parameter AsXML
Write an XML document to the pipeline.
.Example
PS C:> get-weather 12795711
Date : Mon, 16 Feb 2015 3:51 am PST
Location : Beverly Hills, CA
Temperature : 56 F
Condition : Fair
ForecastCondition : Sunny
ForecastLow : 55 F
ForecastHigh : 72 F
.Example
PS C:\> get-weather 12795446 extended
Date : Mon, 16 Feb 2015 6:56 am PST
Location : Las Vegas, NV
Temperature : 52 F
Condition : Mostly Cloudy
ForecastCondition : Sunny
ForecastLow : 49 F
ForecastHigh : 77 F
WindChill : 52 F
WindSpeed : 9 mph
Humidity : 35%
Barometer : 29.95 in and steady
Visibility : 10 mi
Tomorrow : Tue 17 Feb 2015 Sunny Low 49F: High: 70F
.Example
PS C:\> 12795711,12795446,12762736 | Get-Weather -detail all | Format-Table City,Temperature,Condition,Sunrise,Sunset
City Temperature Condition Sunrise Sunset
---- ----------- --------- ------- ------
Beverly Hills 56 F Fair 6:37 am 5:38 pm
Las Vegas 52 F Mostly Cloudy 6:26 am 5:21 pm
Syracuse -3 F Partly Cloudy 7:01 am 5:34 pm
.Example
PS C:\> get-woeid "Bangor,ME" | get-weather
Date : Mon, 16 Feb 2015 9:51 am EST
Location : Bangor, ME
Temperature : 5 F
Condition : Partly Cloudy
ForecastCondition : Partly Cloudy/Wind
ForecastLow : -6 F
ForecastHigh : 12 F
.Example
PS C:\> get-woeid "Juneau, AK","Miami,FL" | get-weather | Select Date,Location,Temperature
Date Location Temperature
---- -------- -----------
Mon, 16 Feb 2015 8:54 am AKST Juneau, AK 35 F
Mon, 16 Feb 2015 12:53 pm EST Miami, FL 75 F
.Inputs
Strings
.Outputs
Custom object
.Link
Invoke-RestMethod
.Link
Get-Woeid
Select-XML
.Notes
NAME: Get-Weather
VERSION: 3.0
AUTHOR: Jeffery Hicks
LASTEDIT: February 16, 2015
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. *
****************************************************************
#>
[cmdletbinding(DefaultParameterSetName="detail")]
Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter a WOEID value",
ValueFromPipeline,
ValueFromPipelineByPropertyName
)]
[ValidateNotNullorEmpty()]
[string[]]$woeid,
[Parameter(Position=1,ParameterSetName="detail")]
[ValidateSet("Basic","Extended","All")]
[String]$Detail="Basic",
[ValidateSet("f","c")]
[String]$Unit="f",
[Parameter(ParameterSetName="online")]
[Switch]$Online,
[Parameter(ParameterSetName="xml")]
[Switch]$AsXML
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
#define property sets
$BasicProp=@("Date","Location","Temperature","Condition","ForecastCondition",
"ForecastLow","ForecastHigh")
$ExtendedProp=$BasicProp
$ExtendedProp+=@("WindChill","WindSpeed","Humidity","Barometer",
"Visibility","Tomorrow")
$AllProp=$ExtendedProp
$AllProp+=@("Sunrise","Sunset","City","Region","Latitude",
"Longitude","ID","URL")
#unit must be lower case
$Unit=$Unit.ToLower()
Write-Verbose "Using unit $($unit.toLower())"
#define base url string
[string]$uribase = "http://weather.yahooapis.com/forecastrss"
Write-Verbose "Base = $uribase"
} #begin
Process {
Write-Verbose "Processing"
foreach ($id in $woeid) {
Write-Verbose "Getting weather info for woeid: $id"
#define a uri for the given WOEID
[string]$uri = "{0}?w={1}&u={2}" -f $uribase,$id,$unit
if ($online) {
Write-Verbose "Opening $uri in web browser"
Start-Process -FilePath $uri
#bail out since there's nothing else to do.
Return
}
Write-Verbose "Downloading $uri"
[xml]$xml= Invoke-WebRequest -uri $uri
if ($AsXML) {
Write-Verbose "Writing XML document"
$xml
Write-Verbose "Ending function"
#bail out since we're done
Return
}
if ($xml.rss.channel.item.Title -eq "City not found") {
Write-Warning "Could not find a location for $id"}
else {
Write-Verbose "Processing xml"
#initialize a new hash table
$properties=@{}
<#
get the yweather nodes
yweather information comes from a different namespace so we'll
use Select-XML to extract the data. Parsing out all data regardless
of requested detail since it doesn't take much effort. Later,
only the requested detail will be written to the pipeline.
#>
#define the namespace hash
$namespace=@{yweather=$xml.rss.yweather}
$units=(Select-Xml -xml $xml -XPath "//yweather:units" -Namespace $namespace ).node
$properties.Add("Condition",$xml.rss.channel.item.condition.text)
$properties.Add("Temperature","$($xml.rss.channel.item.condition.temp) $($units.temperature)")
#convert Date to a [datetime] object
$dt = $xml.rss.channel.item.condition.date.Substring(0,$xml.rss.channel.item.condition.Date.LastIndexOf("m")+1) -as [datetime]
$properties.Add("Date",$dt)
#get forecast
$properties.add("ForecastDate",$xml.rss.channel.item.forecast[0].date)
$properties.add("ForecastCondition",$xml.rss.channel.item.forecast[0].text )
$properties.Add("ForecastLow","$($xml.rss.channel.item.forecast[0].low) $($units.temperature)" )
$properties.Add("ForecastHigh","$($xml.rss.channel.item.forecast[0].high) $($units.temperature)" )
#build tomorrow's foreacst
$t=$xml.rss.channel.item.forecast[1]
$tomorrow="{0} {1} {2} Low {3}{4}: High: {5}{6}" -f $t.day,$t.Date,$t.Text,$t.low, $($units.temperature),$t.high, $($units.temperature)
$properties.add("Tomorrow",$tomorrow)
#get optional information
$properties.Add("Latitude",$xml.rss.channel.item.lat)
$properties.Add("Longitude",$xml.rss.channel.item.long)
$city=$xml.rss.channel.location.city
$properties.Add("City",$city)
$region=$xml.rss.channel.location.region
$country=$xml.rss.channel.location.country
if (-not ($region)) {
#if no region found then use country
Write-Verbose "No region found. Using Country"
$region=$country
}
$properties.Add("Region",$region)
$location="{0}, {1}" -f $city,$region
$properties.Add("Location",$location)
$properties.Add("ID",$id)
#get additional yweather information
$wind=(Select-Xml -xml $xml -XPath "//yweather:wind" -Namespace $namespace ).node
$astronomy=(Select-Xml -xml $xml -XPath "//yweather:astronomy" -Namespace $namespace ).node
$atmosphere=(Select-Xml -xml $xml -XPath "//yweather:atmosphere" -Namespace $namespace ).node
$properties.Add("WindChill","$($wind.chill) $($units.temperature)")
$properties.Add("WindSpeed","$($wind.speed) $($units.speed)")
$properties.Add("Humidity","$($atmosphere.humidity)%")
$properties.Add("Visibility","$($atmosphere.visibility) $($units.distance)")
#decode rising
switch ($atmosphere.rising) {
0 {$state="steady"}
1 {$state="rising"}
2 {$state="falling"}
}
$properties.Add("Barometer","$($atmosphere.pressure) $($units.pressure) and $state")
$properties.Add("Sunrise",$astronomy.sunrise)
$properties.Add("Sunset",$astronomy.sunset)
$properties.Add("url",$uri)
#create new object to hold values
$obj= New-Object -TypeName PSObject -Property $properties
#write object and properties. Default is Basic
Switch ($detail) {
"All" {
Write-Verbose "Using All properties"
$obj | Select-Object -Property $AllProp
} #all
"Extended" {
Write-Verbose "Using Extended properties"
$obj | Select-Object -Property $ExtendedProp
} #extended
Default {
Write-Verbose "Using Basic properties"
$obj | Select-Object -Property $BasicProp
} #default
} #Switch
} #processing XML
} #foreach $id
} #process
End {
Write-Verbose "Ending $($myinvocation.mycommand)"
} #end
} #end function

You can also change the temperature units.
I wrote these as two separate functions, I suppose I could have nested Get-Woeid inside Get-Weather, although the better option is probably to build a module. I'll leave that for you. The functions are designed to take advantage of pipeline binding so that you can pipe Get-Woeid to Get-Weather.

You can even get weather for multiple locations.

This weather source includes a lot of information so I created a parameter to control how much detail to display. What you see above is basic information. But there is 'extended'.

Or you can see everything with a detail setting of 'all'.

Notice that url at the end? As the cherry on top, you can open the weather forecast in a browser using the Online parameter.
get-woeid 13244 | get-weather -Online

There you have it. No matter where you are you can check the weather. Or look out a window.
Enjoy.

What a work of art!!! It has a bit of everything… ValidationSet,ParameterSetName. I love the way you append the properties from Basis to Extended to All. Formating is there also. Last but not least XML and XPath!!! PowerShell at its finest!!! Thanks again!!!
Thanks for the kind words.
Seems a bit complex when you have to know the zip or postal code.
function weather{
$zip = $args
$url = “http://www.weather.com/weather/today/l/” + $zip
start $url
}
Sure, but the point was to display the results in PowerShell.