I’ve been extended and upgrading my library of network functions for some time now, I thought I’d post the functions that work directly with my original collection.

The functions described here have been updated since posting this article. The updated functions are available as part of the script Indented.Net.IP. The module may be installed from the PS gallery:

Install-Module Indented.Net.IP

ConvertTo-HexIP

Sometimes being able to convert to a hexadecimal format is useful, this little function does exactly that. The body is very simple; it uses the built in formatters.

filter ConvertTo-HexIP {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [IPAddress]$IPAddress
    )
   
    ($IPAddress.GetAddressBytes() | ForEach-Object {
        '{0:X2}' -f $_
    }) -join ''
}

ConvertFrom-HexIP

And back again, using System.Convert.ToUInt32 with a base-16 value to get to UInt32, then a call to ConvertTo-DottedDecimalIP (from the original set).

filter ConvertFrom-HexIP {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [ValidatePattern('^[0-9a-f]{8}$')]
        [String]$IPAddress
    )
    
    ConvertTo-DottedDecimalIP ([Convert]::ToUInt32($IPAddress, 16))
}

Get-NetworkSummary

Get-NetworkSummary gets a bit of an improvement.

filter Get-NetworkSummary {
    [CmdletBinding(DefaultParameterSetName = "IPAndMask")]
    param (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "IPAndMask", ValueFromPipeline = $true)]
        [Net.IPAddress]$IPAddress,
        
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "IPAndMask")]
        [Alias("Mask")]
        [IPAddress]$SubnetMask,
        
        [Parameter(Mandatory = $true, ParameterSetName = "CIDRNotation", ValueFromPipeline = $true)]
        [ValidatePattern('^(\d{1,3}\.){3}\d{1,3}[\\/]\d{1,2}$')]
        [String]$Network
    )
    
    if ($pscmdlet.ParameterSetName -eq 'CIDRNotation') {
        $Temp = $Network.Split("/")
        $IPAddress = $Temp[0]
        $SubnetMask = ConvertTo-Mask $Temp[1]
    }
    
    $DecimalIP = ConvertTo-DecimalIP $IPAddress
    $DecimalMask = ConvertTo-DecimalIP $SubnetMask
    $DecimalNetwork = $DecimalIP -band $DecimalMask
    $DecimalBroadcast = $DecimalIP -bor ((-bnot $DecimalMask) -band [UInt32]::MaxValue)

    $NetworkSummary = [PSCustomObject]@{
        NetworkAddress   = (ConvertTo-DottedDecimalIP $DecimalNetwork)
        NetworkDecimal   = $DecimalNetwork
        BroadcastAddress = (ConvertTo-DottedDecimalIP $DecimalBroadcast)
        BroadcastDecimal = $DecimalBroadcast
        Mask             = $SubnetMask
        MaskLength       = (ConvertTo-MaskLength $SubnetMask)
        MaskHexadecimal  = (ConvertTo-HexIP $SubnetMask)
        HostRange        = ''
        NumberOfHosts    = ($DecimalBroadcast - $DecimalNetwork - 1)
        Class            = ''
        IsPrivate        = $false
    }
    
    if ($NetworkSummary.MaskLength -lt 31) {
        $NetworkSummary.HostRange = "{0} - {1}" -f
            (ConvertTo-DottedDecimalIP ($DecimalNetwork + 1)),
            (ConvertTo-DottedDecimalIP ($DecimalBroadcast - 1))
    }
    
    $NetworkSummary.Class = switch -regex ($(ConvertTo-BinaryIP $IPAddress)) {
        "^1111"              { "E"; break }
        "^1110"              { "D"; break }
        "^11000000.10101000" { $NetworkSummary.IsPrivate = $true }
        "^110"               { "C"; break }
        "^10101100.0001"     { $NetworkSummary.IsPrivate = $true }
        "^10"                { "B"; break }
        "^00001010"          { $NetworkSummary.IsPrivate = $true }
        "^0"                 { "A"; break }
    }
    $NetworkSummary
}

