Is this ip address part of the Office 365 IP address range?
The analysis of firewall logs in Office 365 projects repeatedly raises the question: Is this blocked IP address part of the Office 365 address range?
Thanks to PowerShell and the information published by Microsoft, the answer is only a few lines of code away.
My script “Test-IsO365IpAddress.ps1” simply needs the IP address in question and optionally the TCP/UDP port. It retrieves the current list of address ranges from Microsoft and checks if the IP address is part of one of the IP networks. If this is the case, the script outputs all services that use this network.
If a port was also specified as part of the command the output will be filtered.
In addition to the service name (e.g. “Exchange Online”), the category (Optimize, Allow or Default), the required ports, the subnet and the somewhat obsolete “Required” category are also displayed.
Equipped with this data, the visit to the firewall team should be successful.
Many thanks also to Luben Kirov who allowed my to use his network functions to analysis the IP networks fast and reliable and Siva Padisetty whose function checkSubnet I used as inspiration for my own implementation.
Test-IsO365IpAddress.ps1
[CmdletBinding()]
param (
# IP Address to check against Office 365 Range
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
Position = 0)]
$IPAddress,
# Port to check
[Parameter(Mandatory = $false,
Position = 1)]
$Port
)
Begin {
#region Functions
# http://www.gi-architects.co.uk/2016/02/powershell-check-if-ip-or-subnet-matchesfits/
# Please note i took inspiration form www.padisetty.com
# The function will check ip to ip, ip to subnet, subnet to ip or subnet to subnet belong to each other and return true or false and the direction of the check
#////////////////////////////////////////////////////////////////////////
function checkSubnet ([string]$addr1, [string]$addr2) {
# Separate the network address and lenght
$network1, [int]$subnetlen1 = $addr1.Split('/')
$network2, [int]$subnetlen2 = $addr2.Split('/')
#Convert network address to binary
[uint32] $unetwork1 = NetworkToBinary $network1
[uint32] $unetwork2 = NetworkToBinary $network2
#Check if subnet length exists and is less then 32(/32 is host, single ip so no calculation needed) if so convert to binary
if ($subnetlen1 -lt 32) {
[uint32] $mask1 = SubToBinary $subnetlen1
}
if ($subnetlen2 -lt 32) {
[uint32] $mask2 = SubToBinary $subnetlen2
}
#Compare the results
if ($mask1 -and $mask2) {
# If both inputs are subnets check which is smaller and check if it belongs in the larger one
if ($mask1 -lt $mask2) {
return CheckSubnetToNetwork $unetwork1 $mask1 $unetwork2
} else {
return CheckNetworkToSubnet $unetwork2 $mask2 $unetwork1
}
} ElseIf ($mask1) {
# If second input is address and first input is subnet check if it belongs
return CheckSubnetToNetwork $unetwork1 $mask1 $unetwork2
} ElseIf ($mask2) {
# If first input is address and second input is subnet check if it belongs
return CheckNetworkToSubnet $unetwork2 $mask2 $unetwork1
} Else {
# If both inputs are ip check if they match
CheckNetworkToNetwork $unetwork1 $unetwork2
}
}
function CheckNetworkToSubnet ([uint32]$un2, [uint32]$ma2, [uint32]$un1) {
$ReturnArray = "" | Select-Object -Property Condition, Direction
if ($un2 -eq ($ma2 -band $un1)) {
$ReturnArray.Condition = $True
$ReturnArray.Direction = "Addr1ToAddr2"
return $ReturnArray
} else {
$ReturnArray.Condition = $False
$ReturnArray.Direction = "Addr1ToAddr2"
return $ReturnArray
}
}
function CheckSubnetToNetwork ([uint32]$un1, [uint32]$ma1, [uint32]$un2) {
$ReturnArray = "" | Select-Object -Property Condition, Direction
if ($un1 -eq ($ma1 -band $un2)) {
$ReturnArray.Condition = $True
$ReturnArray.Direction = "Addr2ToAddr1"
return $ReturnArray
} else {
$ReturnArray.Condition = $False
$ReturnArray.Direction = "Addr2ToAddr1"
return $ReturnArray
}
}
function CheckNetworkToNetwork ([uint32]$un1, [uint32]$un2) {
$ReturnArray = "" | Select-Object -Property Condition, Direction
if ($un1 -eq $un2) {
$ReturnArray.Condition = $True
$ReturnArray.Direction = "Addr1ToAddr2"
return $ReturnArray
} else {
$ReturnArray.Condition = $False
$ReturnArray.Direction = "Addr1ToAddr2"
return $ReturnArray
}
}
function SubToBinary ([int]$sub) {
return ((-bnot [uint32]0) -shl (32 - $sub))
}
function NetworkToBinary ($network) {
$a = [uint32[]]$network.split('.')
return ($a[0] -shl 24) + ($a[1] -shl 16) + ($a[2] -shl 8) + $a[3]
}
#////////////////////////////////////////////////////////////////////////
#endregion
# Generate GUID to query
$GUID = [guid]::NewGuid()
# Retreive
$Office365IPRanges = Invoke-RestMethod -Method Get -UseBasicParsing -Uri "https://endpoints.office.com/endpoints/worldwide?clientrequestid=$GUID&NoIPv6"
$IPAddressInSubnet = $false
$AllIPAddresses = $Office365IPRanges.ips | Select-Object -Unique
}
Process {
foreach ($Subnet in $AllIPAddresses) {
if ( (checkSubnet $IPAddress $Subnet).Condition ) {
Write-Verbose "$IPAddress is part of $Subnet"
$IPAddressInSubnet = $true
break
}
}
if ($IPAddressInSubnet) {
$MatchingServices = $Office365IPRanges | Where-Object { $Subnet -in $_.ips } | Add-Member -NotePropertyName "Subnet" -NotePropertyValue $Subnet -PassThru | Select-Object serviceAreaDisplayName, category, subnet, tcpPorts, required, @{e={"$IPAddress"};n="IPAddress"}
}
if ( $PSBoundParameters.ContainsKey('Port')) {
$MatchingServices = $MatchingServices | Where-Object { $_.tcpPorts -match $port -or $_.udpPorts -match $port }
}
# Output result
if ($MatchingServices) {
$MatchingServices
} else {
Write-Host -ForegroundColor Yellow "Did not find $IPAddress"
}
}