Skip to content

Instantly share code, notes, and snippets.

@jikuja
Last active December 17, 2024 15:27
Show Gist options
  • Save jikuja/a8dc455f7a3e1158fc1bb1ccb63ef6f1 to your computer and use it in GitHub Desktop.
Save jikuja/a8dc455f7a3e1158fc1bb1ccb63ef6f1 to your computer and use it in GitHub Desktop.

Revisions

  1. Janne Kujanpää revised this gist Dec 17, 2024. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions ServiceTags-Helpers.ps1
    Original file line number Diff line number Diff line change
    @@ -66,13 +66,13 @@ function Test-IpInRange {
    return $true
    }

    function Download-ServiTagsData {
    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 'data-bi-id="downloadretry" href="(?<url>.*\.json?)"') {
    if ($pageContent.Content -match 'href="(?<url>[a-zA-Z0-9/:\.\-_]*\.json?)"') {
    $jsonUrl = $matches['url']
    }
    Write-Host "Downloading file from $jsonUrl"
  2. Janne Kujanpää revised this gist Oct 12, 2024. 3 changed files with 160 additions and 0 deletions.
    9 changes: 9 additions & 0 deletions Invoke-Tests.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    $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
    1 change: 1 addition & 0 deletions ServiceTags-Helpers.ps1
    Original file line number Diff line number Diff line change
    @@ -11,6 +11,7 @@ function Find-AzureServiceByIP {
    # 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
    150 changes: 150 additions & 0 deletions Test-IpInRange.Tests.ps1
    Original file line number Diff line number Diff line change
    @@ -89,3 +89,153 @@ Describe 'Test-IpInRange' {
    $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
    }
    }
  3. Janne Kujanpää revised this gist Oct 10, 2024. 2 changed files with 37 additions and 4 deletions.
    26 changes: 23 additions & 3 deletions 00-README.md
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,34 @@
    # TBD
    # Virtual network service tags

    Helpers for Microsoft provided ServiceTags IP lists.
    Helpers for Microsoft provided Virtual network service tags.

    Revision 2 functions and tests are from ChatGPT.

    ## TODO

    * Add function to download file
    * 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

    ```powershell
    . ./ServiceTags-Helpers.ps1
    ```

    Download data

    ```powershell
    Download-ServiTagsData
    ```

    Run the tool

    ```powershell
    Find-AzureServiceByIP -ipAddress "4.149.115.15"
    ```

    ## How to run tests

    15 changes: 14 additions & 1 deletion ServiceTags-Helpers.ps1
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@
    function Find-AzureServiceByIP {
    param (
    [string]$ipAddress,
    [string]$jsonFilePath
    [string]$jsonFilePath = ".data.json"
    )

    # Load the JSON file into a PowerShell object
    @@ -65,5 +65,18 @@ function Test-IpInRange {
    return $true
    }

    function Download-ServiTagsData {
    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 'data-bi-id="downloadretry" href="(?<url>.*\.json?)"') {
    $jsonUrl = $matches['url']
    }
    Write-Host "Downloading file from $jsonUrl"
    Invoke-RestMethod -Uri $jsonUrl -OutFile $jsonFilePath
    }

    # Example usage:
    # Find-AzureServiceByIP -IpAddress "X.X.X.X" -jsonFilePath "C:\path\to\ServiceTags_Public_20240909.json"
  4. Janne Kujanpää revised this gist Oct 10, 2024. 3 changed files with 188 additions and 1 deletion.
    29 changes: 28 additions & 1 deletion 00-README.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,28 @@
    # TBD
    # TBD

    Helpers for Microsoft provided ServiceTags IP lists.

    Revision 2 functions and tests are from ChatGPT.

    ## TODO

    * Add function to download file
    * Add function to download file from REST API
    * Add transparent caching to persist and re-use downloaded data

    ## How to run tests

    Install Pester

    ```powershell
    Install-Module -Name Pester -Force -SkipPublisherCheck
    ```

    Run tests:

    ```powershell
    Invoke-Pester -Path "Test-IpInRange.Tests.ps1"
    ```

    ```powershell
    ```
    69 changes: 69 additions & 0 deletions ServiceTags-Helpers.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,69 @@
    # Function to find Azure services by IP address
    function Find-AzureServiceByIP {
    param (
    [string]$ipAddress,
    [string]$jsonFilePath
    )

    # 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) {
    # 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
    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
    })
    }
    }
    }
    }
    }

    # Helper function to check if an IP address is within a CIDR block
    function Test-IpInRange {
    param (
    [string]$IpAddress,
    [string]$CIDR
    )

    # Split CIDR into base IP and subnet mask bits
    $cidrParts = $CIDR -split "/"
    $baseIP = $cidrParts[0]
    $subnetBits = [int]$cidrParts[1]

    # Convert IP addresses to byte arrays
    $ipBytes = [System.Net.IPAddress]::Parse($IpAddress).GetAddressBytes()
    $baseIpBytes = [System.Net.IPAddress]::Parse($baseIP).GetAddressBytes()

    # Calculate the number of full bytes and partial bits in the subnet mask
    $fullBytes = [math]::Floor($subnetBits / 8)
    $remainingBits = $subnetBits % 8

    # Compare full bytes of the IP and base IP
    for ($i = 0; $i -lt $fullBytes; $i++) {
    if ($ipBytes[$i] -ne $baseIpBytes[$i]) {
    return $false
    }
    }

    # Compare the remaining bits in the last byte, if any
    if ($remainingBits -gt 0) {
    $mask = 0xFF -bxor (0xFF -shr $remainingBits)
    if (($ipBytes[$fullBytes] -band $mask) -ne ($baseIpBytes[$fullBytes] -band $mask)) {
    return $false
    }
    }

    return $true
    }

    # Example usage:
    # Find-AzureServiceByIP -IpAddress "X.X.X.X" -jsonFilePath "C:\path\to\ServiceTags_Public_20240909.json"
    91 changes: 91 additions & 0 deletions Test-IpInRange.Tests.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,91 @@
    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
    }
    }
  5. jikuja created this gist Oct 10, 2024.
    1 change: 1 addition & 0 deletions 00-README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # TBD