Skip to content

Instantly share code, notes, and snippets.

@jpbruckler
Forked from mlhDevelopment/cidrToIpRange.ps1
Last active September 8, 2025 20:32
Show Gist options
  • Save jpbruckler/8ff08a717a4bf6f1f952a5b45489b201 to your computer and use it in GitHub Desktop.
Save jpbruckler/8ff08a717a4bf6f1f952a5b45489b201 to your computer and use it in GitHub Desktop.
CIDR to IP range conversion using PowerShell
# calculate IP address range from CIDR notation and optionally
# provide the full range to iterate over
# https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
function cidrToIpRange {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$cidrNotation,
[switch]$IncludeRange, # add the Range property
[switch]$ExcludeNetworkBroadcast # skip net & broadcast addresses
)
# -- helpers: IPv4 <-> uint32 (host order) --
function ipToUint32([Net.IPAddress]$ip) {
$bytes = $ip.GetAddressBytes()
[Array]::Reverse($bytes) # network -> host order
[BitConverter]::ToUInt32($bytes, 0)
}
function uint32ToIp([uint32]$n) {
$bytes = [BitConverter]::GetBytes($n)
[Array]::Reverse($bytes) # host -> network order
[Net.IPAddress]::new($bytes)
}
$addr, $maskLength = $cidrNotation -split '/', 2
[int]$maskLen = 0
if (-not [int32]::TryParse($maskLength, [ref]$maskLen)) {
Write-Warning 'No mask, setting to /32'
$maskLen = 32
}
if ($maskLen -lt 0 -or $maskLen -gt 32) {
throw 'CIDR mask length must be between 0 and 32'
}
$ipAddr = [Net.IPAddress]::Parse($addr)
if ($ipAddr.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork) {
throw 'Can only process CIDR for IPv4'
}
# compute network & broadcast
$shift = 32 - $maskLen
if ($maskLen -eq 0) { $mask = [uint32]0 }
else {
$lowMask = ([uint64]1 -shl $shift) - 1 # e.g., shift=8 -> 0xFF
$mask = [uint32]::MaxValue -bxor [uint32]$lowMask
}
$ipNum = ipToUint32 $ipAddr
$net = $ipNum -band $mask
$bcast = $net -bor ([uint32]::MaxValue -bxor $mask)
$start = uint32ToIp $net
$end = uint32ToIp $bcast
$result = [pscustomobject]@{
Start = $start.ToString()
End = $end.ToString()
}
if ($IncludeRange) {
# determine first & last based on whether we exclude network/broadcast
$first = if ($ExcludeNetworkBroadcast -and $maskLen -lt 31) { $net + 1 } else { $net }
$last = if ($ExcludeNetworkBroadcast -and $maskLen -lt 31) { $bcast - 1 } else { $bcast }
if ($last -lt $first) {
# happens for /31 with exclude, or /32 generally
$result | Add-Member -NotePropertyName Range -NotePropertyValue @()
return $result
}
# size & guardrails
$count = [int64]($last - $first + 1)
if ($count -gt 2000000) {
throw "Refusing to allocate a Range with $count addresses. Stream results instead (see note below)."
}
# fill pre-sized array (much faster than pipeline)
$arr = [Net.IPAddress[]]::new($count)
$n = $first
for ($i = 0; $i -lt $count; $i++) {
$arr[$i] = uint32ToIp([uint32]$n)
$n++
}
$result | Add-Member -NotePropertyName Range -NotePropertyValue $arr
}
return $result
}
# Test
$ips = @('1.2.3.4/24', '9.8.7.6')
$ips | % { cidrToIpRange $_ -IncludeRange } | sort-object start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment