Skip to content

Instantly share code, notes, and snippets.

@jikuja
Last active March 17, 2026 10:00
Show Gist options
  • Select an option

  • Save jikuja/a8dc455f7a3e1158fc1bb1ccb63ef6f1 to your computer and use it in GitHub Desktop.

Select an option

Save jikuja/a8dc455f7a3e1158fc1bb1ccb63ef6f1 to your computer and use it in GitHub Desktop.
Azure ServiceTags_Public

Virtual network service tags

Helpers for Microsoft provided Virtual network service tags.

Revision 2 functions and tests are from ChatGPT.

TODO

  • Add function to download file from REST API
  • Add transparent caching to persist and re-use downloaded data
    • Done with default parameter values. Update must be done manually.

How to run the tool

Load functions

. ./ServiceTags-Helpers.ps1 

Download data

Download-ServiTagsData

Run the tool

Find-AzureServiceByIP -ipAddress "4.149.115.15"

How to run tests

Install Pester

Install-Module -Name Pester -Force -SkipPublisherCheck

Run tests:

Invoke-Pester -Path "Test-IpInRange.Tests.ps1"
$config = New-PesterConfiguration
$config.Run.Path = "./Test-IpInRange.Tests.ps1"
$config.CodeCoverage.Enabled = $true
$config.Output.Verbosity = "Detailed"
# Optional to scope the coverage to the list of files or directories in this path
$config.CodeCoverage.Path = "./ServiceTags-Helpers.ps1"
Invoke-Pester -Configuration $config
# Function to find Azure services by IP address
function Find-AzureServiceByIP {
param (
[string]$ipAddress,
[string]$jsonFilePath = ".data.json"
)
# Load the JSON file into a PowerShell object
$serviceTags = Get-Content $jsonFilePath | ConvertFrom-Json
# Loop through each service tag in the file
foreach ($serviceTag in $serviceTags.values) {
foreach ($addressPrefix in $serviceTag.properties.addressPrefixes) {
# TODO: extract and add IPv6 support
# Check if the addressPrefix is an IP range in CIDR format
if ($addressPrefix -match "\d+\.\d+\.\d+\.\d+\/\d+") {
# Use the 'IPAddress' class to check if the given IP is within the CIDR block
# There should be max one match per serviceTag
if (Test-IpInRange -IpAddress $ipAddress -CIDR $addressPrefix) {
# If IP is found, output the service tag details
Write-Output ([pscustomobject]@{
Service = $serviceTag.name
Region = $serviceTag.properties.region
IPRange = $addressPrefix
changeNumer = $serviceTag.properties.changeNumber
regionId = $serviceTag.properties.regionId
platform = $serviceTag.properties.platform
systemService = $serviceTag.properties.systemService
networkFeatures = $serviceTag.properties.networkFeatures
})
}
}
}
}
}
<#
.EXAMPLE
Find-AzureServiceById -UseRegex -id "PowerBI.(North|West)Europe"
#>
function Find-AzureServiceById {
param (
[string]$id,
[string]$jsonFilePath = ".data.json",
[switch] $UseRegex
)
# Load the JSON file into a PowerShell object
$serviceTags = Get-Content $jsonFilePath | ConvertFrom-Json
# Loop through each service tag in the file
foreach ($serviceTag in $serviceTags.values) {
$match = if ($UseRegex) {
$serviceTag.Id -match $Id
}
else {
$serviceTag.Id -eq $Id
}
if ($match) {
# If IP is found, output the service tag details
Write-Output ([pscustomobject]@{
Service = $serviceTag.name
Region = $serviceTag.properties.region
addressPrefixesCount = $serviceTag.properties.addressPrefixes.Count
addressPrefixes = $serviceTag.properties.addressPrefixes
regionId = $serviceTag.properties.regionId
platform = $serviceTag.properties.platform
systemService = $serviceTag.properties.systemService
networkFeatures = $serviceTag.properties.networkFeatures
})
}
}
}
# Helper function to check if an IP address is within a CIDR block
function Test-IpInRange {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string] $IpAddress,
[Parameter(Mandatory)]
[string] $CIDR
)
if ($CIDR -notmatch '^(.+)/(\d{1,3})$') {
throw "Invalid CIDR format: $CIDR"
}
$baseIP = $matches[1]
$subnetBits = [int]$matches[2]
$ipObj = [System.Net.IPAddress]::Parse($IpAddress)
$baseObj = [System.Net.IPAddress]::Parse($baseIP)
if ($ipObj.AddressFamily -ne $baseObj.AddressFamily) {
return $false
}
$ipBytes = $ipObj.GetAddressBytes()
$baseBytes = $baseObj.GetAddressBytes()
$maxBits = if ($ipObj.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork) { 32 } else { 128 }
if ($subnetBits -lt 0 -or $subnetBits -gt $maxBits) {
throw "Invalid prefix length: $CIDR"
}
$fullBytes = [math]::Floor($subnetBits / 8)
$remainingBits = $subnetBits % 8
for ($i = 0; $i -lt $fullBytes; $i++) {
if ($ipBytes[$i] -ne $baseBytes[$i]) {
return $false
}
}
if ($remainingBits -gt 0) {
# Correct mask creation
$mask = [byte](0xFF -shr (8 - $remainingBits) -shl (8 - $remainingBits))
if (($ipBytes[$fullBytes] -band $mask) -ne ($baseBytes[$fullBytes] -band $mask)) {
return $false
}
}
return $true
}
function Download-ServiceTagsData {
param(
[string]$jsonFilePath = ".data.json"
)
$url = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=56519'
$pageContent = Invoke-WebRequest -Uri $url
if ($pageContent.Content -match 'href="(?<url>[a-zA-Z0-9/:\.\-_]*\.json?)"') {
$jsonUrl = $matches['url']
}
Write-Host "Downloading file from $jsonUrl"
Invoke-RestMethod -Uri $jsonUrl -OutFile $jsonFilePath
}
<#
.EXAMPLE
(Find-AzureServiceById -id PowerBI.WestEurope).addressPrefixes | ConvertTo-CidrToAzureStorageFirewallRule | ConvertTo-Json
.EXAMPLE
(Find-AzureServiceById -id PowerBI.WestEurope).addressPrefixes | Where-Object { -not $_.contains(":") } | ConvertTo-CidrToAzureStorageFirewallRule | ConvertTo-Json
#>
function ConvertTo-CidrToAzureStorageFirewallRule {
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('CIDR')]
[string[]] $Cidrs
)
begin {
function Expand-IPv4Cidr31 {
param([string]$ip)
$bytes = [System.Net.IPAddress]::Parse($ip).GetAddressBytes()
[Array]::Reverse($bytes)
$base = [BitConverter]::ToUInt32($bytes, 0)
$out = @()
foreach ($i in 0..1) {
$b = [BitConverter]::GetBytes($base + $i)
[Array]::Reverse($b)
$out += ([System.Net.IPAddress]::new($b)).ToString()
}
return $out
}
function Expand-IPv6Cidr127 {
param([string]$ip)
$addr = [System.Net.IPAddress]::Parse($ip)
$bytes = $addr.GetAddressBytes()
# BigInteger expects little-endian → reverse
[Array]::Reverse($bytes)
$base = [System.Numerics.BigInteger]::new($bytes)
$out = @()
foreach ($i in 0..1) {
$val = $base + $i
$b = $val.ToByteArray()
# Ensure 16 bytes
if ($b.Length -lt 16) {
$b = $b + (0 * (16 - $b.Length))
} elseif ($b.Length -gt 16) {
$b = $b[0..15]
}
[Array]::Reverse($b)
$out += ([System.Net.IPAddress]::new($b)).ToString()
}
return $out
}
}
process {
foreach ($cidr in $Cidrs) {
if ($cidr -notmatch '^(.+)/(\d{1,3})$') {
throw "Invalid CIDR format: $cidr"
}
$ip = $matches[1]
$prefix = [int]$matches[2]
$parsed = [System.Net.IPAddress]::Parse($ip)
if ($parsed.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork) {
# IPv4
if ($prefix -lt 0 -or $prefix -gt 32) {
throw "Invalid IPv4 prefix: $cidr"
}
if ($prefix -eq 31) {
foreach ($addr in Expand-IPv4Cidr31 $ip) {
[pscustomobject]@{
action = 'Allow'
value = $addr
}
}
}
else {
[pscustomobject]@{
action = 'Allow'
value = $cidr
}
}
}
else {
# IPv6
if ($prefix -lt 0 -or $prefix -gt 128) {
throw "Invalid IPv6 prefix: $cidr"
}
if ($prefix -eq 127) {
foreach ($addr in Expand-IPv6Cidr127 $ip) {
[pscustomobject]@{
action = 'Allow'
value = $addr
}
}
}
else {
[pscustomobject]@{
action = 'Allow'
value = $cidr
}
}
}
}
}
}
<#
.EXAMPLE
(Find-AzureServiceById -id PowerBI.WestEurope).addressPrefixes | Where-Object { -not $_.contains(":") } | Convert-CidrToAzureStorageFirewallRule | ConvertTo-BicepArray
#>
function ConvertTo-BicepArray {
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[psobject[]] $InputObject,
[string] $Indent = ' '
)
begin {
function Convert-Value {
param($Value, $Level)
$indentCurrent = $Indent * $Level
$indentNext = $Indent * ($Level + 1)
switch ($Value) {
{ $_ -is [string] } {
return "'$($_ -replace "'", "''")'"
}
{ $_ -is [bool] } {
return ($_.ToString().ToLower())
}
{ $_ -is [int] -or $_ -is [long] -or $_ -is [double] } {
return $_
}
{ $_ -is [hashtable] -or $_ -is [psobject] } {
$props = $_.PSObject.Properties | Where-Object { $_.MemberType -eq 'NoteProperty' }
$lines = @("{")
foreach ($p in $props) {
$val = Convert-Value -Value $p.Value -Level ($Level + 1)
$lines += "$indentNext$($p.Name): $val"
}
$lines += "$indentCurrent}"
return ($lines -join "`n")
}
{ $_ -is [System.Collections.IEnumerable] -and -not ($_ -is [string]) } {
$lines = @("[")
foreach ($item in $_) {
$val = Convert-Value -Value $item -Level ($Level + 1)
$lines += "$indentNext$val"
}
$lines += "$indentCurrent]"
return ($lines -join "`n")
}
default {
return "$Value"
}
}
}
$buffer = @()
}
process {
$buffer += $InputObject
}
end {
$lines = @("[")
foreach ($item in $buffer) {
$lines += "$Indent$(Convert-Value -Value $item -Level 1)"
}
$lines += "]"
$lines -join "`n"
}
}
# Example usage:
# Find-AzureServiceByIP -IpAddress "X.X.X.X" -jsonFilePath "C:\path\to\ServiceTags_Public_20240909.json"
BeforeAll {
. ./"ServiceTags-Helpers.ps1"
}
Describe 'Find-AzureServiceById' {
BeforeAll {
$script:testFile = Join-Path $TestDrive "data.json"
$sample = @{
values = @(
@{
Id = "ServiceA"
name = "Service A"
properties = @{
region = "global"
regionId = "1"
platform = "Azure"
systemService = "Test"
networkFeatures = "Basic"
addressPrefixes = @("10.0.0.0/24")
}
}
@{
Id = "ServiceB-Prod"
name = "Service B"
properties = @{
region = "eu"
regionId = "2"
platform = "Azure"
systemService = "Test"
networkFeatures = "Advanced"
addressPrefixes = @("20.0.0.0/24","20.0.1.0/24")
}
}
)
}
$sample | ConvertTo-Json -Depth 10 | Set-Content $script:testFile
}
It 'Should return exact match when regex is disabled' {
$result = Find-AzureServiceById -Id "ServiceA" -JsonFilePath $script:testFile
$result.Service | Should -Be "Service A"
}
It 'Should return match using regex' {
$result = Find-AzureServiceById -Id "^ServiceB" -JsonFilePath $script:testFile -UseRegex
$result.Service | Should -Be "Service B"
}
It 'Should return nothing when no regex match exists' {
$result = Find-AzureServiceById -Id "^DoesNotExist" -JsonFilePath $script:testFile -UseRegex
$result | Should -BeNullOrEmpty
}
It 'Should correctly count address prefixes' {
$result = Find-AzureServiceById -Id "ServiceB-Prod" -JsonFilePath $script:testFile
$result.addressPrefixesCount | Should -Be 2
}
It 'Should return multiple services' {
$result = Find-AzureServiceById -Id ".*Service.*" -JsonFilePath $script:testFile -UseRegex
$result.Count | Should -Be 2
}
}
BeforeAll {
. ./"ServiceTags-Helpers.ps1"
}
# Describe block is the Pester test group
Describe 'Test-IpInRange' {
# Test case: IP is within the range
It 'Should return $true when IP is within the CIDR range' {
$ip = "192.168.1.10"
$cidr = "192.168.1.0/24"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $true
$result | Should -Be $true
}
# Test case: IP is outside the range
It 'Should return $false when IP is outside the CIDR range' {
$ip = "192.168.2.10"
$cidr = "192.168.1.0/24"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: Exact match at the boundary of the CIDR range
It 'Should return $true for the lowest IP in the CIDR range' {
$ip = "192.168.1.0"
$cidr = "192.168.1.0/24"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $true
$result | Should -Be $true
}
# Test case: Highest IP in the range
It 'Should return $true for the highest IP in the CIDR range' {
$ip = "192.168.1.255"
$cidr = "192.168.1.0/24"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $true
$result | Should -Be $true
}
# Test case: Edge case with large CIDR range
It 'Should return $true when IP is within a large CIDR range' {
$ip = "10.0.0.5"
$cidr = "10.0.0.0/8"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $true
$result | Should -Be $true
}
# Test case: First IP outside the range (just below the range)
It 'Should return $false for the first IP outside the CIDR range (below)' {
$ip = "192.168.0.255"
$cidr = "192.168.1.0/24"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: Last IP outside the range (just above the range)
It 'Should return $false for the first IP outside the CIDR range (above)' {
$ip = "192.168.2.0"
$cidr = "192.168.1.0/24"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
}
Describe 'Test-IpInRange with non-byte-aligned CIDR masks' {
# Test case: IP is within the /19 CIDR range
It 'Should return $true when IP is within the /19 CIDR range' {
$ip = "192.168.32.15"
$cidr = "192.168.32.0/19"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $true
$result | Should -Be $true
}
# Test case: IP is outside the /19 CIDR range
It 'Should return $false when IP is outside the /19 CIDR range' {
$ip = "192.168.64.1"
$cidr = "192.168.32.0/19"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: IP is within the /23 CIDR range
It 'Should return $true when IP is within the /23 CIDR range' {
$ip = "10.0.1.5"
$cidr = "10.0.0.0/23"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $true
$result | Should -Be $true
}
# Test case: IP is outside the /23 CIDR range
It 'Should return $false when IP is outside the /23 CIDR range' {
$ip = "10.0.2.1"
$cidr = "10.0.0.0/23"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: IP is within the /21 CIDR range
It 'Should return $true when IP is within the /21 CIDR range' {
$ip = "172.16.4.1"
$cidr = "172.16.0.0/21"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $true
$result | Should -Be $true
}
# Test case: IP is outside the /21 CIDR range
It 'Should return $false when IP is outside the /21 CIDR range' {
$ip = "172.16.8.1"
$cidr = "172.16.0.0/21"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
}
Describe 'Test-IpInRange with first and last IP outside non-byte-aligned CIDR masks' {
# Test case: First IP outside the /19 CIDR range (just below)
It 'Should return $false for the first IP outside the /19 CIDR range (below)' {
$ip = "192.168.31.255"
$cidr = "192.168.32.0/19"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: Last IP outside the /19 CIDR range (just above)
It 'Should return $false for the last IP outside the /19 CIDR range (above)' {
$ip = "192.168.64.0"
$cidr = "192.168.32.0/19"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: First IP outside the /23 CIDR range (just below)
It 'Should return $false for the first IP outside the /23 CIDR range (below)' {
$ip = "10.0.2.1"
$cidr = "10.0.0.0/23"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: Last IP outside the /23 CIDR range (just above)
It 'Should return $false for the last IP outside the /23 CIDR range (above)' {
$ip = "10.0.2.0"
$cidr = "10.0.0.0/23"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: First IP outside the /21 CIDR range (just below)
It 'Should return $false for the first IP outside the /21 CIDR range (below)' {
$ip = "172.15.255.255"
$cidr = "172.16.0.0/21"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
# Test case: Last IP outside the /21 CIDR range (just above)
It 'Should return $false for the last IP outside the /21 CIDR range (above)' {
$ip = "172.16.8.0"
$cidr = "172.16.0.0/21"
# Call the function and check the result
$result = Test-IpInRange -IpAddress $ip -CIDR $cidr
# The result should be $false
$result | Should -Be $false
}
}
Describe 'Test-IpInRange - IPv6' {
Context 'Aligned CIDR (/64)' {
It 'Should return $true for the first IP in the CIDR' {
$ip = "2001:db8::"
$cidr = "2001:db8::/64"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $true
}
It 'Should return $true for the last IP in the CIDR' {
$ip = "2001:db8::ffff:ffff:ffff:ffff"
$cidr = "2001:db8::/64"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $true
}
It 'Should return $false for the first IP after the CIDR' {
$ip = "2001:db8:0:1::"
$cidr = "2001:db8::/64"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $false
}
It 'Should return $false for the last IP before the CIDR' {
$ip = "2001:db7:ffff:ffff:ffff:ffff:ffff:ffff"
$cidr = "2001:db8::/64"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $false
}
}
Context 'Non-byte aligned CIDR (/65)' {
It 'Should return $true for the first IP in the CIDR' {
$ip = "2001:db8::"
$cidr = "2001:db8::/65"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $true
}
It 'Should return $true for the last IP in the CIDR' {
$ip = "2001:db8::7fff:ffff:ffff:ffff"
$cidr = "2001:db8::/65"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $true
}
It 'Should return $false for the first IP after the CIDR' {
$ip = "2001:db8::8000:0:0:0"
$cidr = "2001:db8::/65"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $false
}
It 'Should return $false for the last IP before the CIDR' {
$ip = "2001:db7:ffff:ffff:ffff:ffff:ffff:ffff"
$cidr = "2001:db8::/65"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $false
}
}
Context 'Highly non-aligned CIDR (/73)' {
It 'Should return $true for the first IP in the CIDR' {
$ip = "2001:db8::"
$cidr = "2001:db8::/73"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $true
}
It 'Should return $true for the last IP in the CIDR' {
$ip = "2001:0db8:0000:0000:007f:ffff:ffff:ffff"
$cidr = "2001:db8::/73"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $true
}
It 'Should return $false for the first IP after the CIDR' {
$ip = "2001:db8::80:0:0:0"
$cidr = "2001:db8::/73"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $false
}
It 'Should return $false for the last IP before the CIDR' {
$ip = "2001:db7:ffff:ffff:ffff:ffff:ffff:ffff"
$cidr = "2001:db8::/73"
(Test-IpInRange -IpAddress $ip -CIDR $cidr) | Should -Be $false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment