Public/Resolve-IPv4Address.ps1

$dotnetclass = @"
public class SubnetInformation
{
    public SubnetInformation()
    {
         
    }
 
    public string AddressValueSupplied { get; set; }
    public string AddressFormatSupplied { get; set; }
    public string IPAddress { get; set; }
    public string Subnet { get; set; }
    public string SubnetMask { get; set; }
    public int SubnetMaskLength { get; set; }
    public string BroadcastAddress { get; set; }
    public int NumberOfHosts { get; set; }
    public string FirstHostAddress { get; set; }
    public string LastHostAddress { get; set; }
    public string AddressClassification { get; set; }
    public string AddressSpaceClassification { get; set; }
    public object GeoInformation { get; set; }
 
    public override string ToString()
    {
        return Subnet + "/" + SubnetMaskLength.ToString();
    }
}
"@

Add-Type -TypeDefinition $dotnetclass

<#
    .SYNOPSIS
    Resolves the subnet definition of a given IPv4 address and subnet mask. An optional geoinformation can be obtained (requires Internet access).
    .DESCRIPTION
    Resolves the subnet definition of a given IPv4 address and, optionally, subnet mask. An optional geoinformation can be obtained (requires Internet access).
 
    The subnet definition includes:
    - Subnet address
    - mask and mask length
    - broadcast address
    - number of usable host addresses (none for /31 mask)
    - the first and last available host address
    - classification of the supplied address within the subnet (host/subnet/braodcast)
    - classification of the supplied address within the IPv4 address space (public/private/reserved/other...)
    - geoinformation about the specified address, if it was classified as 'Public' and the switch -IncludeGeoInformation was given. The geoinformation is obtained from http://ip-api.com.
 
    If only an IP address, without a mask, was given, the subnet definition will return:
    - a Class A/B/C subnet for private IP addresses
    - a /32 subnet for any other address classes
 
    .PARAMETER IPAddress
    One or more IPv4 address definitions in form IP.IP.IP.IP, IP.IP.IP.IP/MASK or IP.IP.IP.IP/SM.SM.SM.SM
    .PARAMETER IncludeGeoInformation
    If specified, an attempt will be made to connect to ip-api.com and retrieve the geo information about the address.
    .EXAMPLE
    '8.8.8.8','141.20.1.3' | Resolve-IPv4Address -IncludeGeoInformation
    Returns a /32 subnet and geoinformation for both specified public addresses.
    .EXAMPLE
    Resolve-IPv4Address -IPAddress '10.0.101.22/22
    Returns a complete subnet definition, without geoinformation, for the specified address.
    .EXAMPLE
    Resolve-IPv4Address -IPAddress '192.168.178.223/255.255.255.128' -IncludeGeoInformation
    Returns a complete subnet definition for the specified address. No geoinformation will be returned, despite the switch, because the address is clissifed as 'Private' in the IPv4 address space.
    .INPUTS
    String[]
    .OUTPUTS
    SubnetInformation
 
#>