Get-NetworkRange

And so does Get-NetworkRange.

filter Get-NetworkRange {
    [CmdletBinding(DefaultParameterSetName = "IPAndMask")]
    param (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "IPAndMask", ValueFromPipeline = $true)]
        [IPAddress]$IPAddress,
        
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "IPAndMask")]
        [Alias("Mask")]
        [IPAddress]$SubnetMask,
        
        [Parameter(Mandatory = $true, ParameterSetName = "CIDRNotation", ValueFromPipeline = $true)]
        # [ValidatePattern('^(\d{1,3}\.){3}\d{1,3}[\\/]\d{1,2}$')]
        [String]$Network
    )
    
    if ($pscmdlet.ParameterSetName -eq 'CIDRNotation') {
        $Temp = $Network.Split("/")
        $IPAddress = $Temp[0]
        $SubnetMask = ConvertTo-Mask $Temp[1]
    }
    $DecimalIP = ConvertTo-DecimalIP $IPAddress
    $DecimalMask = ConvertTo-DecimalIP $SubnetMask
    $DecimalNetwork = $DecimalIP -band $DecimalMask
    $DecimalBroadcast = $DecimalIP -bor ((-bnot $DecimalMask) -band [UInt32]::MaxValue)
    
    for ($i = $DecimalNetwork + 1; $i -lt $DecimalBroadcast; $i++) {
        ConvertTo-DottedDecimalIP $i
    }
}

Get-Subnets

I ran into a need to calculate a list of subnets of a specific length within a super-net, this function does just that.

filter Get-Subnets {
    [CmdletBinding(DefaultParameterSetName = "IPAndMask")]
    param (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "IPAndMask", ValueFromPipeline = $true)]
        [IPAddress]$IPAddress, 

        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "IPAndMask")]
        [Alias("Mask")]
        [IPAddress]$SubnetMask,

        [Parameter(Mandatory = $true, ParameterSetName = "CIDRNotation", ValueFromPipeline = $true)]
        [ValidatePattern('^(\d{1,3}\.){3}\d{1,3}[\\/]\d{1,2}$')]
        [String]$Network,

        [ValidateRange(1, 32)]
        [UInt32]$SupernetLength
    )

    if ($pscmdlet.ParameterSetName -eq 'CIDRNotation') {
        $Temp = $Network.Split("/")
        $IPAddress = $Temp[0]
        $SubnetMask = ConvertTo-Mask $Temp[1]
    } else {
        $SubnetLength = ConvertTo-MaskLength $SubnetMask
    }

    if (!$SupernetLength) {
        $SupernetLength = switch -regex ($(ConvertTo-BinaryIP $IPAddress)) {
            "^110"  { 24 }
            "^10"   { 16 }
            "^0"    { 8 }
            default { 24 }
        }
    }
    if ($SupernetLength -gt $SubnetLength) {
        throw "Subnet is larger than supernet."
    }
    $SupernetMask = ConvertTo-Mask $SupernetLength

    $NumberOfNets = [Math]::Pow(2, ($SubnetLength - $SupernetLength))
    $NumberOfAddresses = [Math]::Pow(2, (32 - $SubnetLength))

    $SupernetAddress = Get-NetworkAddress $IPAddress $SupernetMask
    $DecimalAddress = ConvertTo-DecimalIP $SupernetAddress

    for ($i = 0; $i -lt $NumberOfNets; $i++) {
        $NetworkAddress = ConvertTo-DottedDecimalIP $DecimalAddress 

        [PSCustomObject]@{
            NetworkAddress = $NetworkAddress
            BroadcastAddress = Get-BroadcastAddress $NetworkAddress $SubnetMask
            SubnetMask       = $SubnetMask
            HostAddresses    = $(
                $NumberOfHosts = $NumberOfAddresses - 2
                if ($NumberOfHosts -lt 0) { 0 } else { $NumberOfHosts }
            )
        }
        $DecimalAddress += $NumberOfAddresses
    }
}