PowerShell script to determine if an IP Range is part of the Azure Datacenter IP Range
Microsoft publishes an XML document that contains the IP address ranges (including Compute, SQL and Storage ranges) used in the Microsoft Azure Datacenters. An updated file is posted weekly which reflects the currently deployed ranges and any upcoming changes to the IP ranges.
Working with Virtual Network Service Endpoints, I found that enabling the Microsoft.SQL endpoint (Azure SQL Database) added a route to my Azure Virtual Network's route table with a set of public IP ranges, all of which presumably are attached to the Azure SQL Database infrastructure.
However, "presumably" doesn't quite cut it when it comes to configuring firewalls.
Clicking Download will fetch you a CSV file with the last row giving you the Azure SQL Database Infrastructure IP Ranges.
A nifty bit of manual text editing later, this is what I had:
Notice I've added in a couple of dummy IP addresses, 1.1.1.1/32 and 2.2.2.2/32, to exhibit how the output is presented if IP Ranges don't belong to the Azure Datacenter IP Ranges.
If you've worked with networks and IP addresses, you'll know that these tend to be some of the most misunderstood concepts in IT. I found this out for myself when many years ago, I needed to understand how Hyper-V Network Virtualization worked. The point being, determining which IP range fits into which other IP Range is something best left to PowerShell!
Why PowerShell? Because it has been a while since I wrote my last script and did not want to pass on the chance to brush up my skills.
The most important part of the script is, of course, the logic that checks whether one IP Range is a part of another.
Before you set out to write any script, a best practice is to check if one exists on the Internet and play around with it. It didn't take to long to come across what, in my opinion, is a perfectly written piece of code that I could just plug my script into.
Shout-out to Luben Kirov from GI Architects, whose code is available here: https://www.gi-architects.co.uk/2016/02/powershell-check-if-ip-or-subnet-matchesfits/
I simply dropped all his code into a PSM1 file - a PowerShell Module - and import it in my script to use the checkSubnet function.
That done, all I had to do was build the plumbing around that function, using the Azure Datacenter IP Range XML, the input file I created above and a whole lot of For Loops.
Finally, it pays to always output an object at the end of the script. Why, you may ask. Because if you don't, Ed "The Microsoft Scripting Guy" Wilson will hunt you down and make you do it. Also, because there are so many other advantages - creating reports, passing your script on a pipeline, never again using Write-Host.
The end result:
Here's the script, quick and dirty - no error checking, just a tiny bit of optimization.
As always, let me know in the comments if this helps you!
Richard Spitz
Cloud Solution Architect
Microsoft
# ******************************************************************************************************************************
# Script to check a set of IP Address Ranges and determine if they belong to the Azure DataCenter IP Address Ranges
# Author: Richard Spitz, Cloud Solution Architect, Microsoft
#
# Usage: Check-AzurePublicIP.ps1 -AzurePublicIPFilePath <Path to Azure Public IP file> -InputFilePath <Path to Input IP Range>
#
# Example: Check-AzurePublicIP.ps1 -AzurePublicIPFilePath ".\PublicIPs_20180226.xml" -InputFilePath ".\AzSQLSvcEndpoints.txt"
#
# Prerequisites: See ***DO THIS*** comments below
#
# ******************************************************************************************************************************
# ***DO THIS***
# Download the latest Azure Datacenter Public IPs list from https://www.microsoft.com/en-us/download/details.aspx?id=41653
# InputFile needs to be a text file with the Public IP ranges you want to check, with each value on a new line
param (
[string]$AzurePublicIPFilePath = ".\PublicIPs_20180226.xml",
[string]$InputFilePath = ".\AzSQLSvcEndpoints.txt"
)
# ***DO THIS***
# Save the entire code from https://www.gi-architects.co.uk/2016/02/powershell-check-if-ip-or-subnet-matchesfits/
# to CheckSubnet.PSM1 in the same directory as this script file
Import-Module .\CheckSubnet.psm1
[xml]$AzurePublicIPs = Get-Content -Path $AzurePublicIPFilePath
$inputFile = Get-Content -Path $InputFilePath
[int]$rangeFound = 0
[string]$range = ""
[string]$region = ""
$outputObjectArr = @()
# Loop through the Input file, which contains IP ranges to check
For($l = 0; $l -lt $inputFile.Length; $l++)
{
# Indicate progress
Write-Host "." -NoNewline
# Reset RangeFound Counter for every new IP Range from the InputFile
$rangeFound = 0
# Loop through each Region node within the XML object
For($i = 0; $i -lt $AzurePublicIPs.AzurePublicIpAddresses.Region.Length;$i++)
{
# Loop through each IP Range within the XML object, to test the Subnet within it
For($j = 0; $j -lt $AzurePublicIPs.AzurePublicIpAddresses.Region[$i].IpRange.Length; $j++)
{
# Store current IP Range Subnet as a string
[string]$currentPublicIP = $AzurePublicIPs.AzurePublicIpAddresses.Region[$i].IpRange[$j].Subnet
# Quick and dirty first octet check optimization that cuts down script execution time
If(($currentPublicIP.Split("."))[0] -like ($inputFile[$l].Split("."))[0])
{
# Test the "Condition" property of the returned object from the checkSubnet function (which is
# defined within the CheckSubnet.psm1 module we imported). If true, increment a counter to
# indicate that our IP Range was found
If((checkSubnet $currentPublicIP $inputFile[$l]).Condition)
{
$rangeFound++
# Store values to report later
$range = $currentPublicIP
$region = $AzurePublicIPs.AzurePublicIpAddresses.Region[$i].Name
}
}
}
}
# Create a new object with details and add it to list of output objects, using the "Exists" NoteProperty to
# indicate whether the Input IP Range exists within the Azure IP Range or not
If($rangeFound)
{
$tempObj = New-Object -TypeName PSObject
$tempObj | Add-Member -MemberType NoteProperty -Name InputIPRange -Value $inputFile[$l]
$tempObj | Add-Member -MemberType NoteProperty -Name AzureIPRange -Value $range
$tempObj | Add-Member -MemberType NoteProperty -Name Region -Value $region
$tempObj | Add-Member -MemberType NoteProperty -Name Exists -Value "Yes"
$outputObjectArr += $tempObj
Remove-Variable tempObj
}
else
{
$tempObj = New-Object -TypeName PSObject
$tempObj | Add-Member -MemberType NoteProperty -Name InputIPRange -Value $inputFile[$l]
$tempObj | Add-Member -MemberType NoteProperty -Name AzureIPRange -Value "N.A."
$tempObj | Add-Member -MemberType NoteProperty -Name Region -Value "N.A."
$tempObj | Add-Member -MemberType NoteProperty -Name Exists -Value "No"
$outputObjectArr += $tempObj
Remove-Variable tempObj
}
}
# Dispay Output
Write-Host " "
$outputObjectArr