This week I took the Office 365 Performance Management course on the Microsoft Virtual Academy. If you have any plans using Office 365 I strongly recommend taking this course. One of the topics that was often highlighted is the importance of having all Office 365 URLs and IP Ranges configured on the outbound allow list. The Office 365 URLs and IP Ranges are documented here and the changes to the list are described here.
As part of my continuous PowerShell skill improvement activity, I thought I write a script to retrieve this information via PowerShell. Microsoft has stored the URL and IP address information into tables on this page, but thanks to Lee Holmes Get-WebRequestTable script I could loop through the various tables that contain the URL and IP address range information stored on the page.
The Get-Office365URLIPInfo script can be downloaded from the Microsoft TechNet Gallery here.
function Get-Office365URLIPInfo { <# .Synopsis This script lists all Office 365 URLs and IP Ranges .DESCRIPTION This script retrieves all the URLs and IP addresses that are posted on the Office 365 URLs and IP address ranges site .EXAMPLE Get-Office365URLIPInfo .NOTES Version 1.0, 05/02/2015, Alex Verboon Thanks to Lee Holmes for the Get-WebRequestTable function http://www.leeholmes.com/blog/2015/01/05/extracting-tables-from-powershells-invoke-webrequest/ #> Begin{ # Link to Office 365 URLs and IP address ranges page $url = "https://technet.microsoft.com/library/hh373144.aspx" $info = Invoke-WebRequest -Uri $url Function Get-Webrequesttable { # Lee holmes #http://www.leeholmes.com/blog/2015/01/05/extracting-tables-from-powershells-invoke-webrequest/ param( [Parameter(Mandatory = $true)] [Microsoft.PowerShell.Commands.HtmlWebResponseObject] $WebRequest, [Parameter(Mandatory = $true)] [int] $TableNumber ) ## Extract the tables out of the web request $tables = @($WebRequest.ParsedHtml.getElementsByTagName("TABLE")) $table = $tables[$TableNumber] $titles = @() $rows = @($table.Rows) ## Go through all of the rows in the table foreach($row in $rows) { $cells = @($row.Cells) ## If we’ve found a table header, remember its titles if($cells[0].tagName -eq "TH") { $titles = @($cells | % { ("" + $_.InnerText).Trim() }) continue } ## If we haven’t found any table headers, make up names "P1", "P2", etc. if(-not $titles) { $titles = @(1..($cells.Count + 2) | % { "P$_" }) } ## Now go through the cells in the the row. For each, try to find the ## title that represents that column and create a hashtable mapping those ## titles to content $resultObject = [Ordered] @{} for($counter = 0; $counter -lt $cells.Count; $counter++) { $title = $titles[$counter] if(-not $title) { continue } $resultObject[$title] = ("" + $cells[$counter].InnerText).Trim() } ## And finally cast that hashtable to a PSCustomObject [PSCustomObject] $resultObject } } } Process{ # First let's identify how many tables there are, the scrtipt assumes that all tables have a title $mtnr = 0 While ($true) { Write-debug $mtnr $tr = Get-Webrequesttable -WebRequest $info -TableNumber $mtnr If ($tr.P1 | gm -ErrorAction SilentlyContinue ) { $mtnr = $mtnr -1 Break } $mtnr++ } # maximum number of tables found in page $maxtnr = $mtnr Write-debug "Total number of tables in page: $maxtnr" $URLIPInfo=@() # we start with table 1 and not 0 because table 0 is just an information notice on this page. $table_nr = 1 while ($table_nr -le $maxtnr) { write-debug "getting table number $table_nr" $url = Get-Webrequesttable -WebRequest $info $table_nr $tables = ($url | gm | Select-Object | Where-Object {$_.membertype -eq "NoteProperty"}).Name if ($tables -isnot [array] -eq $true) { $nr_sub_tables = 1 write-debug "number of sub tables $nr_sub_tables" $stable = 0 While ($stable -lt $nr_sub_tables) { $urls = ($url.$tables).Replace("Copy","").replace("^","").split("`r`n") $Table_Title = $tables write-debug "Table $Table_Title " write-debug "Sub Table nr $stable" $stable++ ForEach ($i in $urls) { If ($i.Length -gt 0) { $object = New-Object -TypeName PSObject $object | Add-Member -MemberType NoteProperty -Name "IP_URL" -Value $i.TrimStart() $object | Add-Member -MemberType NoteProperty -Name "Service" -Value $Table_Title $URLIPInfo += $object } } } } Else { $nr_sub_tables = $tables.Count write-debug "number of sub tables array $nr_sub_tables" $stable = 0 While ($stable -lt $nr_sub_tables) { $urls = ($url.($tables[$($stable)])).Replace("Copy","").replace("^","").split("`r`n") $Table_Title = $tables[$($stable)] write-debug "Table $Table_Title " write-debug "Sub Table nr $stable" $stable++ ForEach ($i in $urls) { If ($i.Length -gt 0) { $object = New-Object -TypeName PSObject $object | Add-Member -MemberType NoteProperty -Name "IP_URL" -Value $i.TrimStart() $object | Add-Member -MemberType NoteProperty -Name "Service" -Value $Table_Title $URLIPInfo += $object } } } } $table_nr++ } } # end process End{ $URLIPInfo } } # end function
When you run the script, you get a list of all URLs or IP Ranges and the related Service.
Good morning, You might want to up date your URL, it doesn’t go to the current list. Arguably I’m on linux and I’m not going to test a powershell script, but it seem likely to fail if its not pulling the right URL.
Also here is a straight XML list you can use.
https://support.content.office.net/en-us/static/O365IPAddresses.xml
I use this one in a script that makes an allowed relay list for send mail based servers.
The URL to download the script off technet is not working for me.
Hi, it looks like Microsoft changed the whole page with the IP addresses, so the script doesn’t work anymore.
Hi, Microsoft changed the entire layout of the page with IP addresses, hence the script no longer works as expected.
I’ve created and published in TechNet Gallery the function ConvertFrom-O365IPAddressesXMLFile what convert xml file to custom PowerShell object. The function you can download directly from TechNet Gallery bit.ly/1RZXPsn or from my GitHub repository http://bit.ly/1Otkk2m