functions/Convert-IpListToRanges.ps1

<#
.SYNOPSIS
    Converts a list of IP addresses into contiguous IP ranges.

.DESCRIPTION
    The Convert-IpListToRanges function takes a list of IP addresses, deduplicates and sorts them, then
    groups consecutive addresses into compact range notation (startIP-endIP). Isolated addresses are
    returned as plain IP strings. The result is suitable for firewall rules, ACLs, or any system that
    accepts range-style input.

.PARAMETER IPAddressList
    An array of IPv4 addresses to be collapsed into ranges.

.EXAMPLE
    Convert-IpListToRanges -IPAddressList @("192.168.1.1","192.168.1.2","192.168.1.3","192.168.1.5")
    Output:
        192.168.1.1-192.168.1.3
        192.168.1.5

.EXAMPLE
    Convert-IpListToRanges -IPAddressList @("10.0.0.1","10.0.0.2","10.0.0.3","10.0.0.4")
    Output:
        10.0.0.1-10.0.0.4

.EXAMPLE
    $ips = Get-Content "ip_addresses.txt"
    Convert-IpListToRanges -IPAddressList $ips

.NOTES
    Author: Iman Edrisian
    Version: 1.0.0
    Requires: PowerShell 5.1 or higher

.LINK
    https://github.com/imanedr/psnetworking

.OUTPUTS
    System.String[]
    Returns an array of strings. Each element is either a single IP address or a range in
    "firstIP-lastIP" format.
#>

function Convert-IpListToRanges
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string[]]$IPAddressList
    )

    function ConvertTo-IPInt ([string]$IP) {
        $o = $IP.Split('.')
        return ([uint32]$o[0] -shl 24) -bor ([uint32]$o[1] -shl 16) -bor ([uint32]$o[2] -shl 8) -bor [uint32]$o[3]
    }

    function ConvertFrom-IPInt ([uint32]$Value) {
        return '{0}.{1}.{2}.{3}' -f (($Value -shr 24) -band 255),
                                     (($Value -shr 16) -band 255),
                                     (($Value -shr 8)  -band 255),
                                     ($Value            -band 255)
    }

    $hashSet = New-Object System.Collections.Generic.HashSet[string]
    foreach ($item in $IPAddressList) {
        [void]$hashSet.Add($item.Trim())
    }

    $sortedIPs = Sort-IpAddress -IpAddressList $hashSet

    if (-not $sortedIPs -or $sortedIPs.Count -eq 0) { return @() }

    $ipInts = @($sortedIPs | ForEach-Object { ConvertTo-IPInt $_ })

    $ranges   = @()
    $startInt = $ipInts[0]
    $endInt   = $ipInts[0]

    for ($i = 1; $i -lt $ipInts.Count; $i++) {
        if ($ipInts[$i] -eq $endInt + 1) {
            $endInt = $ipInts[$i]
        } else {
            $ranges += if ($startInt -eq $endInt) {
                ConvertFrom-IPInt $startInt
            } else {
                "$(ConvertFrom-IPInt $startInt)-$(ConvertFrom-IPInt $endInt)"
            }
            $startInt = $ipInts[$i]
            $endInt   = $ipInts[$i]
        }
    }

    $ranges += if ($startInt -eq $endInt) {
        ConvertFrom-IPInt $startInt
    } else {
        "$(ConvertFrom-IPInt $startInt)-$(ConvertFrom-IPInt $endInt)"
    }

    return $ranges
}