function Resolve-IPv4Address {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true, HelpMessage="Enter one or more IPv4 address definitions, separated by commas.")]
        [string[]]$IPAddress,
        
        [Parameter(Mandatory=$false)]
        [switch]$IncludeGeoInformation
    )
    Begin {}
    Process {
        foreach ($currentIPAddress in $IPAddress) {
            $addressFormat = 'UNKNOWN'
            $Address = $null
            $SubnetMask = $null
            $MaskLength = -1
            switch -Regex ($currentIPAddress) {
                '^(?<ip>(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\/(?<mask>(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$' {
                    $Address = $Matches['ip']
                    $SubnetMask = $Matches['mask']
                    if (Test-IPAX4SubnetMask -SubnetMask $SubnetMask) {
                        $MaskLength = Get-IPAX4SubnetMaskLength -SubnetMask $SubnetMask
                        $addressFormat = 'ADDRANDMASK'
                    } else {
                        Write-Warning ('Subnet mask {0} not valid!' -f $SubnetMask)
                        $Address = $null
                        $SubnetMask = $null
                        $addressFormat = 'INVALID'
                    }
                }
                '^(?<ip>(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\/(?<masklen>(?:3[0-2]|2[0-9]|1[0-9]|[0-9]))$' {
                    $Address = $Matches['ip']
                    $MaskLength = $Matches['masklen']
                    $SubnetMask = Get-IPAX4SubnetMask -SubnetMaskLength $MaskLength
                    $addressFormat = 'ADDRANDLEN'
                }
                '^(?<ip>(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$' {
                    $Address = $Matches['ip']
                    $decimalAddress = Convert-V4DottedToInt -InputString $Address
                    if (($decimalAddress -le 184549375) -and ($decimalAddress -ge 167772160)) {
                        $MaskLength = 8
                    } elseif (($decimalAddress -le 2887778303) -and ($decimalAddress -ge 2886729728)) {
                        $MaskLength = 16
                    } elseif (($decimalAddress -le 3232301055) -and ($decimalAddress -ge 3232235520)) {
                        $MaskLength = 24
                    } else {
                        $MaskLength = 32
                    }
                    $SubnetMask = Get-IPAX4SubnetMask -SubnetMaskLength $MaskLength
                    $addressFormat = 'ADDRONLY'
                }
            }
            if ($null -ne $Address) {
                $ipval = Convert-V4DottedToInt -InputString $Address
                $snval = Convert-V4DottedToInt -InputString $SubnetMask
                $snaddr = $ipval -band $snval
                if ($MaskLength -eq 32) {
                    $firstip = Convert-IntToV4Dotted $snaddr
                    $lastip = Convert-IntToV4Dotted $snaddr
                    $numaddr = 1
                    $bcaddr = $snaddr
                    $acl = 'Host'
                } elseif ($MaskLength -eq 31) {
                    $firstip = $null
                    $lastip = $null
                    $numaddr = 0
                    $bcaddr = $snaddr + 1
                    if ($ipval -eq $snaddr) {
                        $acl = 'Subnet'
                    } else {
                        $acl = 'Broadcast'
                    }
                } else {
                    $firstip = Convert-IntToV4Dotted ($snaddr + 1)
                    $numaddr = [math]::Pow(2,(32 - $MaskLength)) - 2
                    $lastip = Convert-IntToV4Dotted ($snaddr + $numaddr)
                    $bcaddr = $snaddr + $numaddr + 1
                    if ($ipval -eq $snaddr) {
                        $acl = 'Subnet'
                    } elseif ($ipval -eq $bcaddr)  {
                        $acl = 'Broadcast'
                    } else {
                        $acl = 'Host'
                    }
                }
                $classification = Get-IPAX4Classification -IPAddress $Address
                $result = [SubnetInformation]::new()
                $result.AddressValueSupplied = $currentIPAddress
                $result.AddressFormatSupplied = $addressFormat
                $result.IPAddress = $Address
                $result.Subnet = Convert-IntToV4Dotted $snaddr
                $result.SubnetMask = $SubnetMask
                $result.SubnetMaskLength = $MaskLength
                $result.BroadcastAddress = Convert-IntToV4Dotted $bcaddr
                $result.NumberOfHosts = $numaddr
                $result.FirstHostAddress = $firstip
                $result.LastHostAddress = $lastip
                $result.AddressClassification = $acl
                $result.AddressSpaceClassification = $classification
                if ($IncludeGeoInformation -and ($classification -eq 'Public')) {
                    $result.GeoInformation = Get-IPAXGeoInformation  -IPAddress $Address
                }
            } else {
                if ($addressFormat -eq 'UNKNOWN') {
                    Write-Warning "Unknown address format: '$currentIPAddress'"
                }
                $result = $null
            }
            $result
        }
    }
    End {}
}